diff --git a/.config/suppress.json b/.config/suppress.json new file mode 100644 index 00000000000..9be220b291e --- /dev/null +++ b/.config/suppress.json @@ -0,0 +1,17 @@ +{ + "tool": "Credential Scanner", + "suppressions": [ + { + "file": "\\test\\tools\\Modules\\WebListener\\ClientCert.pfx", + "_justification": "Test certificate with private key" + }, + { + "file": "\\test\\tools\\Modules\\WebListener\\ServerCert.pfx", + "_justification": "Test certificate with private key" + }, + { + "file": "\\test\\powershell\\Modules\\Microsoft.PowerShell.Security\\certificateCommon.psm1", + "_justification": "Test certificate with private key and inline suppression isn't working" + } + ] +} diff --git a/.config/tsaoptions.json b/.config/tsaoptions.json new file mode 100644 index 00000000000..786ef4331a2 --- /dev/null +++ b/.config/tsaoptions.json @@ -0,0 +1,12 @@ +{ + "codebaseName": "TFSMSAzure_PowerShell", + "instanceUrl": "https://msazure.visualstudio.com", + "projectName": "One", + "areaPath": "One\\MGMT\\Compute\\Powershell\\Powershell\\PowerShell Core\\pwsh", + "notificationAliases": [ + "adityap@microsoft.com", + "dongbow@microsoft.com", + "pmeinecke@microsoft.com", + "tplunk@microsoft.com" + ] +} diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 00000000000..c849a9f78e5 --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,25 @@ +#------------------------------------------------------------------------------------------------------------- +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. See https://go.microsoft.com/fwlink/?linkid=2090316 for license information. +#------------------------------------------------------------------------------------------------------------- + +FROM mcr.microsoft.com/powershell/test-deps:ubuntu-20.04@sha256:d1609c57d2426b9cfffa3a3ab7bda5ebc4448700f8ba8ef377692c4a70e64b8c + +# Avoid warnings by switching to noninteractive +ENV DEBIAN_FRONTEND=noninteractive + +# Configure apt and install packages +RUN apt-get update \ + && apt-get -y upgrade \ + && apt-get -y install --no-install-recommends apt-utils 2>&1 \ + # + # Verify git, process tools, lsb-release (common in install instructions for CLIs) installed + && apt-get -y install --no-install-recommends git procps lsb-release \ + # + # Clean up + && apt-get autoremove -y \ + && apt-get clean -y \ + && rm -rf /var/lib/apt/lists/* + +# Switch back to dialog for any ad-hoc use of apt-get +ENV DEBIAN_FRONTEND=dialog diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 00000000000..eded2d1bdec --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,23 @@ +// See https://aka.ms/vscode-remote/devcontainer.json for format details. +{ + "name": ".NET Core 6.0, including pwsh (Ubuntu 18.04)", + "dockerFile": "Dockerfile", + + "workspaceMount": "source=${localWorkspaceFolder},target=/PowerShell,type=bind", + "workspaceFolder": "/PowerShell", + + // Uncomment the next line to run commands after the container is created. + "postCreateCommand": "cd src/powershell-unix && dotnet restore", + + "customizations": { + "vscode": { + "extensions": [ + "ms-azure-devops.azure-pipelines", + "ms-dotnettools.csharp", + "ms-vscode.powershell", + "DavidAnson.vscode-markdownlint", + "vitaliymaz.vscode-svg-previewer" + ] + } + } +} diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 00000000000..57d2f6c6c3e --- /dev/null +++ b/.editorconfig @@ -0,0 +1,213 @@ +# EditorConfig is awesome: https://EditorConfig.org +# .NET coding convention settings for EditorConfig +# https://learn.microsoft.com/visualstudio/ide/editorconfig-code-style-settings-reference +# +# This file comes from dotnet repositories: +# https://github.com/dotnet/runtime/blob/master/.editorconfig +# https://github.com/dotnet/roslyn/blob/master/.editorconfig + +# Top-most EditorConfig file +root = true + +[*] +charset = utf-8 +# indent_size intentionally not specified in this section +indent_style = space +insert_final_newline = true + +# Source code +[*.{cs,ps1,psd1,psm1}] +indent_size = 4 + +# Shell scripts +[*.sh] +end_of_line = lf +indent_size = 4 + +# Xml project files +[*.{csproj,resx,ps1xml}] +indent_size = 2 + +# Data serialization +[*.{json,yaml,yml}] +indent_size = 2 + +# Markdown +[*.md] +indent_size = 2 + +# Xml files +[*.{resx,ruleset,stylecop,xml,xsd,xsl}] +indent_size = 2 + +# Xml config files +[*.{props,targets,config,nuspec}] +indent_size = 2 + +[*.tsv] +indent_style = tab + +# Dotnet code style settings: +[*.cs] +# Sort using and Import directives with System.* appearing first +dotnet_sort_system_directives_first = true + +file_header_template = Copyright (c) Microsoft Corporation.\nLicensed under the MIT License. + +# Modifier preferences +csharp_preferred_modifier_order = public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async:suggestion + +# Avoid "this." and "Me." if not necessary +dotnet_style_qualification_for_field = false:suggestion +dotnet_style_qualification_for_property = false:suggestion +dotnet_style_qualification_for_method = false:suggestion +dotnet_style_qualification_for_event = false:suggestion + +# Use language keywords instead of framework type names for type references +dotnet_style_predefined_type_for_locals_parameters_members = true:suggestion +dotnet_style_predefined_type_for_member_access = true:suggestion + +# Name all constant fields using PascalCase +dotnet_naming_rule.constant_fields_should_be_pascal_case.severity = suggestion +dotnet_naming_rule.constant_fields_should_be_pascal_case.symbols = constant_fields +dotnet_naming_rule.constant_fields_should_be_pascal_case.style = pascal_case_style + +dotnet_naming_symbols.constant_fields.applicable_kinds = field +dotnet_naming_symbols.constant_fields.required_modifiers = const + +dotnet_naming_style.pascal_case_style.capitalization = pascal_case + +# Static fields should have s_ prefix +dotnet_naming_rule.static_fields_should_have_prefix.severity = suggestion +dotnet_naming_rule.static_fields_should_have_prefix.symbols = static_fields +dotnet_naming_rule.static_fields_should_have_prefix.style = static_prefix_style + +dotnet_naming_symbols.static_fields.applicable_kinds = field +dotnet_naming_symbols.static_fields.required_modifiers = static +dotnet_naming_symbols.static_fields.applicable_accessibilities = private, internal, private_protected + +dotnet_naming_style.static_prefix_style.required_prefix = s_ +dotnet_naming_style.static_prefix_style.capitalization = camel_case + +# Internal and private fields should be _camelCase +dotnet_naming_rule.camel_case_for_private_internal_fields.severity = suggestion +dotnet_naming_rule.camel_case_for_private_internal_fields.symbols = private_internal_fields +dotnet_naming_rule.camel_case_for_private_internal_fields.style = camel_case_underscore_style + +dotnet_naming_symbols.private_internal_fields.applicable_kinds = field +dotnet_naming_symbols.private_internal_fields.applicable_accessibilities = private, internal + +dotnet_naming_style.camel_case_underscore_style.required_prefix = _ +dotnet_naming_style.camel_case_underscore_style.capitalization = camel_case + +# Suggest more modern language features when available +dotnet_style_object_initializer = true:suggestion +dotnet_style_collection_initializer = true:suggestion +# Background Info: https://github.com/dotnet/runtime/pull/100250 +dotnet_style_prefer_collection_expression = when_types_exactly_match +dotnet_style_coalesce_expression = true:suggestion +dotnet_style_null_propagation = true:suggestion +dotnet_style_explicit_tuple_names = true:suggestion +dotnet_style_readonly_field = true:suggestion +dotnet_style_prefer_is_null_check_over_reference_equality_method = true:suggestion +dotnet_style_prefer_inferred_tuple_names = true:suggestion +dotnet_style_prefer_inferred_anonymous_type_member_names = true:suggestion +dotnet_style_prefer_conditional_expression_over_return = true:silent +dotnet_style_prefer_conditional_expression_over_assignment = true:silent +dotnet_style_prefer_auto_properties = true:suggestion +csharp_prefer_simple_default_expression = true:suggestion + +dotnet_code_quality_unused_parameters = non_public:suggestion + +# Dotnet diagnostic settings: +[*.cs] + +# CA1859: Use concrete types when possible for improved performance +# https://learn.microsoft.com/en-gb/dotnet/fundamentals/code-analysis/quality-rules/ca1859 +dotnet_diagnostic.CA1859.severity = suggestion + +# Disable SA1600 (ElementsMustBeDocumented) for test directory only +[test/**/*.cs] +dotnet_diagnostic.SA1600.severity = none + +# CSharp code style settings: +[*.cs] + +csharp_preserve_single_line_blocks = true +csharp_preserve_single_line_statements = false +csharp_prefer_braces = true:silent + +csharp_prefer_static_local_function = true:suggestion +csharp_prefer_simple_using_statement = false:none +csharp_style_prefer_switch_expression = true:suggestion +csharp_style_prefer_range_operator = false:none +csharp_style_prefer_index_operator = false:none +csharp_style_pattern_local_over_anonymous_function = false:none + +csharp_using_directive_placement = outside_namespace:suggestion + +# Indentation preferences +csharp_indent_block_contents = true +csharp_indent_braces = false +csharp_indent_case_contents = true +csharp_indent_case_contents_when_block = true +csharp_indent_switch_labels = true +csharp_indent_labels = one_less_than_current + +# Only use var when it's obvious what the variable type is +csharp_style_var_for_built_in_types = false:none +csharp_style_var_when_type_is_apparent = false:none +csharp_style_var_elsewhere = false:suggestion + +# Expression-bodied members +csharp_style_expression_bodied_local_functions = true:silent + +# Prefer method-like constructs to have a block body +csharp_style_expression_bodied_methods = false:none +csharp_style_expression_bodied_constructors = false:none +csharp_style_expression_bodied_operators = false:none + +# Prefer property-like constructs to have an expression-body +csharp_style_expression_bodied_properties = true:none +csharp_style_expression_bodied_indexers = true:none +csharp_style_expression_bodied_accessors = true:none + +# Suggest more modern language features when available +csharp_style_pattern_matching_over_is_with_cast_check = true:suggestion +csharp_style_pattern_matching_over_as_with_null_check = true:suggestion +csharp_style_inlined_variable_declaration = true:suggestion +csharp_style_throw_expression = true:suggestion +csharp_style_conditional_delegate_call = true:suggestion + +# Space preferences +csharp_space_after_cast = false +csharp_space_after_colon_in_inheritance_clause = true +csharp_space_after_comma = true +csharp_space_after_dot = false +csharp_space_after_keywords_in_control_flow_statements = true +csharp_space_after_semicolon_in_for_statement = true +csharp_space_around_binary_operators = before_and_after +csharp_space_around_declaration_statements = do_not_ignore +csharp_space_before_colon_in_inheritance_clause = true +csharp_space_before_comma = false +csharp_space_before_dot = false +csharp_space_before_open_square_brackets = false +csharp_space_before_semicolon_in_for_statement = false +csharp_space_between_empty_square_brackets = false +csharp_space_between_method_call_empty_parameter_list_parentheses = false +csharp_space_between_method_call_name_and_opening_parenthesis = false +csharp_space_between_method_call_parameter_list_parentheses = false +csharp_space_between_method_declaration_empty_parameter_list_parentheses = false +csharp_space_between_method_declaration_name_and_open_parenthesis = false +csharp_space_between_method_declaration_parameter_list_parentheses = false +csharp_space_between_parentheses = false +csharp_space_between_square_brackets = false + +# Newline settings +csharp_new_line_before_open_brace = all +csharp_new_line_before_else = true +csharp_new_line_before_catch = true +csharp_new_line_before_finally = true +csharp_new_line_before_members_in_object_initializers = true +csharp_new_line_before_members_in_anonymous_types = true +csharp_new_line_between_query_expression_clauses = true diff --git a/.gitattributes b/.gitattributes index a19ade077d3..c9033dc798a 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1 +1,7 @@ CHANGELOG.md merge=union +* text=auto +*.png binary +*.rtf binary +*.sh text eol=lf +testablescript.ps1 text eol=lf +TestFileCatalog.txt text eol=lf diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index 026714abb23..6de13fd8daf 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,79 +1,66 @@ # https://help.github.com/articles/about-codeowners/ # Areas are not limited to the filters defined in this file -# First Lets start with areas with no filters or paths +# First, let's start with areas with no filters or paths # Area: Performance -# @lzybkr @adityapatwardhan - -# Area: Portability -# @BrucePay @JamesWTruher +# @adityapatwardhan # Area: Security -# @TravisEz13 @PaulHigin @chunqingchen - -# Area: Documentation -# @joeyaiello @TravisEz13 - -# Area: Test -# @JamesWTruher @TravisEz13 @adityapatwardhan +src/System.Management.Automation/security/wldpNativeMethods.cs @TravisEz13 @seeminglyscience -# Area: Cmdlets Core -# @JamesWTruher @SteveL-MSFT @anmenaga @chunqingchen +# Area: CI Build +.github/workflows @PowerShell/powershell-maintainers @jshigetomi +.github/actions @PowerShell/powershell-maintainers @jshigetomi -# Now Areas that should have paths or filters, although we might not have them defined -# According to the docs, Order here must be by precedence of the filter, with later rules overwritting +# Now, areas that should have paths or filters, although we might not have them defined +# According to the docs, order here must be by precedence of the filter, with later rules overwritting # but the feature seems to make taking a union of all the matching rules. -# Area: CmdLets Management -src/Microsoft.PowerShell.Commands.Management/ @daxian-dbw @adityapatwardhan +# Area: Cmdlets Management +# src/Microsoft.PowerShell.Commands.Management/ @daxian-dbw @adityapatwardhan -# Area: CmdLets Utility -src/Microsoft.PowerShell.Commands.Utility/ @JamesWTruher @dantraMSFT @PaulHigin +# Area: Utility Cmdlets +# src/Microsoft.PowerShell.Commands.Utility/ # Area: Console -src/Microsoft.PowerShell.ConsoleHost/ @daxian-dbw @lzybkr @anmenaga - -# Area: Demo -demos/ @joeyaiello @SteveL-MSFT @HemantMahawar +# src/Microsoft.PowerShell.ConsoleHost/ @daxian-dbw # Area: DSC -src/System.Management.Automation/DscSupport @TravisEz13 @dantraMSFT +# src/System.Management.Automation/DscSupport @TravisEz13 @SteveL-MSFT -# Area: engine -src/System.Management.Automation/engine @daxian-dbw @vors @lzybkr @BrucePay +# Area: Engine +# src/System.Management.Automation/engine @daxian-dbw # Area: Debugging # Must be below engine to override -src/System.Management.Automation/engine/debugger/ @BrucePay @dantraMSFT @PaulHigin +# src/System.Management.Automation/engine/debugger/ -# Area: help -src/System.Management.Automation/help @Francisco-Gamino @adityapatwardhan +# Area: Help +src/System.Management.Automation/help @adityapatwardhan @daxian-dbw # Area: Intellisense -# @daxian-dbw @lzybkr @charub +# @daxian-dbw # Area: Language -# @daxian-dbw @vors @lzybkr @BrucePay +src/System.Management.Automation/engine/parser @daxian-dbw @seeminglyscience # Area: Providers -src/System.Management.Automation/namespaces @BrucePay @anmenaga - -# Area: PSReadLine -src/Microsoft.PowerShell.PSReadLine @lzybkr @charub +# src/System.Management.Automation/namespaces # Area: Remoting -src/System.Management.Automation/engine/remoting @dantraMSFT @mirichmo @PaulHigin - -# Area: Side-By-Side -# @mirichmo @charub +src/System.Management.Automation/engine/remoting @daxian-dbw @TravisEz13 # Areas: Build # Must be last -*.config @daxian-dbw @TravisEz13 @adityapatwardhan -*.props @daxian-dbw @TravisEz13 @adityapatwardhan -*.yml @daxian-dbw @TravisEz13 @adityapatwardhan -*.csproj @daxian-dbw @TravisEz13 @adityapatwardhan -build.* @daxian-dbw @TravisEz13 @adityapatwardhan -tools/ @daxian-dbw @TravisEz13 @adityapatwardhan -docker/ @daxian-dbw @TravisEz13 @adityapatwardhan +*.config @PowerShell/powershell-maintainers @jshigetomi +*.props @PowerShell/powershell-maintainers @jshigetomi +*.yml @PowerShell/powershell-maintainers @jshigetomi +*.csproj @PowerShell/powershell-maintainers @jshigetomi +build.* @PowerShell/powershell-maintainers @jshigetomi +tools/ @PowerShell/powershell-maintainers @jshigetomi +# docker/ @PowerShell/powershell-maintainers @jshigetomi + +# Area: Compliance +tools/terms @TravisEz13 +tools/credScan @TravisEz13 diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 3220d4a3189..776a9a8c60f 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -1,80 +1,97 @@ # Contributing to PowerShell -We welcome and appreciate contributions from the community. -There are many ways to become involved with PowerShell: -including filing issues, -joining in design conversations, -writing and improving documentation, -and contributing to the code. -Please read the rest of this document to ensure a smooth contribution process. +We welcome and appreciate contributions from the community! -## Intro to Git and GitHub +There are many ways to become involved with PowerShell including: -* Make sure you have a [GitHub account](https://github.com/signup/free). -* Learning Git: - * GitHub Help: [Good Resources for Learning Git and GitHub][good-git-resources] - * [Git Basics](../docs/git/basics.md): install and getting started -* [GitHub Flow Guide](https://guides.github.com/introduction/flow/): - step-by-step instructions of GitHub Flow +- [Contributing to Documentation](#contributing-to-documentation) +- [Contributing to Issues](#contributing-to-issues) +- [Contributing to Code](#contributing-to-code) -## Quick Start Checklist +Please read the rest of this document to ensure a smooth contribution process. -* Review the [Contribution License Agreement][CLA] requirement. -* Get familiar with the [PowerShell repository](../docs/git). +## Contributing to Documentation -## Contributing to Issues +Contributing to the docs is an excellent way to get started with the process of making open source contributions with minimal technical skill required. -* Review [Issue Management][issue-management]. -* Check if the issue you are going to file already exists in our [GitHub issues][open-issue]. -* If you can't find your issue already, - [open a new issue](https://github.com/PowerShell/PowerShell/issues/new), - making sure to follow the directions as best you can. -* If the issue is marked as [`Up-for-Grabs`][up-for-grabs], - the PowerShell Maintainers are looking for help with the issue. +Please see the [Contributor Guide in `MicrosoftDocs/PowerShell-Docs`](https://aka.ms/PSDocsContributor). -## Contributing to Documentation +Learn how to [Contribute to Docs like a Microsoft Insider](https://www.youtube.com/watch?v=ZQODV8krq1Q) (by @sdwheeler) -### Contributing to documentation related to PowerShell +### Updating Documentation for an existing cmdlet -Please see the [Contributor Guide in `PowerShell/PowerShell-Docs`](https://github.com/PowerShell/PowerShell-Docs/blob/staging/CONTRIBUTING.md). +If you made a change to an existing cmdlet and would like to update the documentation using PlatyPS, +here are the quick steps: -### Contributing to documentation related to maintaining or contributing to the PowerShell project +1. Install +`PlatyPS` +if you don't have it - +`Install-Module PlatyPS`. +1. Clone the +[`MicrosoftDocs/PowerShell-Docs`](https://github.com/MicrosoftDocs/PowerShell-Docs) +repository if you don't already have it. +1. Start your local build of PowerShell +(with the change to the cmdlet you made). +1. Find the cmdlet's Markdown file in PowerShell Docs - usually under +`PowerShell-Docs/reference///.md` +(Ex. `PowerShell-Docs/reference/7/Microsoft.PowerShell.Utility/Select-String.md`) +1. Run +`Update-MarkdownHelp -Path ` +which will update the documentation for you. +1. Make any additional changes needed for the cmdlet to be properly documented. +1. Send a Pull Request to the PowerShell Docs repository with the changes that +`PlatyPS` +made. +1. Link your Docs PR to your original change PR. + +### Style notes for documentation related to maintaining or contributing to the PowerShell project * When writing Markdown documentation, use [semantic linefeeds][]. In most cases, it means "one clause/idea per line". -* Otherwise, these issues should be treated like any other issue in this repo. - -#### Spellchecking documentation - -Documentation are spellchecked. We make use of the -[markdown-spellcheck](https://github.com/lukeapage/node-markdown-spellcheck) command line tool, -which can be run in interactive mode to correct typos or add words to the ignore list -(`.spelling` at the repository root). +* Otherwise, these issues should be treated like any other issue in this repository. -To run the spellchecker, follow the steps as follows: +### Spell checking documentation -* install [Node.js](https://nodejs.org/en/) (v6.4.0 or up) -* install [markdown-spellcheck](https://github.com/lukeapage/node-markdown-spellcheck) by - `npm install -g markdown-spellcheck` (v0.11.0 or up) -* run `mdspell "**/*.md" --ignore-numbers --ignore-acronyms` -* if the `.spelling` file is updated, commit and push it +Documentation is spellchecked. We use the +[textlint](https://github.com/textlint/textlint/wiki/Collection-of-textlint-rule) command-line tool, +which can be run in interactive mode to correct typos. -## Contributing to Code +To run the spell checker, follow these steps: -### Code Editor +* install [Node.js](https://nodejs.org/en/) (v10 or up) +* install [textlint](https://github.com/textlint/textlint/wiki/Collection-of-textlint-rule) by + `npm install -g textlint textlint-rule-terminology` +* run `textlint --rule terminology `, + adding `--fix` will accept all the recommendations. -You should use the multi-platform [Visual Studio Code (VS Code)][use-vscode-editor]. +If you need to add a term or disable checking part of a file see the [configuration sections of the rule](https://github.com/sapegin/textlint-rule-terminology). -### Building and testing +### Checking links in documentation -#### Building PowerShell +Documentation is link-checked. We make use of the +`markdown-link-check` command-line tool, +which can be run to see if any links are dead. -Please see [Building PowerShell](../README.md#building-the-repository). +To run the link-checker, follow these steps: -#### Testing PowerShell +* install [Node.js](https://nodejs.org/en/) (v10 or up) +* install `markdown-link-check` by + `npm install -g markdown-link-check@3.8.5` +* run `find . \*.md -exec markdown-link-check {} \;` -Please see PowerShell [Testing Guidelines - Running Tests Outside of CI][running-tests-outside-of-ci] on how to test you build locally. +## Contributing to Issues +1. Review [Issue Management][issue-management]. +1. Check if the issue you are going to file already exists in our [GitHub issues][open-issue]. +1. If you can't find your issue already, + [open a new issue](https://github.com/PowerShell/PowerShell/issues/new/choose), + making sure to follow the directions as best you can. +1. If the issue is marked as [`Up-for-Grabs`][up-for-grabs], + the PowerShell Maintainers are looking for help with the issue. +1. Issues marked as [`First-Time-Issue`][first-time-issue], + are identified as being easy and a great way to learn about this project and making + contributions. + ### Finding or creating an issue 1. Follow the instructions in [Contributing to Issues][contribute-issues] to find or open an issue. @@ -94,10 +111,65 @@ Additional references: * GitHub's guide on [Contributing to Open Source](https://guides.github.com/activities/contributing-to-open-source/#pull-request) * GitHub's guide on [Understanding the GitHub Flow](https://guides.github.com/introduction/flow/) +## Contributing to Code + +### Quick Start Checklist + +* Review the [Contributor License Agreement][CLA] requirement. +* Get familiar with the [PowerShell Repository Git Concepts](../docs/git/README.md). +* Start a [GitHub Codespace](#Dev Container) and start exploring the repository. +* Consider if what you want to do might be implementable as a [PowerShell Binary Module](https://learn.microsoft.com/powershell/scripting/developer/module/how-to-write-a-powershell-binary-module?view=powershell-7.5). + The PowerShell repository has a rigorous acceptance process due to its huge popularity and emphasis on stability and long term support, and with a binary module you can contribute to the community much more quickly. +* Pick an existing issue to work on! For instance, clarifying a confusing or unclear error message is a great starting point. + +### Intro to Git and GitHub + +1. Sign up for a [GitHub account](https://github.com/signup/free). +1. Learning Git and GitHub: + - [Git Basics](../docs/git/basics.md): install and getting started + - [Good Resources for Learning Git and GitHub][good-git-resources] +1. The PowerShell repository uses GitHub Flow as the primary branching strategy. [Learn about GitHub Flow](https://guides.github.com/introduction/flow/) + +### Code Editing + +PowerShell is primarily written in [C#](https://learn.microsoft.com/dotnet/csharp/tour-of-csharp/overview). While you can use any C# development environment you prefer, [Visual Studio Code][use-vscode-editor] is recommended. + +### Dev Container + +There is a PowerShell [Dev Container](https://code.visualstudio.com/docs/devcontainers/containers) which enables you get up and running quickly with a prepared Visual Studio Code environment with all the required prerequisites already installed. + +[GitHub Codespaces](https://github.com/features/codespaces) is the fastest way to get started. +Codespaces allows you to start a Github-hosted devcontainer from anywhere and contribute from your browser or via Visual Studio Code remoting. +All GitHub users get 15 hours per month of a 4-core codespace for free. + +To start a codespace for the PowerShell repository: + +1. Go to https://github.com/PowerShell/PowerShell +1. Click the green button on the right and choose to create a codespace + + ![alt text](Images/Codespaces.png) +1. Alternatively, just hit the comma `,` key on your keyboard which should instantly start a codespace as well. + +Once the codespace starts, you can press `ctrl+shift+b` (`cmd+shift+b` on Mac) to run the default build task. If you would like to interactivey test your changes, you can press `F5` to start debugging, add breakpoints, etc. + +[Learn more about how to get started with C# in Visual Studio Code](https://code.visualstudio.com/docs/csharp/get-started) + +### Building and Testing + +#### Building PowerShell + +[Building PowerShell](../README.md#Building-Powershell) has instructions for various platforms. + +#### Testing PowerShell + +Please see PowerShell [Testing Guidelines - Running Tests Outside of CI][running-tests-outside-of-ci] on how to test your build locally. + ### Lifecycle of a pull request #### Before submitting +* If your change would fix a security vulnerability, + first follow the [vulnerability issue reporting policy][vuln-reporting], before submitting a PR. * To avoid merge conflicts, make sure your branch is rebased on the `master` branch of this repository. * Many code changes will require new tests, so make sure you've added a new test if existing tests do not effectively test the code changed. @@ -105,41 +177,36 @@ Additional references: Each commit should be a **single complete** change. This discipline is important when reviewing the changes as well as when using `git bisect` and `git revert`. -#### Pull request submission +#### Pull request - Submission **Always create a pull request to the `master` branch of this repository**. -![Github-PR-dev.png](Images/Github-PR-dev.png) +![GitHub-PR.png](Images/GitHub-PR.png) +* It's recommended to avoid a PR with too many changes. + A large PR not only stretches the review time, but also makes it much harder to spot issues. + In such case, it's better to split the PR to multiple smaller ones. + For large features, try to approach it in an incremental way, so that each PR won't be too big. * If you're contributing in a way that changes the user or developer experience, you are expected to document those changes. - See [Contributing to documentation related to PowerShell](#contributing-to-documentation-related-to-powershell). + See [Contributing to documentation related to PowerShell](#contributing-to-documentation). * Add a meaningful title of the PR describing what change you want to check in. - Don't simply put: "Fixes issue #5". - A better example is: "Add Ensure parameter to New-Item cmdlet", with "Fixes #5" in the PR's body. + Don't simply put: "Fix issue #5". + Also don't directly use the issue title as the PR title. + An issue title is to briefly describe what is wrong, while a PR title is to briefly describe what is changed. + A better example is: "Add Ensure parameter to New-Item cmdlet", with "Fix #5" in the PR's body. * When you create a pull request, - including a summary of what's included in your changes and - if the changes are related to an existing GitHub issue, - please reference the issue in pull request description (e.g. ```Closes #11```). + include a summary about your changes in the PR description. + The description is used to create changelogs, + so try to have the first sentence explain the benefit to end users. + If the changes are related to an existing GitHub issue, + please reference the issue in the PR description (e.g. ```Fix #11```). See [this][closing-via-message] for more details. -* If the change warrants a note in the [changelog](../CHANGELOG.MD) - either update the changelog in your pull request or - add a comment in the PR description saying that the change may warrant a note in the changelog. - New changes always go into the **Unreleased** section. - Keeping the changelog up-to-date simplifies the release process for Maintainers. - An example (with an associated PR #): - - ```markdown - Unreleased - ---------- - - * `Update-Item` now supports `-FriendlyName` (#1234). - ``` * Please use the present tense and imperative mood when describing your changes: * Instead of "Adding support for Windows Server 2012 R2", write "Add support for Windows Server 2012 R2". * Instead of "Fixed for server connection issue", write "Fix server connection issue". - This form is akin to giving commands to the code base + This form is akin to giving commands to the codebase and is recommended by the Git SCM developers. It is also used in the [Git commit messages](#common-engineering-practices). * If the change is related to a specific resource, please prefix the description with the resource name: @@ -151,7 +218,38 @@ Additional references: As an example, this requirement includes any changes to cmdlets (including cmdlet parameters) and features which have associated about_* topics. While not required, we appreciate any contributors who add this label and create the issue themselves. Even better, all contributors are free to contribute the documentation themselves. - (See [Contributing to documentation related to PowerShell](#contributing-to-documentation-related-to-powershell) for more info.) + (See [Contributing to documentation related to PowerShell](#contributing-to-documentation) for more info.) +* If your change adds a new source file, ensure the appropriate copyright and license headers is on top. + It is standard practice to have both a copyright and license notice for each source file. + * For `.cs` files use the copyright header with empty line after it: + + ```c# + // Copyright (c) Microsoft Corporation. + // Licensed under the MIT License. + + ``` + + * For `.ps1` and `.psm1` files use the copyright header with empty line after it: + + ```powershell + # Copyright (c) Microsoft Corporation. + # Licensed under the MIT License. + + ``` + +* If your change adds a new module manifest (.psd1 file), ensure that: + + ```powershell + Author = "PowerShell" + Company = "Microsoft Corporation" + Copyright = "Copyright (c) Microsoft Corporation." + ``` + + is at the top. + +### Pull Request - Work in Progress + +* If your pull request is not ready to merge, please add the prefix `WIP:` to the beginning of the title and remove the prefix when the PR is ready. #### Pull Request - Automatic Checks @@ -161,81 +259,98 @@ Additional references: * Make sure you follow the [Common Engineering Practices](#common-engineering-practices) and [testing guidelines](../docs/testing-guidelines/testing-guidelines.md). * After submitting your pull request, - our [CI system (Travis CI and AppVeyor)][ci-system] + our [CI system (Azure DevOps Pipelines)][ci-system] will run a suite of tests and automatically update the status of the pull request. -* Our CI contains automated spellchecking. If there is any false-positive, - [run the spellchecker command line tool in interactive mode](#spellchecking-documentation) +* Our CI contains automated spell checking and link checking for Markdown files. If there is any false-positive, + [run the spell checker command-line tool in interactive mode](#spell-checking-documentation) to add words to the `.spelling` file. +* Our packaging test may not pass and ask you to update `files.wxs` file if you add/remove/update nuget package references or add/remove assert files. -#### Pull Request - Code Review - -* Roles and Responsibilities of a PR: Author, Reviewer, and Assignee - * Reviewer and Assignee are two separate roles of a PR. - * A Reviewer can be anyone who wants to contribute. - A Reviewer reviews the change of a PR, - leaves comments for the Author to address, - and approves the PR when the change looks good. - * An Assignee must be a [Maintainer](../docs/maintainers), who monitors the progress of the PR, - coordinates the review process, and merges the PR after it's been approved. - The Assignee may or may not be a Reviewer of the PR at the same time. - * An Author is encouraged to choose Reviewer(s) and an Assignee for the PR. - If no Assignee is chosen, one of the Maintainers shall be assigned to it. - If no Reviewer is chosen, the Assignee shall choose Reviewer(s) as appropriate. - * If an Author is a [PowerShell Team](https://github.com/orgs/PowerShell/people) member, - then the Author **is required** to choose Reviewer(s) and an Assignee for the PR. - * For a PR to be merged, it must be approved by at least one PowerShell Team member or Collaborator, - so additional Reviewer(s) may be added by the Assignee as appropriate. - The Assignee may also be re-assigned by Maintainers. -* A Reviewer can postpone the code review if CI builds fail, - but also can start the code review early regardless of the CI builds. -* The Author **is responsible** for driving the PR to the Approved state. - The Author addresses review comments, and pings Reviewer(s) to start the next iteration. - If the review is making no progress (or very slow), - the Author can always ask the Assignee to help coordinate the process and keep it moving. -* Additional feedback is always welcome! - Even if you are not designated as a Reviewer, - feel free to review others' pull requests anyway. - Leave your comments even if everything looks good; - a simple "Looks good to me" or "LGTM" will suffice. - This way we know someone has already taken a look at it! -* When updating your pull request, please **create new commits** - and **don't rewrite the commits history**. This way it's very easy for - the reviewers to see diff between iterations. - If you rewrite the history in the pull request, review could be much slower. - Once the review is done, you can rewrite the history to make it prettier, - if you like. - Otherwise it's likely would be squashed on merge to master. -* Once the code review is done, - all merge conflicts are resolved, - and the CI system build status is passing, - the PR Assignee will merge your changes. -* For more information on the PowerShell Maintainers' process, - see the [documentation](../docs/maintainers). - -## Making Breaking Changes + You could update the file manually in accordance with messages in the test log file. Or you can use automatically generated file. To get the file you should build the msi package locally: + + ```powershell + Import-Module .\build.psm1 + Start-PSBuild -Clean -CrossGen -PSModuleRestore -Runtime win7-x64 -Configuration Release -ReleaseTag + Import-Module .\tools\packaging + Start-PSPackage -Type msi -ReleaseTag -WindowsRuntime 'win7-x64' -SkipReleaseChecks + ``` + + Last command will report where new file is located. + +#### Pull Request - Workflow + +1. The PR *author* creates a pull request from a fork. +1. The *author* ensures that their pull request passes the [CI system][ci-system] build. + - If the build fails, a [Repository Maintainer][repository-maintainer] adds the `Review - waiting on author` label to the pull request. + The *author* can then continue to update the pull request until the build passes. +1. If the *author* knows whom should participate in the review, they should add them otherwise they can add the recommended *reviewers*. +1. Once the build passes, if there is not sufficient review, the *maintainer* adds the `Review - needed` label. +1. An [Area Expert][area-expert] should also review the pull request. + - If the *author* does not meet the *reviewer*'s standards, the *reviewer* makes comments. A *maintainer* then removes the `Review - needed` label and adds + the `Review - waiting on author` label. The *author* must address the comments and repeat from step 2. + - If the *author* meets the *reviewer*'s standards, the *reviewer* approves the PR. A maintainer then removes the `need review` label. +1. Once the code review is completed, a *maintainer* merges the pull request after one business day to allow for additional critical feedback. + +#### Pull Request - Roles and Responsibilities + +1. The PR *author* is responsible for moving the PR forward to get it approved. + This includes addressing feedback within a timely period and indicating feedback has been addressed by adding a comment and mentioning the specific *reviewers*. + When updating your pull request, please **create new commits** and **don't rewrite the commits history**. + This way it's very easy for the reviewers to see diff between iterations. + If you rewrite the history in the pull request, review could be much slower. + The PR is likely to be squash-merged to master by the *assignee*. +1. *Reviewers* are anyone who wants to contribute. + They are responsible for ensuring the code: addresses the issue being fixed, does not create new issues (functional, performance, reliability, or security), and implements proper design. + *Reviewers* should use the `Review changes` drop down to indicate they are done with their review. + - `Request changes` if you believe the PR merge should be blocked if your feedback is not addressed, + - `Approve` if you believe your feedback has been addressed or the code is fine as-is, it is customary (although not required) to leave a simple "Looks good to me" (or "LGTM") as the comment for approval. + - `Comment` if you are making suggestions that the *author* does not have to accept. + Early in the review, it is acceptable to provide feedback on coding formatting based on the published [Coding Guidelines][coding-guidelines], however, + after the PR has been approved, it is generally *not* recommended to focus on formatting issues unless they go against the [Coding Guidelines][coding-guidelines]. + Non-critical late feedback (after PR has been approved) can be submitted as a new issue or new pull request from the *reviewer*. +1. *Assignees* who are always *Maintainers* ensure that proper review has occurred and if they believe one approval is not sufficient, the *maintainer* is responsible to add more reviewers. + An *assignee* may also be a reviewer, but the roles are distinct. + Once the PR has been approved and the CI system is passing, the *assignee* will merge the PR after giving one business day for any critical feedback. + For more information on the PowerShell Maintainers' process, see the [documentation](../docs/maintainers). + +#### Pull Requests - Abandoned + +A pull request with the label `Review - waiting on author` for **more than two weeks** without a word from the author is considered abandoned. + +In these cases: + +1. *Assignee* will ping the author of PR to remind them of pending changes. + - If the *author* responds, it's no longer an abandoned; the pull request proceeds as normal. +1. If the *author* does not respond **within a week**: + - If the *reviewer*'s comments are very minor, merge the change, fix the code immediately, and create a new PR with the fixes addressing the minor comments. + - If the changes required to merge the pull request are significant but needed, *assignee* creates a new branch with the changes and open an issue to merge the code into the dev branch. + Mention the original pull request ID in the description of the new issue and close the abandoned pull request. + - If the changes in an abandoned pull request are no longer needed (e.g. due to refactoring of the codebase or a design change), *assignee* will simply close the pull request. + +### Making Breaking Changes When you make code changes, -please pay attention to these that can affect the [Public Contract](../docs/dev-process/breaking-change-contract.md). +please pay attention to these that can affect the [Public Contract][breaking-changes-contract]. For example, changing PowerShell parameters, APIs, or protocols break the public contract. Before making changes to the code, -first review the [breaking changes contract](../docs/dev-process/breaking-change-contract.md) +first review the [breaking changes contract][breaking-changes-contract] and follow the guidelines to keep PowerShell backward compatible. -## Making Design Changes +### Making Design Changes To add new features such as cmdlets or making design changes, -please follow the [PowerShell Request for Comments (RFC)](https://github.com/PowerShell/PowerShell-RFC) process. +please follow the [PowerShell Request for Comments (RFC)][rfc-process] process. -## Common Engineering Practices +### Common Engineering Practices -Other than the guidelines for ([coding](../docs/dev-process/coding-guidelines.md), -the [RFC process](https://github.com/PowerShell/PowerShell-RFC) for design, -[documentation](#contributing-to-documentation) and [testing](../docs/testing-guidelines/testing-guidelines.md)) discussed above, +Other than the guidelines for [coding][coding-guidelines], +the [RFC process][rfc-process] for design, +[documentation](#contributing-to-documentation) and [testing](../docs/testing-guidelines/testing-guidelines.md) discussed above, we encourage contributors to follow these common engineering practices: * Format commit messages following these guidelines: -``` +```text Summarize change in 50 characters or less Similar to email, this is the body of the commit message, @@ -264,7 +379,7 @@ Using semantic line feeds (breaks that separate ideas) is also appropriate, as is using Markdown syntax. ``` -* These are based on Tim Pope's [guidelines](http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html), +* These are based on Tim Pope's [guidelines](https://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html), Git SCM [submitting patches](https://git.kernel.org/cgit/git/git.git/tree/Documentation/SubmittingPatches), Brandon Rhodes' [semantic linefeeds][], and John Gruber's [Markdown syntax](https://daringfireball.net/projects/markdown/syntax). @@ -272,30 +387,37 @@ is also appropriate, as is using Markdown syntax. If you find code that you think is a good fit to add to PowerShell, file an issue and start a discussion before proceeding. * Create and/or update tests when making code changes. -* Run tests and ensure they are passing before pull request. +* Run tests and ensure they are passing before opening a pull request. * All pull requests **must** pass CI systems before they can be approved. * Avoid making big pull requests. Before you invest a large amount of time, file an issue and start a discussion with the community. -## Contributor License Agreement (CLA) +### Contributor License Agreement (CLA) To speed up the acceptance of any contribution to any PowerShell repositories, -you could [sign a Microsoft Contribution Licensing Agreement (CLA)](https://cla.microsoft.com/) ahead of time. -If you've already contributed to PowerShell repositories in the past, congratulations! +you should sign the Microsoft [Contributor License Agreement (CLA)](https://cla.microsoft.com/) ahead of time. +If you've already contributed to PowerShell or Microsoft repositories in the past, congratulations! You've already completed this step. This a one-time requirement for the PowerShell project. Signing the CLA process is simple and can be done in less than a minute. You don't have to do this up-front. You can simply clone, fork, and submit your pull request as usual. -When your pull request is created, it is classified by a CLA bot. -If the change is trivial, it's classified as `cla-required`. -Once you sign a CLA, all your existing and future pull requests will be labeled as `cla-signed`. +When your pull request is created, it is checked by the CLA bot. +If you have signed the CLA, the status check will be set to `passing`. Otherwise, it will stay at `pending`. +Once you sign a CLA, all your existing and future pull requests will have the status check automatically set at `passing`. + +## Code of Conduct Enforcement + +Reports of abuse will be reviewed by the [PowerShell Committee][ps-committee] and if it has been determined that violations of the +[Code of Conduct](../CODE_OF_CONDUCT.md) has occurred, then a temporary ban may be imposed. +The duration of the temporary ban will depend on the impact and/or severity of the infraction. +This can vary from 1 day, a few days, a week, and up to 30 days. +Repeat offenses may result in a permanent ban from the PowerShell org. -[testing-guidelines]: ../docs/testing-guidelines/testing-guidelines.md [running-tests-outside-of-ci]: ../docs/testing-guidelines/testing-guidelines.md#running-tests-outside-of-ci [issue-management]: ../docs/maintainers/issue-management.md -[governance]: ../docs/community/governance.md +[vuln-reporting]: ./SECURITY.md [using-prs]: https://help.github.com/articles/using-pull-requests/ [fork-a-repo]: https://help.github.com/articles/fork-a-repo/ [closing-via-message]: https://help.github.com/articles/closing-issues-via-commit-messages/ @@ -305,6 +427,13 @@ Once you sign a CLA, all your existing and future pull requests will be labeled [contribute-issues]: #contributing-to-issues [open-issue]: https://github.com/PowerShell/PowerShell/issues [up-for-grabs]: https://github.com/powershell/powershell/issues?q=is%3Aopen+is%3Aissue+label%3AUp-for-Grabs -[semantic linefeeds]: http://rhodesmill.org/brandon/2012/one-sentence-per-line/ +[semantic linefeeds]: https://rhodesmill.org/brandon/2012/one-sentence-per-line/ [PowerShell-Docs]: https://github.com/powershell/powershell-docs/ -[use-vscode-editor]: ../docs/learning-powershell/using-vscode.md#editing-with-visual-studio-code +[use-vscode-editor]: https://learn.microsoft.com/dotnet/core/tutorials/with-visual-studio-code +[repository-maintainer]: ../docs/community/governance.md#repository-maintainers +[area-expert]: ../.github/CODEOWNERS +[first-time-issue]: https://github.com/powershell/powershell/issues?q=is%3Aopen+is%3Aissue+label%3AFirst-Time-Issue +[coding-guidelines]: ../docs/dev-process/coding-guidelines.md +[breaking-changes-contract]: ../docs/dev-process/breaking-change-contract.md +[rfc-process]: https://github.com/PowerShell/PowerShell-RFC +[ps-committee]: ../docs/community/governance.md#powershell-committee diff --git a/.github/ISSUE_TEMPLATE.md b/.github/ISSUE_TEMPLATE.md deleted file mode 100644 index 5c21d4eeb8c..00000000000 --- a/.github/ISSUE_TEMPLATE.md +++ /dev/null @@ -1,43 +0,0 @@ - - -Steps to reproduce ------------------- - -```powershell - -``` - -Expected behavior ------------------ - -```none - -``` - -Actual behavior ---------------- - -```none - -``` - -Environment data ----------------- - - - -```powershell -> $PSVersionTable - -``` diff --git a/.github/ISSUE_TEMPLATE/Bug_Report.yaml b/.github/ISSUE_TEMPLATE/Bug_Report.yaml new file mode 100644 index 00000000000..03fcf444e88 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/Bug_Report.yaml @@ -0,0 +1,75 @@ +name: Bug report 🐛 +description: Report errors or unexpected behavior 🤔 +labels: Needs-Triage +body: +- type: markdown + attributes: + value: > + For Windows PowerShell 5.1 issues, suggestions, or feature requests please use the + [Feedback Hub app](https://support.microsoft.com/windows/send-feedback-to-microsoft-with-the-feedback-hub-app-f59187f8-8739-22d6-ba93-f66612949332) + + This repository is **ONLY** for PowerShell Core 6 and PowerShell 7+ issues. +- type: checkboxes + attributes: + label: Prerequisites + options: + - label: Write a descriptive title. + required: true + - label: Make sure you are able to repro it on the [latest released version](https://github.com/PowerShell/PowerShell/releases) + required: true + - label: Search the existing issues. + required: true + - label: Refer to the [FAQ](https://github.com/PowerShell/PowerShell/blob/master/docs/FAQ.md). + required: true + - label: Refer to [Differences between Windows PowerShell 5.1 and PowerShell](https://learn.microsoft.com/powershell/scripting/whats-new/differences-from-windows-powershell). + required: true +- type: textarea + attributes: + label: Steps to reproduce + description: > + List of steps, sample code, failing test or link to a project that reproduces the behavior. + Make sure you place a stack trace inside a code (```) block to avoid linking unrelated issues. + placeholder: > + I am experiencing a problem with X. + I think Y should be happening but Z is actually happening. + validations: + required: true +- type: textarea + attributes: + label: Expected behavior + render: console + placeholder: | + PS> 2 + 2 + 4 + validations: + required: true +- type: textarea + attributes: + label: Actual behavior + render: console + placeholder: | + PS> 2 + 2 + 5 + validations: + required: true +- type: textarea + attributes: + label: Error details + description: Paste verbatim output from `Get-Error` if PowerShell return an error. + render: console + placeholder: PS> Get-Error +- type: textarea + attributes: + label: Environment data + description: Paste verbatim output from `$PSVersionTable` below. + render: powershell + placeholder: PS> $PSVersionTable + validations: + required: true +- type: textarea + attributes: + label: Visuals + description: > + Please upload images or animations that can be used to reproduce issues in the area below. + Try the [Steps Recorder](https://support.microsoft.com/en-us/windows/record-steps-to-reproduce-a-problem-46582a9b-620f-2e36-00c9-04e25d784e47) + on Windows or [Screenshot](https://support.apple.com/en-us/HT208721) on macOS. diff --git a/.github/ISSUE_TEMPLATE/Feature_Request.yaml b/.github/ISSUE_TEMPLATE/Feature_Request.yaml new file mode 100644 index 00000000000..c8e4cec3c4d --- /dev/null +++ b/.github/ISSUE_TEMPLATE/Feature_Request.yaml @@ -0,0 +1,20 @@ +name: Feature Request / Idea 🚀 +description: Suggest a new feature or improvement (this does not mean you have to implement it) +labels: [Issue-Enhancement, Needs-Triage] +body: +- type: textarea + attributes: + label: Summary of the new feature / enhancement + description: > + A clear and concise description of what the problem is that the + new feature would solve. Try formulating it in user story style + (if applicable). + placeholder: "'As a user I want X so that Y...' with X being the being the action and Y being the value of the action." + validations: + required: true +- type: textarea + attributes: + label: Proposed technical implementation details (optional) + placeholder: > + A clear and concise description of what you want to happen. + Consider providing an example PowerShell experience with expected result. diff --git a/.github/ISSUE_TEMPLATE/Microsoft_Update_Issue.yaml b/.github/ISSUE_TEMPLATE/Microsoft_Update_Issue.yaml new file mode 100644 index 00000000000..ce3de7ae848 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/Microsoft_Update_Issue.yaml @@ -0,0 +1,87 @@ +name: Microsoft Update issue report 🐛 +description: Report issue installing a PowerShell 7 Update or fresh install through Microsoft Update 🤔 +labels: Needs-Triage +assignees: + - TravisEz13 +body: +- type: markdown + attributes: + value: > + For Windows PowerShell 5.1 issues, suggestions, or feature requests please use the + [Feedback Hub app](https://support.microsoft.com/windows/send-feedback-to-microsoft-with-the-feedback-hub-app-f59187f8-8739-22d6-ba93-f66612949332) + + This repository is **ONLY** for PowerShell Core 6 and PowerShell 7+ issues. +- type: checkboxes + attributes: + label: Prerequisites + options: + - label: Write a descriptive title. + required: true + - label: Make sure you are able to repro it on the [latest released version](https://github.com/PowerShell/PowerShell/releases) + required: true + - label: Search the existing issues. + required: true + - label: Refer to the [FAQ](https://github.com/PowerShell/PowerShell/blob/master/docs/FAQ.md). + required: true + - label: Refer to [Differences between Windows PowerShell 5.1 and PowerShell](https://learn.microsoft.com/powershell/scripting/whats-new/differences-from-windows-powershell). + required: true +- type: textarea + attributes: + label: Steps to reproduce + description: > + List of steps, sample code, failing test or link to a project that reproduces the behavior. + Make sure you place a stack trace inside a code (```) block to avoid linking unrelated issues. + placeholder: > + I am experiencing a problem with X. + I think Y should be happening but Z is actually happening. + validations: + required: true +- type: textarea + attributes: + label: Expected behavior + render: console + placeholder: | + PS> 2 + 2 + 4 + validations: + required: true +- type: textarea + attributes: + label: Actual behavior + render: console + placeholder: | + PS> 2 + 2 + 5 + validations: + required: true +- type: textarea + attributes: + label: Environment data + description: Paste verbatim output from `$PSVersionTable` below. + render: powershell + placeholder: PS> $PSVersionTable + validations: + required: true +- type: textarea + attributes: + label: OS Data + description: Paste verbatim output from `(Get-CimInstance Win32_OperatingSystem) | Select-Object -Property Version, Caption` below. + render: powershell + placeholder: PS> (Get-CimInstance Win32_OperatingSystem) | Select-Object -Property Version, Caption + validations: + required: true +- type: textarea + attributes: + label: Windows update log + description: Please run `Get-WindowsUpdateLog` and upload the resulting file to this issue. + render: markdown + placeholder: PS> Get-WindowsUpdateLog + validations: + required: true +- type: textarea + attributes: + label: Visuals + description: > + Please upload images or animations that can be used to reproduce issues in the area below. + Try the [Steps Recorder](https://support.microsoft.com/en-us/windows/record-steps-to-reproduce-a-problem-46582a9b-620f-2e36-00c9-04e25d784e47) + on Windows or [Screenshot](https://support.apple.com/en-us/HT208721) on macOS. diff --git a/.github/ISSUE_TEMPLATE/Release_Process.yaml b/.github/ISSUE_TEMPLATE/Release_Process.yaml new file mode 100644 index 00000000000..7e8d6282db1 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/Release_Process.yaml @@ -0,0 +1,41 @@ +name: Release Process +description: Maintainers Only - Release Process +title: "Release Process for v7.x.x" +labels: [Issue-Meta, Needs-Triage] +body: +- type: markdown + attributes: + value: > + This template is for maintainers to create an issues to track the release process. + Please **only** use this template if you are a maintainer. +- type: textarea + attributes: + label: Checklist + value: | + - [ ] Verify that [`PowerShell-Native`](https://github.com/PowerShell/PowerShell-Native) has been updated / released as needed. + - [ ] Check for `PowerShellGet` and `PackageManagement` release plans. + - [ ] Start process to sync Azure DevOps artifacts feed such as modules and NuGet packages. + - [ ] Create a private branch named `release/v6.x.x` in Azure DevOps repository. + All release related changes should happen in this branch. + - [ ] Prepare packages + - [ ] Kick off coordinated build. + - [ ] Kick off Release pipeline. + - *These tasks are orchestrated by the release pipeline, but here as status to the community.* + - [ ] Prepare packages + - [ ] Sign the RPM package. + - [ ] Install and verify the packages. + - [ ] Trigger the docker staging builds (signing must be done). + - [ ] Create the release tag and push the tag to `PowerShell/PowerShell` repository. + - [ ] Run tests on all supported Linux distributions and publish results. + - [ ] Update documentation, and scripts. + - [ ] Update [CHANGELOG.md](../../CHANGELOG.md) with the finalized change log draft. + - [ ] Stage a PR to master to update other documents and + scripts to use the new package names, links, and `metadata.json`. + - [ ] For preview releases, + merge the release branch to GitHub `master` with a merge commit. + - [ ] For non-preview releases, + make sure all changes are either already in master or have a PR open. + - [ ] Delete the release branch. + - [ ] Trigger the Docker image release. + - [ ] Retain builds. + - [ ] Update https://github.com/dotnet/dotnet-docker/tree/master/3.0/sdk with new version and SHA hashes for global tool. NOTE: this link is broken! diff --git a/.github/ISSUE_TEMPLATE/WG_member_request.yaml b/.github/ISSUE_TEMPLATE/WG_member_request.yaml new file mode 100644 index 00000000000..1d7f0e9ba53 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/WG_member_request.yaml @@ -0,0 +1,66 @@ +name: Working Group Member Request +description: Request membership to serve on a PowerShell Working Group +title: Working Group Member Request +labels: [WG-NeedsReview, WG-Cmdlets, WG-Engine, WG-Interactive-Console, WG-Remoting, Needs-Triage] +body: +- type: markdown + attributes: + value: | + ## Thank you for your interest in joining a PowerShell Working Group. + + ### Please complete the following public form to request membership to a PowerShell Working Group. + + > [!NOTE] + > Not all Working Groups are accepting new members at this time. +- type : dropdown + id : request_type + validations: + required: true + attributes: + label: Name of Working Group you are requesting to join? + description: >- + Please select the name of the working group you are requesting to join. (Select one) + options: + - "Cmdlets and Modules" + - "Engine" + - "Interactive UX" + - "Remoting" +- type: dropdown + id: time + validations: + required: true + attributes: + label: Can you provide at least 1 hour per week to the Working Group? Note that time commitments will vary per Working Group and decided by its members. + description: >- + Please select Yes or No. + options: + - "Yes" + - "No" +- type: markdown + attributes: + value: | + ## ⚠️ This form is public. Do not provide any private or proprietary information. ⚠️ +- type: textarea + attributes: + label: Why do you want to join this working group? + description: Please provide a brief description of why you want to join this working group. + placeholder: > + I want to join this working group because... + validations: + required: true +- type: textarea + attributes: + label: What skills do you bring to this working group? + description: Please provide a brief description of what skills you bring to this working group. + placeholder: > + I bring the following skills to this working group... + validations: + required: true +- type: textarea + attributes: + label: Public links to articles, code, or other resources that demonstrate your skills. + description: Please provide public links to articles, code, or other resources that demonstrate your skills. + placeholder: > + I have the following public links to articles, code, or other resources that demonstrate my skills... + validations: + required: true diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 00000000000..973921cb24a --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,11 @@ +blank_issues_enabled: false +contact_links: + - name: Windows PowerShell + url: https://support.microsoft.com/windows/send-feedback-to-microsoft-with-the-feedback-hub-app-f59187f8-8739-22d6-ba93-f66612949332 + about: Windows PowerShell issues or suggestions. + - name: Support + url: https://github.com/PowerShell/PowerShell/blob/master/.github/SUPPORT.md + about: PowerShell Support Questions/Help + - name: Documentation Issue + url: https://github.com/MicrosoftDocs/PowerShell-Docs/issues/new/choose + about: Please open issues on documentation for PowerShell here. diff --git a/.github/Images/Codespaces.png b/.github/Images/Codespaces.png new file mode 100644 index 00000000000..f37792f5c9f Binary files /dev/null and b/.github/Images/Codespaces.png differ diff --git a/.github/Images/GitHub-PR.png b/.github/Images/GitHub-PR.png new file mode 100644 index 00000000000..1ae852aecd5 Binary files /dev/null and b/.github/Images/GitHub-PR.png differ diff --git a/.github/Images/Github-PR-dev.png b/.github/Images/Github-PR-dev.png deleted file mode 100644 index 80bb77e4830..00000000000 Binary files a/.github/Images/Github-PR-dev.png and /dev/null differ diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 4ccc6feb551..27089847987 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,8 +1,31 @@ - -If you are a PowerShell Team member, please make sure you choose the Reviewer(s) and Assignee for your PR. -If you are not from the PowerShell Team, you can leave the fields blank and the Maintainers will choose them for you. If you are familiar with the team, feel free to mention some Reviewers yourself. +# PR Summary -For more information about the roles of Reviewer and Assignee, refer to CONTRIBUTING.md. + ---> +## PR Context + + + +## PR Checklist + +- [ ] [PR has a meaningful title](https://github.com/PowerShell/PowerShell/blob/master/.github/CONTRIBUTING.md#pull-request---submission) + - Use the present tense and imperative mood when describing your changes +- [ ] [Summarized changes](https://github.com/PowerShell/PowerShell/blob/master/.github/CONTRIBUTING.md#pull-request---submission) +- [ ] [Make sure all `.h`, `.cpp`, `.cs`, `.ps1` and `.psm1` files have the correct copyright header](https://github.com/PowerShell/PowerShell/blob/master/.github/CONTRIBUTING.md#pull-request---submission) +- [ ] This PR is ready to merge. If this PR is a work in progress, please open this as a [Draft Pull Request and mark it as Ready to Review when it is ready to merge](https://docs.github.com/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-pull-requests#draft-pull-requests). +- **[Breaking changes](https://github.com/PowerShell/PowerShell/blob/master/.github/CONTRIBUTING.md#making-breaking-changes)** + - [ ] None + - **OR** + - [ ] [Experimental feature(s) needed](https://github.com/MicrosoftDocs/PowerShell-Docs/blob/main/reference/7.5/Microsoft.PowerShell.Core/About/about_Experimental_Features.md) + - [ ] Experimental feature name(s): +- **User-facing changes** + - [ ] Not Applicable + - **OR** + - [ ] [Documentation needed](https://github.com/PowerShell/PowerShell/blob/master/.github/CONTRIBUTING.md#pull-request---submission) + - [ ] Issue filed: +- **Testing - New and feature** + - [ ] N/A or can only be tested interactively + - **OR** + - [ ] [Make sure you've added a new test if existing tests do not effectively test the code changed](https://github.com/PowerShell/PowerShell/blob/master/.github/CONTRIBUTING.md#before-submitting) diff --git a/.github/SECURITY.md b/.github/SECURITY.md new file mode 100644 index 00000000000..f941d308b1f --- /dev/null +++ b/.github/SECURITY.md @@ -0,0 +1,41 @@ + + +## Security + +Microsoft takes the security of our software products and services seriously, which includes all source code repositories managed through our GitHub organizations, which include [Microsoft](https://github.com/Microsoft), [Azure](https://github.com/Azure), [DotNet](https://github.com/dotnet), [AspNet](https://github.com/aspnet), [Xamarin](https://github.com/xamarin) and [PowerShell](https://github.com/PowerShell). + +If you believe you have found a security vulnerability in any Microsoft-owned repository that meets [Microsoft's definition of a security vulnerability](https://aka.ms/security.md/definition), please report it to us as described below. + +## Reporting Security Issues + +**Please do not report security vulnerabilities through public GitHub issues.** + +Instead, please report them to the Microsoft Security Response Center (MSRC) at [https://msrc.microsoft.com/create-report](https://aka.ms/security.md/msrc/create-report). + +If you prefer to submit without logging in, send email to [secure@microsoft.com](mailto:secure@microsoft.com). If possible, encrypt your message with our PGP key; please download it from the [Microsoft Security Response Center PGP Key page](https://aka.ms/security.md/msrc/pgp). + +You should receive a response within 24 hours. If for some reason you do not, please follow up via email to ensure we received your original message. Additional information can be found at [microsoft.com/msrc](https://www.microsoft.com/msrc). + +Please include the requested information listed below (as much as you can provide) to help us better understand the nature and scope of the possible issue: + + * Type of issue (e.g. buffer overflow, SQL injection, cross-site scripting, etc.) + * Full paths of source file(s) related to the manifestation of the issue + * The location of the affected source code (tag/branch/commit or direct URL) + * Any special configuration required to reproduce the issue + * Step-by-step instructions to reproduce the issue + * Proof-of-concept or exploit code (if possible) + * Impact of the issue, including how an attacker might exploit the issue + +This information will help us triage your report more quickly. + +If you are reporting for a bug bounty, more complete reports can contribute to a higher bounty award. Please visit our [Microsoft Bug Bounty Program](https://aka.ms/security.md/msrc/bounty) page for more details about our active programs. + +## Preferred Languages + +We prefer all communications to be in English. + +## Policy + +Microsoft follows the principle of [Coordinated Vulnerability Disclosure](https://aka.ms/security.md/cvd). + + diff --git a/.github/SUPPORT.md b/.github/SUPPORT.md new file mode 100644 index 00000000000..6acedb28d27 --- /dev/null +++ b/.github/SUPPORT.md @@ -0,0 +1,13 @@ +# PowerShell Support + +If you have any problems, please consult the [known issues][], developer [FAQ][], and [GitHub issues][]. +If you do not see your problem captured, please file a [new issue][] and follow the provided template. +Also make sure to see the [Official Support Policy][]. +If you know how to fix the issue, feel free to send a pull request our way. (The [Contribution Guides][] apply to that pull request, you may want to give it a read!) + +[Official Support Policy]: https://learn.microsoft.com/powershell/scripting/powershell-support-lifecycle +[FAQ]: https://github.com/PowerShell/PowerShell/tree/master/docs/FAQ.md +[Contribution Guides]: https://github.com/PowerShell/PowerShell/tree/master/.github/CONTRIBUTING.md +[known issues]: https://learn.microsoft.com/powershell/scripting/whats-new/differences-from-windows-powershell +[GitHub issues]: https://github.com/PowerShell/PowerShell/issues +[new issue]: https://github.com/PowerShell/PowerShell/issues/new/choose diff --git a/.github/action-filters.yml b/.github/action-filters.yml new file mode 100644 index 00000000000..9a61bc1947b --- /dev/null +++ b/.github/action-filters.yml @@ -0,0 +1,23 @@ +github: &github + - .github/actions/** + - .github/workflows/**-ci.yml +tools: &tools + - tools/buildCommon/** + - tools/ci.psm1 +props: &props + - '**.props' +tests: &tests + - test/powershell/** + - test/tools/** + - test/xUnit/** +mainSource: &mainSource + - src/** +buildModule: &buildModule + - build.psm1 +source: + - *github + - *tools + - *props + - *buildModule + - *mainSource + - *tests diff --git a/.github/actions/build/ci/action.yml b/.github/actions/build/ci/action.yml new file mode 100644 index 00000000000..be9c0ecd20b --- /dev/null +++ b/.github/actions/build/ci/action.yml @@ -0,0 +1,40 @@ +name: CI Build +description: 'Builds PowerShell' +runs: + using: composite + steps: + - name: Capture Environment + if: success() || failure() + run: |- + Import-Module .\tools\ci.psm1 + Show-Environment + shell: pwsh + - name: Set Build Name for Non-PR + if: github.event_name != 'PullRequest' + run: Write-Host "##vso[build.updatebuildnumber]$env:BUILD_SOURCEBRANCHNAME-$env:BUILD_SOURCEVERSION-$((get-date).ToString("yyyyMMddhhmmss"))" + shell: pwsh + - uses: actions/setup-dotnet@v4 + with: + global-json-file: ./global.json + - name: Bootstrap + if: success() + run: |- + Write-Verbose -Verbose "Running Bootstrap..." + Import-Module .\tools\ci.psm1 + Invoke-CIInstall -SkipUser + Write-Verbose -Verbose "Start Sync-PSTags" + Sync-PSTags -AddRemoteIfMissing + Write-Verbose -Verbose "End Sync-PSTags" + shell: pwsh + - name: Build + if: success() + run: |- + Write-Verbose -Verbose "Running Build..." + Import-Module .\tools\ci.psm1 + Invoke-CIBuild + shell: pwsh + - name: Upload build artifact + uses: actions/upload-artifact@v4 + with: + name: build + path: ${{ runner.workspace }}/build diff --git a/.github/actions/infrastructure/get-changed-files/README.md b/.github/actions/infrastructure/get-changed-files/README.md new file mode 100644 index 00000000000..277b28c0674 --- /dev/null +++ b/.github/actions/infrastructure/get-changed-files/README.md @@ -0,0 +1,122 @@ +# Get Changed Files Action + +A reusable composite action that retrieves the list of files changed in a pull request or push event. + +## Features + +- Supports both `pull_request` and `push` events +- Optional filtering by file pattern +- Returns files as JSON array for easy consumption +- Filters out deleted files (only returns added, modified, or renamed files) +- Handles up to 100 changed files per request + +## Usage + +### Basic Usage (Pull Requests Only) + +```yaml +- name: Get changed files + id: changed-files + uses: "./.github/actions/infrastructure/get-changed-files" + +- name: Process files + run: | + echo "Changed files: ${{ steps.changed-files.outputs.files }}" + echo "Count: ${{ steps.changed-files.outputs.count }}" +``` + +### With Filtering + +```yaml +# Get only markdown files +- name: Get changed markdown files + id: changed-md + uses: "./.github/actions/infrastructure/get-changed-files" + with: + filter: '*.md' + +# Get only GitHub workflow/action files +- name: Get changed GitHub files + id: changed-github + uses: "./.github/actions/infrastructure/get-changed-files" + with: + filter: '.github/' +``` + +### Support Both PR and Push Events + +```yaml +- name: Get changed files + id: changed-files + uses: "./.github/actions/infrastructure/get-changed-files" + with: + event-types: 'pull_request,push' +``` + +## Inputs + +| Name | Description | Required | Default | +|------|-------------|----------|---------| +| `filter` | Optional filter pattern (e.g., `*.md` for markdown files, `.github/` for GitHub files) | No | `''` | +| `event-types` | Comma-separated list of event types to support (`pull_request`, `push`) | No | `pull_request` | + +## Outputs + +| Name | Description | +|------|-------------| +| `files` | JSON array of changed file paths | +| `count` | Number of changed files | + +## Filter Patterns + +The action supports simple filter patterns: + +- **Extension matching**: Use `*.ext` to match files with a specific extension + - Example: `*.md` matches all markdown files + - Example: `*.yml` matches all YAML files + +- **Path prefix matching**: Use a path prefix to match files in a directory + - Example: `.github/` matches all files in the `.github` directory + - Example: `tools/` matches all files in the `tools` directory + +## Example: Processing Changed Files + +```yaml +- name: Get changed files + id: changed-files + uses: "./.github/actions/infrastructure/get-changed-files" + +- name: Process each file + shell: pwsh + env: + CHANGED_FILES: ${{ steps.changed-files.outputs.files }} + run: | + $changedFilesJson = $env:CHANGED_FILES + $changedFiles = $changedFilesJson | ConvertFrom-Json + + foreach ($file in $changedFiles) { + Write-Host "Processing: $file" + # Your processing logic here + } +``` + +## Limitations + +- Simple filter patterns only (no complex glob or regex patterns) + +## Pagination + +The action automatically handles pagination to fetch **all** changed files in a PR, regardless of how many files were changed: + +- Fetches files in batches of 100 per page +- Continues fetching until all files are retrieved +- Logs a note when pagination occurs, showing the total file count +- **No file limit** - all changed files will be processed, even in very large PRs + +This ensures that critical workflows (such as merge conflict checking, link validation, etc.) don't miss files due to pagination limits. + +## Related Actions + +- **markdownlinks**: Uses this pattern to get changed markdown files +- **merge-conflict-checker**: Uses this pattern to get changed files for conflict detection +- **path-filters**: Similar functionality but with more complex filtering logic diff --git a/.github/actions/infrastructure/get-changed-files/action.yml b/.github/actions/infrastructure/get-changed-files/action.yml new file mode 100644 index 00000000000..c897d4f388d --- /dev/null +++ b/.github/actions/infrastructure/get-changed-files/action.yml @@ -0,0 +1,117 @@ +name: 'Get Changed Files' +description: 'Gets the list of files changed in a pull request or push event' +inputs: + filter: + description: 'Optional filter pattern (e.g., "*.md" for markdown files, ".github/" for GitHub files)' + required: false + default: '' + event-types: + description: 'Comma-separated list of event types to support (pull_request, push)' + required: false + default: 'pull_request' +outputs: + files: + description: 'JSON array of changed file paths' + value: ${{ steps.get-files.outputs.files }} + count: + description: 'Number of changed files' + value: ${{ steps.get-files.outputs.count }} +runs: + using: 'composite' + steps: + - name: Get changed files + id: get-files + uses: actions/github-script@v7 + with: + script: | + const eventTypes = '${{ inputs.event-types }}'.split(',').map(t => t.trim()); + const filter = '${{ inputs.filter }}'; + let changedFiles = []; + + if (eventTypes.includes('pull_request') && context.eventName === 'pull_request') { + console.log(`Getting files changed in PR #${context.payload.pull_request.number}`); + + // Fetch all files changed in the PR with pagination + let allFiles = []; + let page = 1; + let fetchedCount; + + do { + const { data: files } = await github.rest.pulls.listFiles({ + owner: context.repo.owner, + repo: context.repo.repo, + pull_number: context.payload.pull_request.number, + per_page: 100, + page: page + }); + + allFiles = allFiles.concat(files); + fetchedCount = files.length; + page++; + } while (fetchedCount === 100); + + if (allFiles.length >= 100) { + console.log(`Note: This PR has ${allFiles.length} changed files. All files fetched using pagination.`); + } + + changedFiles = allFiles + .filter(file => file.status === 'added' || file.status === 'modified' || file.status === 'renamed') + .map(file => file.filename); + + } else if (eventTypes.includes('push') && context.eventName === 'push') { + console.log(`Getting files changed in push to ${context.ref}`); + + const { data: comparison } = await github.rest.repos.compareCommits({ + owner: context.repo.owner, + repo: context.repo.repo, + base: context.payload.before, + head: context.payload.after, + }); + + changedFiles = comparison.files + .filter(file => file.status === 'added' || file.status === 'modified' || file.status === 'renamed') + .map(file => file.filename); + + } else { + core.setFailed(`Unsupported event type: ${context.eventName}. Supported types: ${eventTypes.join(', ')}`); + return; + } + + // Apply filter if provided + if (filter) { + const filterLower = filter.toLowerCase(); + const beforeFilter = changedFiles.length; + changedFiles = changedFiles.filter(file => { + const fileLower = file.toLowerCase(); + // Support simple patterns like "*.md" or ".github/" + if (filterLower.startsWith('*.')) { + const ext = filterLower.substring(1); + return fileLower.endsWith(ext); + } else { + return fileLower.startsWith(filterLower); + } + }); + console.log(`Filter '${filter}' applied: ${beforeFilter} → ${changedFiles.length} files`); + } + + // Calculate simple hash for verification + const crypto = require('crypto'); + const filesJson = JSON.stringify(changedFiles.sort()); + const hash = crypto.createHash('sha256').update(filesJson).digest('hex').substring(0, 8); + + // Log changed files in a collapsible group + core.startGroup(`Changed Files (${changedFiles.length} total, hash: ${hash})`); + if (changedFiles.length > 0) { + changedFiles.forEach(file => console.log(` - ${file}`)); + } else { + console.log(' (no files changed)'); + } + core.endGroup(); + + console.log(`Found ${changedFiles.length} changed files`); + core.setOutput('files', JSON.stringify(changedFiles)); + core.setOutput('count', changedFiles.length); + +branding: + icon: 'file-text' + color: 'blue' diff --git a/.github/actions/infrastructure/markdownlinks/Parse-MarkdownLink.ps1 b/.github/actions/infrastructure/markdownlinks/Parse-MarkdownLink.ps1 new file mode 100644 index 00000000000..a56d696eb6e --- /dev/null +++ b/.github/actions/infrastructure/markdownlinks/Parse-MarkdownLink.ps1 @@ -0,0 +1,182 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +#requires -version 7 +# Markdig is always available in PowerShell 7 +<# +.SYNOPSIS + Parse CHANGELOG files using Markdig to extract links. + +.DESCRIPTION + This script uses Markdig.Markdown.Parse to parse all markdown files in the CHANGELOG directory + and extract different types of links (inline links, reference links, etc.). + +.PARAMETER ChangelogPath + Path to the CHANGELOG directory. Defaults to ./CHANGELOG + +.PARAMETER LinkType + Filter by link type: All, Inline, Reference, AutoLink. Defaults to All. + +.EXAMPLE + .\Parse-MarkdownLink.ps1 + +.EXAMPLE + .\Parse-MarkdownLink.ps1 -LinkType Reference +#> + +param( + [string]$ChangelogPath = "./CHANGELOG", + [ValidateSet("All", "Inline", "Reference", "AutoLink")] + [string]$LinkType = "All" +) + +Write-Verbose "Using built-in Markdig functionality to parse markdown files" + +function Get-LinksFromMarkdownAst { + param( + [Parameter(Mandatory)] + [object]$Node, + [Parameter(Mandatory)] + [string]$FileName, + [System.Collections.ArrayList]$Links + ) + + if ($null -eq $Links) { + return + } + + # Check if current node is a link + if ($Node -is [Markdig.Syntax.Inlines.LinkInline]) { + $linkInfo = [PSCustomObject]@{ + Path = $FileName + Line = $Node.Line + 1 # Convert to 1-based line numbering + Column = $Node.Column + 1 # Convert to 1-based column numbering + Url = $Node.Url ?? "" + Text = $Node.FirstChild?.ToString() ?? "" + Type = "Inline" + IsImage = $Node.IsImage + } + [void]$Links.Add($linkInfo) + } + elseif ($Node -is [Markdig.Syntax.Inlines.AutolinkInline]) { + $linkInfo = [PSCustomObject]@{ + Path = $FileName + Line = $Node.Line + 1 + Column = $Node.Column + 1 + Url = $Node.Url ?? "" + Text = $Node.Url ?? "" + Type = "AutoLink" + IsImage = $false + } + [void]$Links.Add($linkInfo) + } + elseif ($Node -is [Markdig.Syntax.LinkReferenceDefinitionGroup]) { + foreach ($refDef in $Node) { + $linkInfo = [PSCustomObject]@{ + Path = $FileName + Line = $refDef.Line + 1 + Column = $refDef.Column + 1 + Url = $refDef.Url ?? "" + Text = $refDef.Label ?? "" + Type = "Reference" + IsImage = $false + } + [void]$Links.Add($linkInfo) + } + } + elseif ($Node -is [Markdig.Syntax.LinkReferenceDefinition]) { + $linkInfo = [PSCustomObject]@{ + Path = $FileName + Line = $Node.Line + 1 + Column = $Node.Column + 1 + Url = $Node.Url ?? "" + Text = $Node.Label ?? "" + Type = "Reference" + IsImage = $false + } + [void]$Links.Add($linkInfo) + } + + # For MarkdownDocument (root), iterate through all blocks + if ($Node -is [Markdig.Syntax.MarkdownDocument]) { + foreach ($block in $Node) { + Get-LinksFromMarkdownAst -Node $block -FileName $FileName -Links $Links + } + } + # For block containers, iterate through children + elseif ($Node -is [Markdig.Syntax.ContainerBlock]) { + foreach ($child in $Node) { + Get-LinksFromMarkdownAst -Node $child -FileName $FileName -Links $Links + } + } + # For leaf blocks with inlines, process the inline content + elseif ($Node -is [Markdig.Syntax.LeafBlock] -and $Node.Inline) { + Get-LinksFromMarkdownAst -Node $Node.Inline -FileName $FileName -Links $Links + } + # For inline containers, process all child inlines + elseif ($Node -is [Markdig.Syntax.Inlines.ContainerInline]) { + $child = $Node.FirstChild + while ($child) { + Get-LinksFromMarkdownAst -Node $child -FileName $FileName -Links $Links + $child = $child.NextSibling + } + } + # For other inline elements that might have children + elseif ($Node.PSObject.Properties.Name -contains "FirstChild" -and $Node.FirstChild) { + $child = $Node.FirstChild + while ($child) { + Get-LinksFromMarkdownAst -Node $child -FileName $FileName -Links $Links + $child = $child.NextSibling + } + } +} + +function Parse-ChangelogFiles { + param( + [string]$Path + ) + + if (-not (Test-Path $Path)) { + Write-Error "CHANGELOG directory not found: $Path" + return + } + + $markdownFiles = Get-ChildItem -Path $Path -Filter "*.md" -File + + if ($markdownFiles.Count -eq 0) { + Write-Warning "No markdown files found in $Path" + return + } + + $allLinks = [System.Collections.ArrayList]::new() + + foreach ($file in $markdownFiles) { + Write-Verbose "Processing file: $($file.Name)" + + try { + $content = Get-Content -Path $file.FullName -Raw -Encoding UTF8 + + # Parse the markdown content using Markdig + $document = [Markdig.Markdown]::Parse($content, [Markdig.MarkdownPipelineBuilder]::new()) + + # Extract links from the AST + Get-LinksFromMarkdownAst -Node $document -FileName $file.FullName -Links $allLinks + + } catch { + Write-Warning "Error processing file $($file.Name): $($_.Exception.Message)" + } + } + + # Filter by link type if specified + if ($LinkType -ne "All") { + $allLinks = $allLinks | Where-Object { $_.Type -eq $LinkType } + } + + return $allLinks +} + +# Main execution +$links = Parse-ChangelogFiles -Path $ChangelogPath + +# Output PowerShell objects +$links diff --git a/.github/actions/infrastructure/markdownlinks/README.md b/.github/actions/infrastructure/markdownlinks/README.md new file mode 100644 index 00000000000..e566ec2bcc3 --- /dev/null +++ b/.github/actions/infrastructure/markdownlinks/README.md @@ -0,0 +1,177 @@ +# Verify Markdown Links Action + +A GitHub composite action that verifies all links in markdown files using PowerShell and Markdig. + +## Features + +- ✅ Parses markdown files using Markdig (built into PowerShell 7) +- ✅ Extracts all link types: inline links, reference links, and autolinks +- ✅ Verifies HTTP/HTTPS links with configurable timeouts and retries +- ✅ Validates local file references +- ✅ Supports excluding specific URL patterns +- ✅ Provides detailed error reporting with file locations +- ✅ Outputs metrics for CI/CD integration + +## Usage + +### Basic Usage + +```yaml +- name: Verify Markdown Links + uses: ./.github/actions/infrastructure/markdownlinks + with: + path: './CHANGELOG' +``` + +### Advanced Usage + +```yaml +- name: Verify Markdown Links + uses: ./.github/actions/infrastructure/markdownlinks + with: + path: './docs' + fail-on-error: 'true' + timeout: 30 + max-retries: 2 + exclude-patterns: '*.example.com/*,*://localhost/*' +``` + +### With Outputs + +```yaml +- name: Verify Markdown Links + id: verify-links + uses: ./.github/actions/infrastructure/markdownlinks + with: + path: './CHANGELOG' + fail-on-error: 'false' + +- name: Display Results + run: | + echo "Total links: ${{ steps.verify-links.outputs.total-links }}" + echo "Passed: ${{ steps.verify-links.outputs.passed-links }}" + echo "Failed: ${{ steps.verify-links.outputs.failed-links }}" + echo "Skipped: ${{ steps.verify-links.outputs.skipped-links }}" +``` + +## Inputs + +| Input | Description | Required | Default | +|-------|-------------|----------|---------| +| `path` | Path to the directory containing markdown files to verify | No | `./CHANGELOG` | +| `exclude-patterns` | Comma-separated list of URL patterns to exclude from verification | No | `''` | +| `fail-on-error` | Whether to fail the action if any links are broken | No | `true` | +| `timeout` | Timeout in seconds for HTTP requests | No | `30` | +| `max-retries` | Maximum number of retries for failed requests | No | `2` | + +## Outputs + +| Output | Description | +|--------|-------------| +| `total-links` | Total number of unique links checked | +| `passed-links` | Number of links that passed verification | +| `failed-links` | Number of links that failed verification | +| `skipped-links` | Number of links that were skipped | + +## Excluded Link Types + +The action automatically skips the following link types: + +- **Anchor links** (`#section-name`) - Would require full markdown parsing +- **Email links** (`mailto:user@example.com`) - Cannot be verified without sending email + +## GitHub Workflow Test + +This section provides a workflow example and instructions for testing the link verification action. + +### Testing the Workflow + +To test that the workflow properly detects broken links: + +1. Make change to this file (e.g., this README.md file already contains one in the [Broken Link Test](#broken-link-test) section) +1. The workflow will run and should fail, reporting the broken link(s) +1. Revert your change to this file +1. Push again to verify the workflow passes + +### Example Workflow Configuration + +```yaml +name: Verify Links + +on: + push: + branches: [ main ] + paths: + - '**/*.md' + pull_request: + branches: [ main ] + paths: + - '**/*.md' + schedule: + # Run weekly to catch external link rot + - cron: '0 0 * * 0' + +jobs: + verify-links: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Verify CHANGELOG Links + uses: ./.github/actions/infrastructure/markdownlinks + with: + path: './CHANGELOG' + fail-on-error: 'true' + + - name: Verify Documentation Links + uses: ./.github/actions/infrastructure/markdownlinks + with: + path: './docs' + fail-on-error: 'false' + exclude-patterns: '*.internal.example.com/*' +``` + +## How It Works + +1. **Parse Markdown**: Uses `Parse-MarkdownLink.ps1` to extract all links from markdown files using Markdig +2. **Deduplicate**: Groups links by URL to avoid checking the same link multiple times +3. **Verify Links**: + - HTTP/HTTPS links: Makes HEAD/GET requests with configurable timeout and retries + - Local file references: Checks if the file exists relative to the markdown file + - Excluded patterns: Skips links matching the exclude patterns +4. **Report Results**: Displays detailed results with file locations for failed links +5. **Set Outputs**: Provides metrics for downstream steps + +## Error Output Example + +``` +✗ FAILED: https://example.com/broken-link - HTTP 404 + Found in: /path/to/file.md:42:15 + Found in: /path/to/other.md:100:20 + +Link Verification Summary +============================================================ +Total URLs checked: 150 +Passed: 145 +Failed: 2 +Skipped: 3 + +Failed Links: + • https://example.com/broken-link + Error: HTTP 404 + Occurrences: 2 +``` + +## Requirements + +- PowerShell 7+ (includes Markdig) +- Runs on: `ubuntu-latest`, `windows-latest`, `macos-latest` + +## Broken Link Test + +- [Broken Link](https://github.com/PowerShell/PowerShell/wiki/NonExistentPage404) + +## License + +Same as the PowerShell repository. diff --git a/.github/actions/infrastructure/markdownlinks/Verify-MarkdownLinks.ps1 b/.github/actions/infrastructure/markdownlinks/Verify-MarkdownLinks.ps1 new file mode 100644 index 00000000000..f50ab1590b9 --- /dev/null +++ b/.github/actions/infrastructure/markdownlinks/Verify-MarkdownLinks.ps1 @@ -0,0 +1,317 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +#Requires -Version 7.0 + +<# +.SYNOPSIS + Verify all links in markdown files. + +.DESCRIPTION + This script parses markdown files to extract links and verifies their accessibility. + It supports HTTP/HTTPS links and local file references. + +.PARAMETER Path + Path to the directory containing markdown files. Defaults to current directory. + +.PARAMETER File + Array of specific markdown files to verify. If provided, Path parameter is ignored. + +.PARAMETER TimeoutSec + Timeout in seconds for HTTP requests. Defaults to 30. + +.PARAMETER MaximumRetryCount + Maximum number of retries for failed requests. Defaults to 2. + +.PARAMETER RetryIntervalSec + Interval in seconds between retry attempts. Defaults to 2. + +.EXAMPLE + .\Verify-MarkdownLinks.ps1 -Path ./CHANGELOG + +.EXAMPLE + .\Verify-MarkdownLinks.ps1 -Path ./docs -FailOnError + +.EXAMPLE + .\Verify-MarkdownLinks.ps1 -File @('CHANGELOG/7.5.md', 'README.md') +#> + +param( + [Parameter(ParameterSetName = 'ByPath', Mandatory)] + [string]$Path = "Q:\src\git\powershell\docs\git", + [Parameter(ParameterSetName = 'ByFile', Mandatory)] + [string[]]$File = @(), + [int]$TimeoutSec = 30, + [int]$MaximumRetryCount = 2, + [int]$RetryIntervalSec = 2 +) + +$ErrorActionPreference = 'Stop' + +# Get the script directory +$scriptDir = Split-Path -Parent $MyInvocation.MyCommand.Path + +# Determine what to process: specific files or directory +if ($File.Count -gt 0) { + Write-Host "Extracting links from $($File.Count) specified markdown file(s)" -ForegroundColor Cyan + + # Process each file individually + $allLinks = @() + $parseScriptPath = Join-Path $scriptDir "Parse-MarkdownLink.ps1" + + foreach ($filePath in $File) { + if (Test-Path $filePath) { + Write-Verbose "Processing: $filePath" + $fileLinks = & $parseScriptPath -ChangelogPath $filePath + $allLinks += $fileLinks + } + else { + Write-Warning "File not found: $filePath" + } + } +} +else { + Write-Host "Extracting links from markdown files in: $Path" -ForegroundColor Cyan + + # Get all links from markdown files using the Parse-ChangelogLinks script + $parseScriptPath = Join-Path $scriptDir "Parse-MarkdownLink.ps1" + $allLinks = & $parseScriptPath -ChangelogPath $Path +} + +if ($allLinks.Count -eq 0) { + Write-Host "No links found in markdown files." -ForegroundColor Yellow + exit 0 +} + +Write-Host "Found $($allLinks.Count) links to verify" -ForegroundColor Green + +# Group links by URL to avoid duplicate checks +$uniqueLinks = $allLinks | Group-Object -Property Url + +Write-Host "Unique URLs to verify: $($uniqueLinks.Count)" -ForegroundColor Cyan + +$results = @{ + Total = $uniqueLinks.Count + Passed = 0 + Failed = 0 + Skipped = 0 + Errors = [System.Collections.ArrayList]::new() +} + +function Test-HttpLink { + param( + [string]$Url + ) + + try { + # Try HEAD request first (faster, doesn't download content) + $response = Invoke-WebRequest -Uri $Url ` + -Method Head ` + -TimeoutSec $TimeoutSec ` + -MaximumRetryCount $MaximumRetryCount ` + -RetryIntervalSec $RetryIntervalSec ` + -UserAgent "Mozilla/5.0 (compatible; GitHubActions/1.0; +https://github.com/PowerShell/PowerShell)" ` + -SkipHttpErrorCheck + + # If HEAD fails with 404 or 405, retry with GET (some servers don't support HEAD) + if ($response.StatusCode -eq 404 -or $response.StatusCode -eq 405) { + Write-Verbose "HEAD request failed with $($response.StatusCode), retrying with GET for: $Url" + $response = Invoke-WebRequest -Uri $Url ` + -Method Get ` + -TimeoutSec $TimeoutSec ` + -MaximumRetryCount $MaximumRetryCount ` + -RetryIntervalSec $RetryIntervalSec ` + -UserAgent "Mozilla/5.0 (compatible; GitHubActions/1.0; +https://github.com)" ` + -SkipHttpErrorCheck + } + + if ($response.StatusCode -ge 200 -and $response.StatusCode -lt 400) { + return @{ Success = $true; StatusCode = $response.StatusCode } + } + else { + return @{ Success = $false; StatusCode = $response.StatusCode; Error = "HTTP $($response.StatusCode)" } + } + } + catch { + return @{ Success = $false; StatusCode = 0; Error = $_.Exception.Message } + } +} + +function Test-LocalLink { + param( + [string]$Url, + [string]$BasePath + ) + + # Strip query parameters (e.g., ?sanitize=true) and anchors (e.g., #section) + $cleanUrl = $Url -replace '\?.*$', '' -replace '#.*$', '' + + # Handle relative paths + $targetPath = Join-Path $BasePath $cleanUrl + + if (Test-Path $targetPath) { + return @{ Success = $true } + } + else { + return @{ Success = $false; Error = "File not found: $targetPath" } + } +} + +# Verify each unique link +$progressCount = 0 +foreach ($linkGroup in $uniqueLinks) { + $progressCount++ + $url = $linkGroup.Name + $occurrences = $linkGroup.Group + Write-Verbose -Verbose "[$progressCount/$($uniqueLinks.Count)] Checking: $url" + + # Determine link type and verify + $verifyResult = $null + if ($url -match '^https?://') { + $verifyResult = Test-HttpLink -Url $url + } + elseif ($url -match '^#') { + Write-Verbose -Verbose "Skipping anchor link: $url" + $results.Skipped++ + continue + } + elseif ($url -match '^mailto:') { + Write-Verbose -Verbose "Skipping mailto link: $url" + $results.Skipped++ + continue + } + else { + $basePath = Split-Path -Parent $occurrences[0].Path + $verifyResult = Test-LocalLink -Url $url -BasePath $basePath + } + if ($verifyResult.Success) { + Write-Host "✓ OK: $url" -ForegroundColor Green + $results.Passed++ + } + else { + $errorMsg = if ($verifyResult.StatusCode) { + "HTTP $($verifyResult.StatusCode)" + } + else { + $verifyResult.Error + } + + # Determine if this status code should be ignored or treated as failure + # Ignore: 401 (Unauthorized), 403 (Forbidden), 429 (Too Many Requests - already retried) + # Fail: 404 (Not Found), 410 (Gone), 406 (Not Acceptable) - these indicate broken links + $shouldIgnore = $false + $ignoreReason = "" + + switch ($verifyResult.StatusCode) { + 401 { + $shouldIgnore = $true + $ignoreReason = "authentication required" + } + 403 { + $shouldIgnore = $true + $ignoreReason = "access forbidden" + } + 429 { + $shouldIgnore = $true + $ignoreReason = "rate limited (already retried)" + } + } + + if ($shouldIgnore) { + Write-Host "⊘ IGNORED: $url - $errorMsg ($ignoreReason)" -ForegroundColor Yellow + Write-Verbose -Verbose "Ignored error details for $url - Status: $($verifyResult.StatusCode) - $ignoreReason" + foreach ($occurrence in $occurrences) { + Write-Verbose -Verbose " Found in: $($occurrence.Path):$($occurrence.Line):$($occurrence.Column)" + } + $results.Skipped++ + } + else { + Write-Host "✗ FAILED: $url - $errorMsg" -ForegroundColor Red + foreach ($occurrence in $occurrences) { + Write-Host " Found in: $($occurrence.Path):$($occurrence.Line):$($occurrence.Column)" -ForegroundColor DarkGray + } + $results.Failed++ + [void]$results.Errors.Add(@{ + Url = $url + Error = $errorMsg + Occurrences = $occurrences + }) + } + } + } + +# Print summary +Write-Host "`n" + ("=" * 60) -ForegroundColor Cyan +Write-Host "Link Verification Summary" -ForegroundColor Cyan +Write-Host ("=" * 60) -ForegroundColor Cyan +Write-Host "Total URLs checked: $($results.Total)" -ForegroundColor White +Write-Host "Passed: $($results.Passed)" -ForegroundColor Green +Write-Host "Failed: $($results.Failed)" -ForegroundColor $(if ($results.Failed -gt 0) { "Red" } else { "Green" }) +Write-Host "Skipped: $($results.Skipped)" -ForegroundColor Gray + +if ($results.Failed -gt 0) { + Write-Host "`nFailed Links:" -ForegroundColor Red + foreach ($failedLink in $results.Errors) { + Write-Host " • $($failedLink.Url)" -ForegroundColor Red + Write-Host " Error: $($failedLink.Error)" -ForegroundColor DarkGray + Write-Host " Occurrences: $($failedLink.Occurrences.Count)" -ForegroundColor DarkGray + } + + Write-Host "`n❌ Link verification failed!" -ForegroundColor Red + exit 1 +} +else { + Write-Host "`n✅ All links verified successfully!" -ForegroundColor Green +} + +# Write to GitHub Actions step summary if running in a workflow +if ($env:GITHUB_STEP_SUMMARY) { + $summaryContent = @" + +# Markdown Link Verification Results + +## Summary +- **Total URLs checked:** $($results.Total) +- **Passed:** ✅ $($results.Passed) +- **Failed:** $(if ($results.Failed -gt 0) { "❌" } else { "✅" }) $($results.Failed) +- **Skipped:** $($results.Skipped) + +"@ + + if ($results.Failed -gt 0) { + $summaryContent += @" + +## Failed Links + +| URL | Error | Occurrences | +|-----|-------|-------------| + +"@ + foreach ($failedLink in $results.Errors) { + $summaryContent += "| $($failedLink.Url) | $($failedLink.Error) | $($failedLink.Occurrences.Count) |`n" + } + + $summaryContent += @" + +
+Click to see all failed link locations + +"@ + foreach ($failedLink in $results.Errors) { + $summaryContent += "`n### $($failedLink.Url)`n" + $summaryContent += "**Error:** $($failedLink.Error)`n`n" + foreach ($occurrence in $failedLink.Occurrences) { + $summaryContent += "- `$($occurrence.Path):$($occurrence.Line):$($occurrence.Column)`n" + } + } + $summaryContent += "`n
`n" + } + else { + $summaryContent += "`n## ✅ All links verified successfully!`n" + } + + Write-Verbose -Verbose "Writing `n $summaryContent `n to ${env:GITHUB_STEP_SUMMARY}" + $summaryContent | Out-File -FilePath $env:GITHUB_STEP_SUMMARY -Append + Write-Verbose -Verbose "Summary written to GitHub Actions step summary" +} + diff --git a/.github/actions/infrastructure/markdownlinks/action.yml b/.github/actions/infrastructure/markdownlinks/action.yml new file mode 100644 index 00000000000..de2952252d4 --- /dev/null +++ b/.github/actions/infrastructure/markdownlinks/action.yml @@ -0,0 +1,110 @@ +name: 'Verify Markdown Links' +description: 'Verify all links in markdown files using PowerShell and Markdig' +author: 'PowerShell Team' + +inputs: + timeout-sec: + description: 'Timeout in seconds for HTTP requests' + required: false + default: '30' + maximum-retry-count: + description: 'Maximum number of retries for failed requests' + required: false + default: '2' + +outputs: + total-links: + description: 'Total number of unique links checked' + value: ${{ steps.verify.outputs.total }} + passed-links: + description: 'Number of links that passed verification' + value: ${{ steps.verify.outputs.passed }} + failed-links: + description: 'Number of links that failed verification' + value: ${{ steps.verify.outputs.failed }} + skipped-links: + description: 'Number of links that were skipped' + value: ${{ steps.verify.outputs.skipped }} + +runs: + using: 'composite' + steps: + - name: Get changed markdown files + id: changed-files + uses: "./.github/actions/infrastructure/get-changed-files" + with: + filter: '*.md' + event-types: 'pull_request,push' + + - name: Verify markdown links + id: verify + shell: pwsh + env: + CHANGED_FILES_JSON: ${{ steps.changed-files.outputs.files }} + run: | + Write-Host "Starting markdown link verification..." -ForegroundColor Cyan + + # Get changed markdown files from environment variable (secure against injection) + $changedFilesJson = $env:CHANGED_FILES_JSON + $changedFiles = $changedFilesJson | ConvertFrom-Json + + if ($changedFiles.Count -eq 0) { + Write-Host "No markdown files changed, skipping verification" -ForegroundColor Yellow + "total=0" >> $env:GITHUB_OUTPUT + "passed=0" >> $env:GITHUB_OUTPUT + "failed=0" >> $env:GITHUB_OUTPUT + "skipped=0" >> $env:GITHUB_OUTPUT + exit 0 + } + + Write-Host "Changed markdown files: $($changedFiles.Count)" -ForegroundColor Cyan + $changedFiles | ForEach-Object { Write-Host " - $_" -ForegroundColor Gray } + + # Build parameters for each file + $params = @{ + File = $changedFiles + TimeoutSec = [int]'${{ inputs.timeout-sec }}' + MaximumRetryCount = [int]'${{ inputs.maximum-retry-count }}' + } + + # Run the verification script + $scriptPath = Join-Path '${{ github.action_path }}' 'Verify-MarkdownLinks.ps1' + + # Capture output and parse results + $output = & $scriptPath @params 2>&1 | Tee-Object -Variable capturedOutput + + # Try to extract metrics from output + $totalLinks = 0 + $passedLinks = 0 + $failedLinks = 0 + $skippedLinks = 0 + + foreach ($line in $capturedOutput) { + if ($line -match 'Total URLs checked: (\d+)') { + $totalLinks = $Matches[1] + } + elseif ($line -match 'Passed: (\d+)') { + $passedLinks = $Matches[1] + } + elseif ($line -match 'Failed: (\d+)') { + $failedLinks = $Matches[1] + } + elseif ($line -match 'Skipped: (\d+)') { + $skippedLinks = $Matches[1] + } + } + + # Set outputs + "total=$totalLinks" >> $env:GITHUB_OUTPUT + "passed=$passedLinks" >> $env:GITHUB_OUTPUT + "failed=$failedLinks" >> $env:GITHUB_OUTPUT + "skipped=$skippedLinks" >> $env:GITHUB_OUTPUT + + Write-Host "Action completed" -ForegroundColor Cyan + + # Exit with the same code as the verification script + exit $LASTEXITCODE + +branding: + icon: 'link' + color: 'blue' diff --git a/.github/actions/infrastructure/merge-conflict-checker/README.md b/.github/actions/infrastructure/merge-conflict-checker/README.md new file mode 100644 index 00000000000..b53d6f99964 --- /dev/null +++ b/.github/actions/infrastructure/merge-conflict-checker/README.md @@ -0,0 +1,86 @@ +# Merge Conflict Checker + +This composite GitHub Action checks for Git merge conflict markers in files changed in pull requests. + +## Purpose + +Automatically detects leftover merge conflict markers (`<<<<<<<`, `=======`, `>>>>>>>`) in pull request files to prevent them from being merged into the codebase. + +## Usage + +### In a Workflow + +```yaml +- name: Check for merge conflict markers + uses: "./.github/actions/infrastructure/merge-conflict-checker" +``` + +### Complete Example + +```yaml +jobs: + merge_conflict_check: + name: Check for Merge Conflict Markers + runs-on: ubuntu-latest + if: github.event_name == 'pull_request' + permissions: + pull-requests: read + contents: read + steps: + - name: checkout + uses: actions/checkout@v5 + + - name: Check for merge conflict markers + uses: "./.github/actions/infrastructure/merge-conflict-checker" +``` + +## How It Works + +1. **File Detection**: Uses GitHub's API to get the list of files changed in the pull request +2. **Marker Scanning**: Reads each changed file and searches for the following markers: + - `<<<<<<<` (conflict start marker) + - `=======` (conflict separator) + - `>>>>>>>` (conflict end marker) +3. **Result Reporting**: + - If markers are found, the action fails and lists all affected files + - If no markers are found, the action succeeds + +## Outputs + +- `files-checked`: Number of files that were checked +- `conflicts-found`: Number of files containing merge conflict markers + +## Behavior + +- **Event Support**: Only works with `pull_request` events +- **File Handling**: + - Checks only files that were added, modified, or renamed + - Skips deleted files + - **Filters out `*.cs` files** (C# files are excluded from merge conflict checking) + - Skips binary/unreadable files + - Skips directories +- **Empty File List**: Gracefully handles cases where no files need checking (e.g., PRs that only delete files) + +## Example Output + +When conflict markers are detected: + +``` +❌ Merge conflict markers detected in the following files: + - src/example.cs + Markers found: <<<<<<<, =======, >>>>>>> + - README.md + Markers found: <<<<<<<, =======, >>>>>>> + +Please resolve these conflicts before merging. +``` + +When no markers are found: + +``` +✅ No merge conflict markers found +``` + +## Integration + +This action is integrated into the `linux-ci.yml` workflow and runs automatically on all pull requests to ensure code quality before merging. diff --git a/.github/actions/infrastructure/merge-conflict-checker/action.yml b/.github/actions/infrastructure/merge-conflict-checker/action.yml new file mode 100644 index 00000000000..41c7d2ad941 --- /dev/null +++ b/.github/actions/infrastructure/merge-conflict-checker/action.yml @@ -0,0 +1,37 @@ +name: 'Check for Merge Conflict Markers' +description: 'Checks for Git merge conflict markers in changed files for pull requests' +author: 'PowerShell Team' + +outputs: + files-checked: + description: 'Number of files checked for merge conflict markers' + value: ${{ steps.check.outputs.files-checked }} + conflicts-found: + description: 'Number of files with merge conflict markers' + value: ${{ steps.check.outputs.conflicts-found }} + +runs: + using: 'composite' + steps: + - name: Get changed files + id: changed-files + uses: "./.github/actions/infrastructure/get-changed-files" + + - name: Check for merge conflict markers + id: check + shell: pwsh + env: + CHANGED_FILES_JSON: ${{ steps.changed-files.outputs.files }} + run: | + # Get changed files from environment variable (secure against injection) + $changedFilesJson = $env:CHANGED_FILES_JSON + # Ensure we always have an array (ConvertFrom-Json returns null for empty JSON arrays) + $changedFiles = @($changedFilesJson | ConvertFrom-Json) + + # Import ci.psm1 and run the check + Import-Module "$env:GITHUB_WORKSPACE/tools/ci.psm1" -Force + Test-MergeConflictMarker -File $changedFiles -WorkspacePath $env:GITHUB_WORKSPACE + +branding: + icon: 'alert-triangle' + color: 'red' diff --git a/.github/actions/infrastructure/path-filters/action.yml b/.github/actions/infrastructure/path-filters/action.yml new file mode 100644 index 00000000000..656719262b2 --- /dev/null +++ b/.github/actions/infrastructure/path-filters/action.yml @@ -0,0 +1,137 @@ +name: Path Filters +description: 'Path Filters' +inputs: + GITHUB_TOKEN: + description: 'GitHub token' + required: true +outputs: + source: + description: 'Source code changes (composite of all changes)' + value: ${{ steps.filter.outputs.source }} + githubChanged: + description: 'GitHub workflow changes' + value: ${{ steps.filter.outputs.githubChanged }} + toolsChanged: + description: 'Tools changes' + value: ${{ steps.filter.outputs.toolsChanged }} + propsChanged: + description: 'Props changes' + value: ${{ steps.filter.outputs.propsChanged }} + testsChanged: + description: 'Tests changes' + value: ${{ steps.filter.outputs.testsChanged }} + mainSourceChanged: + description: 'Main source code changes (any changes in src/)' + value: ${{ steps.filter.outputs.mainSourceChanged }} + buildModuleChanged: + description: 'Build module changes' + value: ${{ steps.filter.outputs.buildModuleChanged }} + packagingChanged: + description: 'Packaging related changes' + value: ${{ steps.filter.outputs.packagingChanged }} +runs: + using: composite + steps: + - name: Get changed files + id: get-files + if: github.event_name == 'pull_request' + uses: "./.github/actions/infrastructure/get-changed-files" + + - name: Check if GitHubWorkflowChanges is present + id: filter + uses: actions/github-script@v7.0.1 + env: + FILES_JSON: ${{ steps.get-files.outputs.files }} + with: + github-token: ${{ inputs.GITHUB_TOKEN }} + script: | + console.log(`Event Name: ${context.eventName}`); + + // Just say everything changed if this is not a PR + if (context.eventName !== 'pull_request') { + console.log('Not a pull request, setting all outputs to true'); + core.setOutput('toolsChanged', true); + core.setOutput('githubChanged', true); + core.setOutput('propsChanged', true); + core.setOutput('testsChanged', true); + core.setOutput('mainSourceChanged', true); + core.setOutput('buildModuleChanged', true); + core.setOutput('source', true); + return; + } + + // Get files from environment variable (secure against injection) + const files = JSON.parse(process.env.FILES_JSON || '[]'); + + // Calculate hash for verification (matches get-changed-files action) + const crypto = require('crypto'); + const filesJson = JSON.stringify(files.sort()); + const hash = crypto.createHash('sha256').update(filesJson).digest('hex').substring(0, 8); + console.log(`Received ${files.length} files (hash: ${hash})`); + + // Analyze changes with detailed logging + core.startGroup('Path Filter Analysis'); + + const actionsChanged = files.some(file => file.startsWith('.github/actions')); + console.log(`✓ Actions changed: ${actionsChanged}`); + + const workflowsChanged = files.some(file => file.startsWith('.github/workflows')); + console.log(`✓ Workflows changed: ${workflowsChanged}`); + + const githubChanged = actionsChanged || workflowsChanged; + console.log(`→ GitHub changed (actions OR workflows): ${githubChanged}`); + + const toolsCiPsm1Changed = files.some(file => file === 'tools/ci.psm1'); + console.log(`✓ tools/ci.psm1 changed: ${toolsCiPsm1Changed}`); + + const toolsBuildCommonChanged = files.some(file => file.startsWith('tools/buildCommon/')); + console.log(`✓ tools/buildCommon/ changed: ${toolsBuildCommonChanged}`); + + const toolsChanged = toolsCiPsm1Changed || toolsBuildCommonChanged; + console.log(`→ Tools changed: ${toolsChanged}`); + + const propsChanged = files.some(file => file.endsWith('.props')); + console.log(`✓ Props files changed: ${propsChanged}`); + + const testsChanged = files.some(file => file.startsWith('test/powershell/') || file.startsWith('test/tools/') || file.startsWith('test/xUnit/')); + console.log(`✓ Tests changed: ${testsChanged}`); + + const mainSourceChanged = files.some(file => file.startsWith('src/')); + console.log(`✓ Main source (src/) changed: ${mainSourceChanged}`); + + const buildModuleChanged = files.some(file => file === 'build.psm1'); + console.log(`✓ build.psm1 changed: ${buildModuleChanged}`); + + const globalConfigChanged = files.some(file => file === '.globalconfig' || file === 'nuget.config' || file === 'global.json'); + console.log(`✓ Global config changed: ${globalConfigChanged}`); + + const packagingChanged = files.some(file => + file === '.github/workflows/windows-ci.yml' || + file === '.github/workflows/linux-ci.yml' || + file.startsWith('assets/wix/') || + file === 'PowerShell.Common.props' || + file.match(/^src\/.*\.csproj$/) || + file.startsWith('test/packaging/windows/') || + file.startsWith('test/packaging/linux/') || + file.startsWith('tools/packaging/') || + file.startsWith('tools/wix/') + ) || + buildModuleChanged || + globalConfigChanged || + toolsCiPsm1Changed; + console.log(`→ Packaging changed: ${packagingChanged}`); + + const source = mainSourceChanged || toolsChanged || githubChanged || propsChanged || testsChanged || globalConfigChanged; + console.log(`→ Source (composite): ${source}`); + + core.endGroup(); + + core.setOutput('toolsChanged', toolsChanged); + core.setOutput('githubChanged', githubChanged); + core.setOutput('propsChanged', propsChanged); + core.setOutput('testsChanged', testsChanged); + core.setOutput('mainSourceChanged', mainSourceChanged); + core.setOutput('buildModuleChanged', buildModuleChanged); + core.setOutput('globalConfigChanged', globalConfigChanged); + core.setOutput('packagingChanged', packagingChanged); + core.setOutput('source', source); diff --git a/.github/actions/test/linux-packaging/action.yml b/.github/actions/test/linux-packaging/action.yml new file mode 100644 index 00000000000..3a61e0751c7 --- /dev/null +++ b/.github/actions/test/linux-packaging/action.yml @@ -0,0 +1,69 @@ +name: linux_packaging +description: 'Linux packaging for PowerShell' + +runs: + using: composite + steps: + - name: Capture Environment + if: success() || failure() + run: |- + Import-Module ./tools/ci.psm1 + Show-Environment + shell: pwsh + + - uses: actions/setup-dotnet@v5 + with: + global-json-file: ./global.json + + - name: Bootstrap + run: |- + Import-Module ./build.psm1 + Start-PSBootstrap -Scenario Package + Import-Module ./tools/ci.psm1 + Invoke-CIInstall -SkipUser + shell: pwsh + + - name: Build and Package + run: |- + Import-Module ./tools/ci.psm1 + $releaseTag = Get-ReleaseTag + Start-PSBuild -Configuration 'Release' -ReleaseTag $releaseTag + Invoke-CIFinish + shell: pwsh + + - name: Install Pester + run: |- + Import-Module ./tools/ci.psm1 + Install-CIPester + shell: pwsh + + - name: Validate Package Names + run: |- + # Run Pester tests to validate package names + Import-Module Pester -Force + $testResults = Invoke-Pester -Path ./test/packaging/linux/package-validation.tests.ps1 -PassThru + if ($testResults.FailedCount -gt 0) { + throw "Package validation tests failed" + } + shell: pwsh + + - name: Upload deb packages + uses: actions/upload-artifact@v4 + with: + name: packages-deb + path: ${{ runner.workspace }}/packages/*.deb + if-no-files-found: ignore + + - name: Upload rpm packages + uses: actions/upload-artifact@v4 + with: + name: packages-rpm + path: ${{ runner.workspace }}/packages/*.rpm + if-no-files-found: ignore + + - name: Upload tar.gz packages + uses: actions/upload-artifact@v4 + with: + name: packages-tar + path: ${{ runner.workspace }}/packages/*.tar.gz + if-no-files-found: ignore diff --git a/.github/actions/test/nix/action.yml b/.github/actions/test/nix/action.yml new file mode 100644 index 00000000000..7f68e71c1f5 --- /dev/null +++ b/.github/actions/test/nix/action.yml @@ -0,0 +1,158 @@ +name: nix_test +description: 'Test PowerShell on non-Windows platforms' + +inputs: + purpose: + required: false + default: '' + type: string + tagSet: + required: false + default: CI + type: string + ctrfFolder: + required: false + default: ctrf + type: string + GITHUB_TOKEN: + description: 'GitHub token for API authentication' + required: true + +runs: + using: composite + steps: + - name: Capture Environment + if: success() || failure() + run: |- + Import-Module ./tools/ci.psm1 + Show-Environment + shell: pwsh + + - name: Download Build Artifacts + uses: actions/download-artifact@v4 + with: + path: "${{ github.workspace }}" + + - name: Capture Artifacts Directory + continue-on-error: true + run: |- + Import-Module ./build.psm1 + Write-LogGroupStart -Title 'Artifacts Directory' + Get-ChildItem "${{ github.workspace }}/build/*" -Recurse + Write-LogGroupEnd -Title 'Artifacts Directory' + shell: pwsh + + - uses: actions/setup-dotnet@v4 + with: + global-json-file: ./global.json + + - name: Set Package Name by Platform + id: set_package_name + shell: pwsh + run: |- + Import-Module ./.github/workflows/GHWorkflowHelper/GHWorkflowHelper.psm1 + $platform = $env:RUNNER_OS + Write-Host "Runner platform: $platform" + if ($platform -eq 'Linux') { + $packageName = 'DSC-*-x86_64-linux.tar.gz' + } elseif ($platform -eq 'macOS') { + $packageName = 'DSC-*-x86_64-apple-darwin.tar.gz' + } else { + throw "Unsupported platform: $platform" + } + + Set-GWVariable -Name "DSC_PACKAGE_NAME" -Value $packageName + + - name: Get Latest DSC Package Version + shell: pwsh + run: |- + Import-Module ./.github/workflows/GHWorkflowHelper/GHWorkflowHelper.psm1 + $headers = @{ + Authorization = "Bearer ${{ inputs.GITHUB_TOKEN }}" + } + $releases = Invoke-RestMethod -Uri "https://api.github.com/repos/PowerShell/Dsc/releases" -Headers $headers + $latestRelease = $releases | Where-Object { $v = $_.name.trim("v"); $semVer = [System.Management.Automation.SemanticVersion]::new($v); if ($semVer.Major -eq 3 -and $semVer.Minor -ge 2) { $_ } } | Select-Object -First 1 + $latestVersion = $latestRelease.tag_name.TrimStart("v") + Write-Host "Latest DSC Version: $latestVersion" + + $packageName = "$env:DSC_PACKAGE_NAME" + + Write-Host "Package Name: $packageName" + + $downloadUrl = $latestRelease.assets | Where-Object { $_.name -like "*$packageName*" } | Select-Object -First 1 | Select-Object -ExpandProperty browser_download_url + Write-Host "Download URL: $downloadUrl" + + $tempPath = Get-GWTempPath + + Invoke-RestMethod -Uri $downloadUrl -OutFile "$tempPath/DSC.tar.gz" -Verbose -Headers $headers + New-Item -ItemType Directory -Path "$tempPath/DSC" -Force -Verbose + tar xvf "$tempPath/DSC.tar.gz" -C "$tempPath/DSC" + $dscRoot = "$tempPath/DSC" + Write-Host "DSC Root: $dscRoot" + Set-GWVariable -Name "DSC_ROOT" -Value $dscRoot + + - name: Bootstrap + shell: pwsh + run: |- + Import-Module ./build.psm1 + Write-LogGroupStart -Title 'Bootstrap' + Import-Module ./tools/ci.psm1 + Invoke-CIInstall -SkipUser + Write-LogGroupEnd -Title 'Bootstrap' + + - name: Extract Files + uses: actions/github-script@v7.0.0 + env: + DESTINATION_FOLDER: "${{ github.workspace }}/bins" + ARCHIVE_FILE_PATTERNS: "${{ github.workspace }}/build/build.zip" + with: + script: |- + const fs = require('fs').promises + const path = require('path') + const target = path.resolve(process.env.DESTINATION_FOLDER) + const patterns = process.env.ARCHIVE_FILE_PATTERNS + const globber = await glob.create(patterns) + await io.mkdirP(path.dirname(target)) + for await (const file of globber.globGenerator()) { + if ((await fs.lstat(file)).isDirectory()) continue + await exec.exec(`7z x ${file} -o${target} -aoa`) + } + + - name: Fix permissions + continue-on-error: true + run: |- + find "${{ github.workspace }}/bins" -type d -exec chmod +rwx {} \; + find "${{ github.workspace }}/bins" -type f -exec chmod +rw {} \; + shell: bash + + - name: Capture Extracted Build ZIP + continue-on-error: true + run: |- + Import-Module ./build.psm1 + Write-LogGroupStart -Title 'Extracted Build ZIP' + Get-ChildItem "${{ github.workspace }}/bins/*" -Recurse -ErrorAction SilentlyContinue + Write-LogGroupEnd -Title 'Extracted Build ZIP' + shell: pwsh + + - name: Test + if: success() + run: |- + Import-Module ./tools/ci.psm1 + Restore-PSOptions -PSOptionsPath '${{ github.workspace }}/build/psoptions.json' + $options = (Get-PSOptions) + $rootPath = '${{ github.workspace }}/bins' + $originalRootPath = Split-Path -path $options.Output + $path = Join-Path -path $rootPath -ChildPath (split-path -leaf -path $originalRootPath) + $pwshPath = Join-Path -path $path -ChildPath 'pwsh' + chmod a+x $pwshPath + $options.Output = $pwshPath + Set-PSOptions $options + Invoke-CITest -Purpose '${{ inputs.purpose }}' -TagSet '${{ inputs.tagSet }}' -TitlePrefix '${{ inputs.buildName }}' -OutputFormat NUnitXml + shell: pwsh + + - name: Convert, Publish, and Upload Pester Test Results + uses: "./.github/actions/test/process-pester-results" + with: + name: "${{ inputs.purpose }}-${{ inputs.tagSet }}" + testResultsFolder: "${{ runner.workspace }}/testResults" + ctrfFolder: "${{ inputs.ctrfFolder }}" diff --git a/.github/actions/test/process-pester-results/action.yml b/.github/actions/test/process-pester-results/action.yml new file mode 100644 index 00000000000..27b94f6ebcb --- /dev/null +++ b/.github/actions/test/process-pester-results/action.yml @@ -0,0 +1,27 @@ +name: process-pester-test-results +description: 'Process Pester test results' + +inputs: + name: + required: true + default: '' + type: string + testResultsFolder: + required: false + default: "${{ runner.workspace }}/testResults" + type: string + +runs: + using: composite + steps: + - name: Log Summary + run: |- + & "$env:GITHUB_ACTION_PATH/process-pester-results.ps1" -Name '${{ inputs.name }}' -TestResultsFolder '${{ inputs.testResultsFolder }}' + shell: pwsh + + - name: Upload testResults artifact + if: always() + uses: actions/upload-artifact@v4 + with: + name: junit-pester-${{ inputs.name }} + path: ${{ runner.workspace }}/testResults diff --git a/.github/actions/test/process-pester-results/process-pester-results.ps1 b/.github/actions/test/process-pester-results/process-pester-results.ps1 new file mode 100644 index 00000000000..523de3bebaa --- /dev/null +++ b/.github/actions/test/process-pester-results/process-pester-results.ps1 @@ -0,0 +1,68 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +param( + [parameter(Mandatory)] + [string]$Name, + [parameter(Mandatory)] + [string]$TestResultsFolder +) + +Import-Module "$PSScriptRoot/../../../../build.psm1" + +if (-not $env:GITHUB_STEP_SUMMARY) { + Write-Error "GITHUB_STEP_SUMMARY is not set. Ensure this workflow is running in a GitHub Actions environment." + exit 1 +} + +$testCaseCount = 0 +$testErrorCount = 0 +$testFailureCount = 0 +$testNotRunCount = 0 +$testInconclusiveCount = 0 +$testIgnoredCount = 0 +$testSkippedCount = 0 +$testInvalidCount = 0 + +Get-ChildItem -Path "${TestResultsFolder}/*.xml" -Recurse | ForEach-Object { + $results = [xml] (get-content $_.FullName) + + $testCaseCount += [int]$results.'test-results'.total + $testErrorCount += [int]$results.'test-results'.errors + $testFailureCount += [int]$results.'test-results'.failures + $testNotRunCount += [int]$results.'test-results'.'not-run' + $testInconclusiveCount += [int]$results.'test-results'.inconclusive + $testIgnoredCount += [int]$results.'test-results'.ignored + $testSkippedCount += [int]$results.'test-results'.skipped + $testInvalidCount += [int]$results.'test-results'.invalid +} + +@" + +# Summary of $Name + +- Total Tests: $testCaseCount +- Total Errors: $testErrorCount +- Total Failures: $testFailureCount +- Total Not Run: $testNotRunCount +- Total Inconclusive: $testInconclusiveCount +- Total Ignored: $testIgnoredCount +- Total Skipped: $testSkippedCount +- Total Invalid: $testInvalidCount + +"@ | Out-File -FilePath $ENV:GITHUB_STEP_SUMMARY -Append + +Write-Log "Summary written to $ENV:GITHUB_STEP_SUMMARY" + +Write-LogGroupStart -Title 'Test Results' +Get-Content $ENV:GITHUB_STEP_SUMMARY +Write-LogGroupEnd -Title 'Test Results' + +if ($testErrorCount -gt 0 -or $testFailureCount -gt 0) { + Write-Error "There were $testErrorCount/$testFailureCount errors/failures in the test results." + exit 1 +} +if ($testCaseCount -eq 0) { + Write-Error "No test cases were run." + exit 1 +} diff --git a/.github/actions/test/windows/action.yml b/.github/actions/test/windows/action.yml new file mode 100644 index 00000000000..2c41f6aac5c --- /dev/null +++ b/.github/actions/test/windows/action.yml @@ -0,0 +1,107 @@ +name: windows_test +description: 'Test PowerShell on Windows' + +inputs: + purpose: + required: false + default: '' + type: string + tagSet: + required: false + default: CI + type: string + ctrfFolder: + required: false + default: ctrf + type: string + GITHUB_TOKEN: + description: 'GitHub token for API authentication' + required: true + +runs: + using: composite + steps: + - name: Capture Environment + if: success() || failure() + run: |- + Import-Module ./tools/ci.psm1 + Show-Environment + shell: pwsh + + - name: Download Build Artifacts + uses: actions/download-artifact@v4 + with: + path: "${{ github.workspace }}" + + - name: Capture Artifacts Directory + continue-on-error: true + run: |- + Import-Module ./build.psm1 + Write-LogGroupStart -Title 'Artifacts Directory' + Get-ChildItem "${{ github.workspace }}/build/*" -Recurse + Write-LogGroupEnd -Title 'Artifacts Directory' + shell: pwsh + + - uses: actions/setup-dotnet@v4 + with: + global-json-file: .\global.json + + - name: Get Latest DSC Package Version + shell: pwsh + run: |- + Import-Module .\.github\workflows\GHWorkflowHelper\GHWorkflowHelper.psm1 + $headers = @{ + Authorization = "Bearer ${{ inputs.GITHUB_TOKEN }}" + } + $releases = Invoke-RestMethod -Uri "https://api.github.com/repos/PowerShell/Dsc/releases" -Headers $headers + $latestRelease = $releases | Where-Object { $v = $_.name.trim("v"); $semVer = [System.Management.Automation.SemanticVersion]::new($v); if ($semVer.Major -eq 3 -and $semVer.Minor -ge 2) { $_ } } | Select-Object -First 1 + $latestVersion = $latestRelease.tag_name.TrimStart("v") + Write-Host "Latest DSC Version: $latestVersion" + + $downloadUrl = $latestRelease.assets | Where-Object { $_.name -like "DSC-*-x86_64-pc-windows-msvc.zip" } | Select-Object -First 1 | Select-Object -ExpandProperty browser_download_url + Write-Host "Download URL: $downloadUrl" + $tempPath = Get-GWTempPath + Invoke-RestMethod -Uri $downloadUrl -OutFile "$tempPath\DSC.zip" -Headers $headers + + $null = New-Item -ItemType Directory -Path "$tempPath\DSC" -Force + Expand-Archive -Path "$tempPath\DSC.zip" -DestinationPath "$tempPath\DSC" -Force + $dscRoot = "$tempPath\DSC" + Write-Host "DSC Root: $dscRoot" + Set-GWVariable -Name "DSC_ROOT" -Value $dscRoot + + - name: Bootstrap + shell: powershell + run: |- + Import-Module ./build.psm1 + Write-LogGroupStart -Title 'Bootstrap' + Write-Host "Old Path:" + Write-Host $env:Path + $dotnetPath = Join-Path $env:SystemDrive 'Program Files\dotnet' + $paths = $env:Path -split ";" | Where-Object { -not $_.StartsWith($dotnetPath) } + $env:Path = $paths -join ";" + Write-Host "New Path:" + Write-Host $env:Path + # Bootstrap + Import-Module .\tools\ci.psm1 + Invoke-CIInstall + Write-LogGroupEnd -Title 'Bootstrap' + + - name: Test + if: success() + run: |- + Import-Module .\build.psm1 -force + Import-Module .\tools\ci.psm1 + Restore-PSOptions -PSOptionsPath '${{ github.workspace }}\build\psoptions.json' + $options = (Get-PSOptions) + $path = split-path -path $options.Output + $rootPath = split-Path -path $path + Expand-Archive -Path '${{ github.workspace }}\build\build.zip' -DestinationPath $rootPath -Force + Invoke-CITest -Purpose '${{ inputs.purpose }}' -TagSet '${{ inputs.tagSet }}' -OutputFormat NUnitXml + shell: pwsh + + - name: Convert, Publish, and Upload Pester Test Results + uses: "./.github/actions/test/process-pester-results" + with: + name: "${{ inputs.purpose }}-${{ inputs.tagSet }}" + testResultsFolder: ${{ runner.workspace }}\testResults + ctrfFolder: "${{ inputs.ctrfFolder }}" diff --git a/.github/chatmodes/cherry-pick-commits.chatmode.md b/.github/chatmodes/cherry-pick-commits.chatmode.md new file mode 100644 index 00000000000..826ab11d56c --- /dev/null +++ b/.github/chatmodes/cherry-pick-commits.chatmode.md @@ -0,0 +1,78 @@ +# Cherry-Pick Commits Between Branches + +Cherry-pick recent commits from a source branch to a target branch without switching branches. + +## Instructions for Copilot + +1. **Confirm branches with the user** + - Ask the user to confirm the source and target branches + - If different branches are needed, update the configuration + +2. **Identify unique commits** + - Run: `git log .. --oneline --reverse` + - **IMPORTANT**: The commit count may be misleading if branches diverged from different base commits + - Compare the LAST few commits from each branch to identify actual missing commits: + - `git log --oneline -10` + - `git log --oneline -10` + - Look for commits with the same message but different SHAs (rebased commits) + - Show the user ONLY the truly missing commits (usually just the most recent ones) + +3. **Confirm with user before proceeding** + - If the commit count seems unusually high (e.g., 400+), STOP and verify semantically + - Ask: "I found X commits to cherry-pick. Shall I proceed?" + - If there are many commits, warn that this may take time + +4. **Execute the cherry-pick** + - Ensure the target branch is checked out first + - Run: `git cherry-pick ` for single commits + - Or: `git cherry-pick ` for multiple commits + - Apply commits in chronological order (oldest first) + +5. **Handle any issues** + - If conflicts occur, pause and ask user for guidance + - If empty commits occur, automatically skip with `git cherry-pick --skip` + +6. **Verify and report results** + - Run: `git log - --oneline` + - Show the user the newly applied commits + - Confirm the branch is now ahead by X commits + +## Key Git Commands + +```bash +# Find unique commits (may show full divergence if branches were rebased) +git log .. --oneline --reverse + +# Compare recent commits on each branch (more reliable for rebased branches) +git log --oneline -10 +git log --oneline -10 + +# Cherry-pick specific commits (when target is checked out) +git cherry-pick +git cherry-pick + +# Skip empty commits +git cherry-pick --skip + +# Verify result +git log - --oneline +``` + +## Common Scenarios + +- **Empty commits**: Automatically skip with `git cherry-pick --skip` +- **Conflicts**: Stop, show files with conflicts, ask user to resolve +- **Many commits**: Warn user and confirm before proceeding +- **Already applied**: These will result in empty commits that should be skipped +- **Diverged branches**: If branches diverged (rebased), `git log` may show the entire history difference + - The actual missing commits are usually only the most recent ones + - Compare commit messages from recent history on both branches + - Cherry-pick only commits that are semantically missing + +## Workflow Style + +Use an interactive, step-by-step approach: +- Show output from each command +- Ask for confirmation before major actions +- Provide clear status updates +- Handle errors gracefully with user guidance diff --git a/.github/dependabot.yml b/.github/dependabot.yml new file mode 100644 index 00000000000..45d2e8fe928 --- /dev/null +++ b/.github/dependabot.yml @@ -0,0 +1,32 @@ +version: 2 + +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "daily" + labels: + - "CL-BuildPackaging" + + - package-ecosystem: "github-actions" + directory: "/" + target-branch: "release/*" + schedule: + interval: "daily" + labels: + - "CL-BuildPackaging" + + - package-ecosystem: "docker" + directory: / + schedule: + interval: daily + labels: + - "CL-BuildPackaging" + + - package-ecosystem: "docker" + directory: "/" + target-branch: "release/*" + schedule: + interval: daily + labels: + - "CL-BuildPackaging" diff --git a/.github/instructions/build-and-packaging-steps.instructions.md b/.github/instructions/build-and-packaging-steps.instructions.md new file mode 100644 index 00000000000..934b1539593 --- /dev/null +++ b/.github/instructions/build-and-packaging-steps.instructions.md @@ -0,0 +1,127 @@ +--- +applyTo: + - ".github/actions/**/*.yml" + - ".github/workflows/**/*.yml" +--- + +# Build and Packaging Steps Pattern + +## Important Rule + +**Build and packaging must run in the same step OR you must save and restore PSOptions between steps.** + +## Why This Matters + +When `Start-PSBuild` runs, it creates PSOptions that contain build configuration details (runtime, configuration, output path, etc.). The packaging functions like `Start-PSPackage` and `Invoke-CIFinish` rely on these PSOptions to know where the build output is located and how it was built. + +GitHub Actions steps run in separate PowerShell sessions. This means PSOptions from one step are not available in the next step. + +## Pattern 1: Combined Build and Package (Recommended) + +Run build and packaging in the same step to keep PSOptions in memory: + +```yaml +- name: Build and Package + run: |- + Import-Module ./tools/ci.psm1 + $releaseTag = Get-ReleaseTag + Start-PSBuild -Configuration 'Release' -ReleaseTag $releaseTag + Invoke-CIFinish + shell: pwsh +``` + +**Benefits:** +- Simpler code +- No need for intermediate files +- PSOptions automatically available to packaging + +## Pattern 2: Separate Steps with Save/Restore + +If you must separate build and packaging into different steps: + +```yaml +- name: Build PowerShell + run: |- + Import-Module ./tools/ci.psm1 + $releaseTag = Get-ReleaseTag + Start-PSBuild -Configuration 'Release' -ReleaseTag $releaseTag + Save-PSOptions -PSOptionsPath "${{ runner.workspace }}/psoptions.json" + shell: pwsh + +- name: Create Packages + run: |- + Import-Module ./tools/ci.psm1 + Restore-PSOptions -PSOptionsPath "${{ runner.workspace }}/psoptions.json" + Invoke-CIFinish + shell: pwsh +``` + +**When to use:** +- When you need to run other steps between build and packaging +- When build and packaging require different permissions or environments + +## Common Mistakes + +### ❌ Incorrect: Separate steps without save/restore + +```yaml +- name: Build PowerShell + run: |- + Start-PSBuild -Configuration 'Release' + shell: pwsh + +- name: Create Packages + run: |- + Invoke-CIFinish # ❌ FAILS: PSOptions not available + shell: pwsh +``` + +### ❌ Incorrect: Using artifacts without PSOptions + +```yaml +- name: Download Build Artifacts + uses: actions/download-artifact@v4 + with: + name: build + +- name: Create Packages + run: |- + Invoke-CIFinish # ❌ FAILS: PSOptions not restored + shell: pwsh +``` + +## Related Functions + +- `Start-PSBuild` - Builds PowerShell and sets PSOptions +- `Save-PSOptions` - Saves PSOptions to a JSON file +- `Restore-PSOptions` - Loads PSOptions from a JSON file +- `Get-PSOptions` - Gets current PSOptions +- `Set-PSOptions` - Sets PSOptions +- `Start-PSPackage` - Creates packages (requires PSOptions) +- `Invoke-CIFinish` - Calls packaging (requires PSOptions on Linux/macOS) + +## Examples + +### Linux Packaging Action + +```yaml +- name: Build and Package + run: |- + Import-Module ./tools/ci.psm1 + $releaseTag = Get-ReleaseTag + Start-PSBuild -Configuration 'Release' -ReleaseTag $releaseTag + Invoke-CIFinish + shell: pwsh +``` + +### Windows Packaging Workflow + +```yaml +- name: Build and Package + run: | + Import-Module .\tools\ci.psm1 + Invoke-CIFinish -Runtime ${{ matrix.runtimePrefix }}-${{ matrix.architecture }} -channel ${{ matrix.channel }} + shell: pwsh +``` + +Note: `Invoke-CIFinish` for Windows includes both build and packaging in its logic when `Stage` contains 'Build'. diff --git a/.github/instructions/build-checkout-prerequisites.instructions.md b/.github/instructions/build-checkout-prerequisites.instructions.md new file mode 100644 index 00000000000..717aa6faa36 --- /dev/null +++ b/.github/instructions/build-checkout-prerequisites.instructions.md @@ -0,0 +1,148 @@ +--- +applyTo: + - ".github/**/*.yml" + - ".github/**/*.yaml" +--- + +# Build and Checkout Prerequisites for PowerShell CI + +This document describes the checkout and build prerequisites used in PowerShell's CI workflows. It is intended for GitHub Copilot sessions working with the build system. + +## Overview + +The PowerShell repository uses a standardized build process across Linux, Windows, and macOS CI workflows. Understanding the checkout configuration and the `Sync-PSTags` operation is crucial for working with the build system. + +## Checkout Configuration + +### Fetch Depth + +All CI workflows that build or test PowerShell use `fetch-depth: 1000` in the checkout step: + +```yaml +- name: checkout + uses: actions/checkout@v5 + with: + fetch-depth: 1000 +``` + +**Why 1000 commits?** +- The build system needs access to Git history to determine version information +- `Sync-PSTags` requires sufficient history to fetch and work with tags +- 1000 commits provides a reasonable balance between clone speed and having enough history for version calculation +- Shallow clones (fetch-depth: 1) would break versioning logic + +**Exceptions:** +- The `changes` job uses default fetch depth (no explicit `fetch-depth`) since it only needs to detect file changes +- The `analyze` job (CodeQL) uses `fetch-depth: '0'` (full history) for comprehensive security analysis +- Linux packaging uses `fetch-depth: 0` to ensure all tags are available for package version metadata + +### Workflows Using fetch-depth: 1000 + +- **Linux CI** (`.github/workflows/linux-ci.yml`): All build and test jobs +- **Windows CI** (`.github/workflows/windows-ci.yml`): All build and test jobs +- **macOS CI** (`.github/workflows/macos-ci.yml`): All build and test jobs + +## Sync-PSTags Operation + +### What is Sync-PSTags? + +`Sync-PSTags` is a PowerShell function defined in `build.psm1` that ensures Git tags from the upstream PowerShell repository are synchronized to the local clone. + +### Location + +- **Function Definition**: `build.psm1` (line 36-76) +- **Called From**: + - `.github/actions/build/ci/action.yml` (Bootstrap step, line 24) + - `tools/ci.psm1` (Invoke-CIInstall function, line 146) + +### How It Works + +```powershell +Sync-PSTags -AddRemoteIfMissing +``` + +The function: +1. Searches for a Git remote pointing to the official PowerShell repository: + - `https://github.com/PowerShell/PowerShell` + - `git@github.com:PowerShell/PowerShell` + +2. If no upstream remote exists and `-AddRemoteIfMissing` is specified: + - Adds a remote named `upstream` pointing to `https://github.com/PowerShell/PowerShell.git` + +3. Fetches all tags from the upstream remote: + ```bash + git fetch --tags --quiet upstream + ``` + +4. Sets `$script:tagsUpToDate = $true` to indicate tags are synchronized + +### Why Sync-PSTags is Required + +Tags are critical for: +- **Version Calculation**: `Get-PSVersion` uses `git describe --abbrev=0` to find the latest tag +- **Build Numbering**: CI builds use tag-based versioning for artifacts +- **Changelog Generation**: Release notes are generated based on tags +- **Package Metadata**: Package versions are derived from Git tags + +Without synchronized tags: +- Version detection would fail or return incorrect versions +- Builds might have inconsistent version numbers +- The build process would error when trying to determine the version + +### Bootstrap Step in CI Action + +The `.github/actions/build/ci/action.yml` includes this in the Bootstrap step: + +```yaml +- name: Bootstrap + if: success() + run: |- + Write-Verbose -Verbose "Running Bootstrap..." + Import-Module .\tools\ci.psm1 + Invoke-CIInstall -SkipUser + Write-Verbose -Verbose "Start Sync-PSTags" + Sync-PSTags -AddRemoteIfMissing + Write-Verbose -Verbose "End Sync-PSTags" + shell: pwsh +``` + +**Note**: `Sync-PSTags` is called twice: +1. Once by `Invoke-CIInstall` (in `tools/ci.psm1`) +2. Explicitly again in the Bootstrap step + +This redundancy ensures tags are available even if the first call encounters issues. + +## Best Practices for Copilot Sessions + +When working with the PowerShell CI system: + +1. **Always use `fetch-depth: 1000` or greater** when checking out code for build or test operations +2. **Understand that `Sync-PSTags` requires network access** to fetch tags from the upstream repository +3. **Don't modify the fetch-depth without understanding the impact** on version calculation +4. **If adding new CI workflows**, follow the existing pattern: + - Use `fetch-depth: 1000` for build/test jobs + - Call `Sync-PSTags -AddRemoteIfMissing` during bootstrap + - Ensure the upstream remote is properly configured + +5. **For local development**, developers should: + - Have the upstream remote configured + - Run `Sync-PSTags -AddRemoteIfMissing` before building + - Or use `Start-PSBuild` which handles this automatically + +## Related Files + +- `.github/actions/build/ci/action.yml` - Main CI build action +- `.github/workflows/linux-ci.yml` - Linux CI workflow +- `.github/workflows/windows-ci.yml` - Windows CI workflow +- `.github/workflows/macos-ci.yml` - macOS CI workflow +- `build.psm1` - Contains Sync-PSTags function definition +- `tools/ci.psm1` - CI-specific build functions that call Sync-PSTags + +## Summary + +The PowerShell CI system depends on: +1. **Adequate Git history** (fetch-depth: 1000) for version calculation +2. **Synchronized Git tags** via `Sync-PSTags` for accurate versioning +3. **Upstream remote access** to fetch official repository tags + +These prerequisites ensure consistent, accurate build versioning across all CI platforms. diff --git a/.github/instructions/build-configuration-guide.instructions.md b/.github/instructions/build-configuration-guide.instructions.md new file mode 100644 index 00000000000..d0384f4f307 --- /dev/null +++ b/.github/instructions/build-configuration-guide.instructions.md @@ -0,0 +1,150 @@ +--- +applyTo: + - "build.psm1" + - "tools/ci.psm1" + - ".github/**/*.yml" + - ".github/**/*.yaml" + - ".pipelines/**/*.yml" +--- + +# Build Configuration Guide + +## Choosing the Right Configuration + +### For Testing + +**Use: Default (Debug)** + +```yaml +- name: Build for Testing + shell: pwsh + run: | + Import-Module ./tools/ci.psm1 + Start-PSBuild +``` + +**Why Debug:** +- Includes debugging symbols +- Better error messages +- Faster build times +- Suitable for xUnit and Pester tests + +**Do NOT use:** +- `-Configuration 'Release'` (unnecessary for tests) +- `-ReleaseTag` (not needed for tests) +- `-CI` (unless you specifically need Pester module) + +### For Release/Packaging + +**Use: Release with version tag and public NuGet feeds** + +```yaml +- name: Build for Release + shell: pwsh + run: | + Import-Module ./build.psm1 + Import-Module ./tools/ci.psm1 + Switch-PSNugetConfig -Source Public + $releaseTag = Get-ReleaseTag + Start-PSBuild -Configuration 'Release' -ReleaseTag $releaseTag +``` + +**Why Release:** +- Optimized binaries +- No debug symbols (smaller size) +- Production-ready + +**Why Switch-PSNugetConfig -Source Public:** +- Switches NuGet package sources to public feeds (nuget.org and public Azure DevOps feeds) +- Required for CI/CD environments that don't have access to private feeds +- Uses publicly available packages instead of Microsoft internal feeds + +### For Code Coverage + +**Use: CodeCoverage configuration** + +```yaml +- name: Build with Coverage + shell: pwsh + run: | + Import-Module ./tools/ci.psm1 + Start-PSBuild -Configuration 'CodeCoverage' +``` + +## Platform Considerations + +### All Platforms + +Same commands work across Linux, Windows, and macOS: + +```yaml +strategy: + matrix: + os: [ubuntu-latest, windows-latest, macos-latest] +runs-on: ${{ matrix.os }} +steps: + - name: Build PowerShell + shell: pwsh + run: | + Import-Module ./tools/ci.psm1 + Start-PSBuild +``` + +### Output Locations + +**Linux/macOS:** +``` +src/powershell-unix/bin/Debug///publish/ +``` + +**Windows:** +``` +src/powershell-win-core/bin/Debug///publish/ +``` + +## Best Practices + +1. Use default configuration for testing +2. Avoid redundant parameters +3. Match configuration to purpose +4. Use `-CI` only when needed +5. Always specify `-ReleaseTag` for release or packaging builds +6. Use `Switch-PSNugetConfig -Source Public` in CI/CD for release builds + +## NuGet Feed Configuration + +### Switch-PSNugetConfig + +The `Switch-PSNugetConfig` function in `build.psm1` manages NuGet package source configuration. + +**Available Sources:** + +- **Public**: Uses public feeds (nuget.org and public Azure DevOps feeds) + - Required for: CI/CD environments, public builds, packaging + - Does not require authentication + +- **Private**: Uses internal PowerShell team feeds + - Required for: Internal development with preview packages + - Requires authentication credentials + +- **NuGetOnly**: Uses only nuget.org + - Required for: Minimal dependency scenarios + +**Usage:** + +```powershell +# Switch to public feeds (most common for CI/CD) +Switch-PSNugetConfig -Source Public + +# Switch to private feeds with authentication +Switch-PSNugetConfig -Source Private -UserName $userName -ClearTextPAT $pat + +# Switch to nuget.org only +Switch-PSNugetConfig -Source NuGetOnly +``` + +**When to Use:** + +- **Always use `-Source Public`** before building in CI/CD workflows +- Use before any build that will create packages for distribution +- Use in forks or environments without access to Microsoft internal feeds diff --git a/.github/instructions/code-review-branch-strategy.instructions.md b/.github/instructions/code-review-branch-strategy.instructions.md new file mode 100644 index 00000000000..191a677b912 --- /dev/null +++ b/.github/instructions/code-review-branch-strategy.instructions.md @@ -0,0 +1,230 @@ +--- +applyTo: "**/*" +--- + +# Code Review Branch Strategy Guide + +This guide helps GitHub Copilot provide appropriate feedback when reviewing code changes, particularly distinguishing between issues that should be fixed in the current branch versus the default branch. + +## Purpose + +When reviewing pull requests, especially those targeting release branches, it's important to identify whether an issue should be fixed in: +- **The current PR/branch** - Release-specific fixes or backports +- **The default branch first** - General bugs that exist in the main codebase + +## Branch Types and Fix Strategy + +### Release Branches (e.g., `release/v7.5`, `release/v7.4`) + +**Purpose:** Contain release-specific changes and critical backports + +**Should contain:** +- Release-specific configuration changes +- Critical bug fixes that are backported from the default branch +- Release packaging/versioning adjustments + +**Should NOT contain:** +- New general bug fixes that haven't been fixed in the default branch +- Refactoring or improvements that apply to the main codebase +- Workarounds for issues that exist in the default branch + +### Default/Main Branch (e.g., `master`, `main`) + +**Purpose:** Primary development branch for all ongoing work + +**Should contain:** +- All general bug fixes +- New features and improvements +- Refactoring and code quality improvements +- Fixes that will later be backported to release branches + +## Identifying Issues That Belong in the Default Branch + +When reviewing a PR targeting a release branch, look for these indicators that suggest the fix should be in the default branch first: + +### 1. The Root Cause Exists in Default Branch + +If the underlying issue exists in the default branch's code, it should be fixed there first. + +**Example:** +```yaml +# PR changes this in release/v7.5: +- $metadata = Get-Content "$repoRoot/tools/metadata.json" -Raw | ConvertFrom-Json ++ $metadata = Get-Content "$(Build.SourcesDirectory)/PowerShell/tools/metadata.json" -Raw | ConvertFrom-Json +``` + +**Analysis:** If `$repoRoot` is undefined because the template doesn't include its dependencies in BOTH the release branch AND the default branch, the fix should address the root cause in the default branch first. + +### 2. The Fix is a Workaround Rather Than a Proper Solution + +If the change introduces a workaround (hardcoded paths, special cases) rather than fixing the underlying design issue, it likely belongs in the default branch as a proper fix. + +**Example:** +- Using hardcoded paths instead of fixing variable initialization +- Adding special cases instead of fixing the logic +- Duplicating code instead of fixing shared dependencies + +### 3. The Issue Affects General Functionality + +If the issue affects general functionality not specific to a release, it should be fixed in the default branch. + +**Example:** +- Template dependencies that affect all pipelines +- Shared utility functions +- Common configuration issues + +## Providing Code Review Feedback + +### For Issues in the Current Branch + +When an issue is specific to the current branch or is a legitimate fix for the branch being targeted, **use the default code review feedback format** without any special branch-strategy commentary. + +### For Issues That Belong in the Default Branch + +1. **Provide the code review feedback** +2. **Explain why it should be fixed in the default branch** +3. **Provide an issue template** in markdown format + +**Example:** + +```markdown +The `channelSelection.yml` template relies on `$repoRoot` being set by `SetVersionVariables.yml`, but doesn't declare this dependency. This issue exists in both the release branch and the default branch. + +**This should be fixed in the default branch first**, then backported if needed. The proper fix is to ensure template dependencies are correctly declared, rather than using hardcoded paths as a workaround. + +--- + +**Suggested Issue for Default Branch:** + +### Issue Title +`channelSelection.yml` template missing dependency on `SetVersionVariables.yml` + +### Description +The `channelSelection.yml` template uses the `$repoRoot` variable but doesn't ensure it's set beforehand by including `SetVersionVariables.yml`. + +**Current State:** +- `channelSelection.yml` expects `$repoRoot` to be available +- Not all pipelines that use `channelSelection.yml` include `SetVersionVariables.yml` first +- This creates an implicit dependency that's not enforced + +**Expected State:** +Either: +1. `channelSelection.yml` should include `SetVersionVariables.yml` as a dependency, OR +2. `channelSelection.yml` should be refactored to not depend on `$repoRoot`, OR +3. Pipelines using `channelSelection.yml` should explicitly include `SetVersionVariables.yml` first + +**Files Affected:** +- `.pipelines/templates/channelSelection.yml` +- `.pipelines/templates/package-create-msix.yml` +- `.pipelines/templates/release-SetTagAndChangelog.yml` + +**Priority:** Medium +**Labels:** `Issue-Bug`, `Area-Build`, `Area-Pipeline` +``` + +## Issue Template Format + +When creating an issue template for the default branch, use this structure: + +```markdown +### Issue Title +[Clear, concise description of the problem] + +### Description +[Detailed explanation of the issue] + +**Current State:** +- [What's happening now] +- [Why it's problematic] + +**Expected State:** +- [What should happen] +- [Proposed solution(s)] + +**Files Affected:** +- [List of files] + +**Priority:** [Low/Medium/High/Critical] +**Labels:** [Suggested labels like `Issue-Bug`, `Area-*`] + +**Additional Context:** +[Any additional information, links to related issues, etc.] +``` + +## Common Scenarios + +### Scenario 1: Template Dependency Issues + +**Indicators:** +- Missing template includes +- Undefined variables from other templates +- Assumptions about pipeline execution order + +**Action:** Suggest fixing template dependencies in the default branch. + +### Scenario 2: Hardcoded Values + +**Indicators:** +- Hardcoded paths replacing variables +- Environment-specific values in shared code +- Magic strings or numbers + +**Action:** Suggest proper variable/parameter usage in the default branch. + +### Scenario 3: Logic Errors + +**Indicators:** +- Incorrect conditional logic +- Missing error handling +- Race conditions + +**Action:** Suggest fixing the logic in the default branch unless it's release-specific. + +### Scenario 4: Legitimate Release Branch Fixes + +**Indicators:** +- Version-specific configuration +- Release packaging changes +- Backport of already-fixed default branch issue + +**Action:** Provide normal code review feedback for the current PR. + +## Best Practices + +1. **Always check if the issue exists in the default branch** before suggesting a release-branch-only fix +2. **Prefer fixing root causes over workarounds** +3. **Provide clear rationale** for why a fix belongs in the default branch +4. **Include actionable issue templates** so users can easily create issues +5. **Be helpful, not blocking** - provide the feedback even if you can't enforce where it's fixed + +## Examples of Good vs. Bad Approaches + +### ❌ Bad: Workaround in Release Branch Only + +```yaml +# In release/v7.5 only +- pwsh: | + $metadata = Get-Content "$(Build.SourcesDirectory)/PowerShell/tools/metadata.json" -Raw +``` + +**Why bad:** Hardcodes path to work around missing `$repoRoot`, doesn't fix the default branch. + +### ✅ Good: Fix in Default Branch, Then Backport + +```yaml +# In default branch first +- template: SetVersionVariables.yml@self # Ensures $repoRoot is set +- template: channelSelection.yml@self # Now can use $repoRoot +``` + +**Why good:** Fixes the root cause by ensuring dependencies are declared, then backport to release if needed. + +## When in Doubt + +If you're unsure whether an issue should be fixed in the current branch or the default branch, ask yourself: + +1. Does this issue exist in the default branch? +2. Is this a workaround or a proper fix? +3. Will other branches/releases benefit from this fix? + +If the answer to any of these is "yes," suggest fixing it in the default branch first. diff --git a/.github/instructions/instruction-file-format.instructions.md b/.github/instructions/instruction-file-format.instructions.md new file mode 100644 index 00000000000..7c4e0bdd13d --- /dev/null +++ b/.github/instructions/instruction-file-format.instructions.md @@ -0,0 +1,220 @@ +--- +applyTo: + - ".github/instructions/**/*.instructions.md" +--- + +# Instruction File Format Guide + +This document describes the format and guidelines for creating custom instruction files for GitHub Copilot in the PowerShell repository. + +## File Naming Convention + +All instruction files must use the `.instructions.md` suffix: +- ✅ Correct: `build-checkout-prerequisites.instructions.md` +- ✅ Correct: `start-psbuild-basics.instructions.md` +- ❌ Incorrect: `build-guide.md` +- ❌ Incorrect: `instructions.md` + +## Required Frontmatter + +Every instruction file must start with YAML frontmatter containing an `applyTo` section: + +```yaml +--- +applyTo: + - "path/to/files/**/*.ext" + - "specific-file.ext" +--- +``` + +### applyTo Patterns + +Specify which files or directories these instructions apply to: + +**For workflow files:** +```yaml +applyTo: + - ".github/**/*.yml" + - ".github/**/*.yaml" +``` + +**For build scripts:** +```yaml +applyTo: + - "build.psm1" + - "tools/ci.psm1" +``` + +**For multiple contexts:** +```yaml +applyTo: + - "build.psm1" + - "tools/**/*.psm1" + - ".github/**/*.yml" +``` + +## Content Structure + +### 1. Clear Title + +Use a descriptive H1 heading after the frontmatter: + +```markdown +# Build Configuration Guide +``` + +### 2. Purpose or Overview + +Start with a brief explanation of what the instructions cover: + +```markdown +## Purpose + +This guide explains how to configure PowerShell builds for different scenarios. +``` + +### 3. Actionable Content + +Provide clear, actionable guidance: + +**✅ Good - Specific and actionable:** +```markdown +## Default Usage + +Use `Start-PSBuild` with no parameters for testing: + +```powershell +Import-Module ./tools/ci.psm1 +Start-PSBuild +``` +``` + +**❌ Bad - Vague and unclear:** +```markdown +## Usage + +You can use Start-PSBuild to build stuff. +``` + +### 4. Code Examples + +Include working code examples with proper syntax highlighting: + +```markdown +```yaml +- name: Build PowerShell + shell: pwsh + run: | + Import-Module ./tools/ci.psm1 + Start-PSBuild +``` +``` + +### 5. Context and Rationale + +Explain why things are done a certain way: + +```markdown +**Why fetch-depth: 1000?** +- The build system needs Git history for version calculation +- Shallow clones would break versioning logic +``` + +## Best Practices + +### Be Concise + +- Focus on essential information +- Remove redundant explanations +- Use bullet points for lists + +### Be Specific + +- Provide exact commands and parameters +- Include file paths and line numbers when relevant +- Show concrete examples, not abstract concepts + +### Avoid Duplication + +- Don't repeat information from other instruction files +- Reference other files when appropriate +- Keep each file focused on one topic + +### Use Proper Formatting + +**Headers:** +- Use H1 (`#`) for the main title +- Use H2 (`##`) for major sections +- Use H3 (`###`) for subsections + +**Code blocks:** +- Always specify the language: ` ```yaml `, ` ```powershell `, ` ```bash ` +- Keep examples short and focused +- Test examples before including them + +**Lists:** +- Use `-` for unordered lists +- Use `1.` for ordered lists +- Keep list items concise + +## Example Structure + +```markdown +--- +applyTo: + - "relevant/files/**/*.ext" +--- + +# Title of Instructions + +Brief description of what these instructions cover. + +## Section 1 + +Content with examples. + +```language +code example +``` + +## Section 2 + +More specific guidance. + +### Subsection + +Detailed information when needed. + +## Best Practices + +- Actionable tip 1 +- Actionable tip 2 +``` + +## Maintaining Instructions + +### When to Create a New File + +Create a new instruction file when: +- Covering a distinct topic not addressed elsewhere +- The content is substantial enough to warrant its own file +- The `applyTo` scope is different from existing files + +### When to Update an Existing File + +Update an existing file when: +- Information is outdated +- New best practices emerge +- Examples need correction + +### When to Merge or Delete + +Merge or delete files when: +- Content is duplicated across multiple files +- A file is too small to be useful standalone +- Information is no longer relevant + +## Reference + +For more details, see: +- [GitHub Copilot Custom Instructions Documentation](https://docs.github.com/en/copilot/how-tos/configure-custom-instructions/add-repository-instructions) diff --git a/.github/instructions/log-grouping-guidelines.instructions.md b/.github/instructions/log-grouping-guidelines.instructions.md new file mode 100644 index 00000000000..ff845db4e4b --- /dev/null +++ b/.github/instructions/log-grouping-guidelines.instructions.md @@ -0,0 +1,181 @@ +--- +applyTo: + - "build.psm1" + - "tools/ci.psm1" + - ".github/**/*.yml" + - ".github/**/*.yaml" +--- + +# Log Grouping Guidelines for GitHub Actions + +## Purpose + +Guidelines for using `Write-LogGroupStart` and `Write-LogGroupEnd` to create collapsible log sections in GitHub Actions CI/CD runs. + +## Key Principles + +### 1. Groups Cannot Be Nested + +GitHub Actions does not support nested groups. Only use one level of grouping. + +**❌ Don't:** +```powershell +Write-LogGroupStart -Title "Outer Group" +Write-LogGroupStart -Title "Inner Group" +# ... operations ... +Write-LogGroupEnd -Title "Inner Group" +Write-LogGroupEnd -Title "Outer Group" +``` + +**✅ Do:** +```powershell +Write-LogGroupStart -Title "Operation A" +# ... operations ... +Write-LogGroupEnd -Title "Operation A" + +Write-LogGroupStart -Title "Operation B" +# ... operations ... +Write-LogGroupEnd -Title "Operation B" +``` + +### 2. Groups Should Be Substantial + +Only create groups for operations that generate substantial output (5+ lines). Small groups add clutter without benefit. + +**❌ Don't:** +```powershell +Write-LogGroupStart -Title "Generate Resource Files" +Write-Log -message "Run ResGen" +Start-ResGen +Write-LogGroupEnd -Title "Generate Resource Files" +``` + +**✅ Do:** +```powershell +Write-Log -message "Run ResGen (generating C# bindings for resx files)" +Start-ResGen +``` + +### 3. Groups Should Represent Independent Operations + +Each group should be a logically independent operation that users might want to expand/collapse separately. + +**✅ Good examples:** +- Install Native Dependencies +- Install .NET SDK +- Build PowerShell +- Restore NuGet Packages + +**❌ Bad examples:** +- Individual project restores (too granular) +- Small code generation steps (too small) +- Sub-steps of a larger operation (would require nesting) + +### 4. One Group Per Iteration Is Excessive + +Avoid putting log groups inside loops where each iteration creates a separate group. This would probably cause nesting. + +**❌ Don't:** +```powershell +$projects | ForEach-Object { + Write-LogGroupStart -Title "Restore Project: $_" + dotnet restore $_ + Write-LogGroupEnd -Title "Restore Project: $_" +} +``` + +**✅ Do:** +```powershell +Write-LogGroupStart -Title "Restore All Projects" +$projects | ForEach-Object { + Write-Log -message "Restoring $_" + dotnet restore $_ +} +Write-LogGroupEnd -Title "Restore All Projects" +``` + +## Usage Pattern + +```powershell +Write-LogGroupStart -Title "Descriptive Operation Name" +try { + # ... operation code ... + Write-Log -message "Status updates" +} +finally { + # Ensure group is always closed +} +Write-LogGroupEnd -Title "Descriptive Operation Name" +``` + +## When to Use Log Groups + +Use log groups for: +- Major build phases (bootstrap, restore, build, test, package) +- Installation operations (dependencies, SDKs, tools) +- Operations that produce 5+ lines of output +- Operations where users might want to collapse verbose output + +Don't use log groups for: +- Single-line operations +- Code that's already inside another group +- Loop iterations with minimal output per iteration +- Diagnostic or debug output that should always be visible + +## Examples from build.psm1 + +### Good Usage + +```powershell +function Start-PSBootstrap { + # Multiple independent operations, each with substantial output + Write-LogGroupStart -Title "Install Native Dependencies" + # ... apt-get/yum/brew install commands ... + Write-LogGroupEnd -Title "Install Native Dependencies" + + Write-LogGroupStart -Title "Install .NET SDK" + # ... dotnet installation ... + Write-LogGroupEnd -Title "Install .NET SDK" +} +``` + +### Avoid + +```powershell +# Too small - just 2-3 lines +Write-LogGroupStart -Title "Generate Resource Files (ResGen)" +Write-Log -message "Run ResGen" +Start-ResGen +Write-LogGroupEnd -Title "Generate Resource Files (ResGen)" +``` + +## GitHub Actions Syntax + +These functions emit GitHub Actions workflow commands: +- `Write-LogGroupStart` → `::group::Title` +- `Write-LogGroupEnd` → `::endgroup::` + +In the GitHub Actions UI, this renders as collapsible sections with the specified title. + +## Testing + +Test log grouping locally: +```powershell +$env:GITHUB_ACTIONS = 'true' +Import-Module ./build.psm1 +Write-LogGroupStart -Title "Test" +Write-Log -Message "Content" +Write-LogGroupEnd -Title "Test" +``` + +Output should show: +``` +::group::Test +Content +::endgroup:: +``` + +## References + +- [GitHub Actions: Grouping log lines](https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#grouping-log-lines) +- `build.psm1`: `Write-LogGroupStart` and `Write-LogGroupEnd` function definitions diff --git a/.github/instructions/onebranch-condition-syntax.instructions.md b/.github/instructions/onebranch-condition-syntax.instructions.md new file mode 100644 index 00000000000..19bf331d9c3 --- /dev/null +++ b/.github/instructions/onebranch-condition-syntax.instructions.md @@ -0,0 +1,223 @@ +--- +applyTo: ".pipelines/**/*.{yml,yaml}" +--- + +# OneBranch Pipeline Condition Syntax + +## Overview +Azure Pipelines (OneBranch) uses specific syntax for referencing variables and parameters in condition expressions. Using the wrong syntax will cause conditions to fail silently or behave unexpectedly. + +## Variable Reference Patterns + +### In Condition Expressions + +**✅ Correct Pattern:** +```yaml +condition: eq(variables['VariableName'], 'value') +condition: or(eq(variables['VAR1'], 'true'), eq(variables['VAR2'], 'true')) +condition: and(succeeded(), eq(variables['Architecture'], 'fxdependent')) +``` + +**❌ Incorrect Patterns:** +```yaml +# Don't use $(VAR) string expansion in conditions +condition: eq('$(VariableName)', 'value') + +# Don't use direct variable references +condition: eq($VariableName, 'value') +``` + +### In Script Content (pwsh, bash, etc.) + +**✅ Correct Pattern:** +```yaml +- pwsh: | + $value = '$(VariableName)' + Write-Host "Value: $(VariableName)" +``` + +### In Input Fields + +**✅ Correct Pattern:** +```yaml +inputs: + serviceEndpoint: '$(ServiceEndpoint)' + sbConfigPath: '$(SBConfigPath)' +``` + +## Parameter References + +### Template Parameters (Compile-Time) + +**✅ Correct Pattern:** +```yaml +parameters: + - name: OfficialBuild + type: boolean + default: false + +steps: + - task: SomeTask@1 + condition: eq('${{ parameters.OfficialBuild }}', 'true') +``` + +Note: Parameters use `${{ parameters.Name }}` because they're evaluated at template compile-time. + +### Runtime Variables (Execution-Time) + +**✅ Correct Pattern:** +```yaml +steps: + - pwsh: | + Write-Host "##vso[task.setvariable variable=MyVar]somevalue" + displayName: Set Variable + + - task: SomeTask@1 + condition: eq(variables['MyVar'], 'somevalue') +``` + +## Common Scenarios + +### Scenario 1: Check if Variable Equals Value + +```yaml +- task: DoSomething@1 + condition: eq(variables['PREVIEW'], 'true') +``` + +### Scenario 2: Multiple Variable Conditions (OR) + +```yaml +- task: DoSomething@1 + condition: or(eq(variables['STABLE'], 'true'), eq(variables['LTS'], 'true')) +``` + +### Scenario 3: Multiple Variable Conditions (AND) + +```yaml +- task: DoSomething@1 + condition: and(succeeded(), eq(variables['Architecture'], 'fxdependent')) +``` + +### Scenario 4: Complex Conditions + +```yaml +- task: DoSomething@1 + condition: and( + succeededOrFailed(), + ne(variables['UseAzDevOpsFeed'], ''), + eq(variables['Build.SourceBranch'], 'refs/heads/master') + ) +``` + +### Scenario 5: Built-in Variables + +```yaml +- task: CodeQL3000Init@0 + condition: eq(variables['Build.SourceBranch'], 'refs/heads/master') + +- step: finalize + condition: eq(variables['Agent.JobStatus'], 'SucceededWithIssues') +``` + +### Scenario 6: Parameter vs Variable + +```yaml +parameters: + - name: OfficialBuild + type: boolean + +steps: + # Parameter condition (compile-time) + - task: SignFiles@1 + condition: eq('${{ parameters.OfficialBuild }}', 'true') + + # Variable condition (runtime) + - task: PublishArtifact@1 + condition: eq(variables['PUBLISH_ENABLED'], 'true') +``` + +## Why This Matters + +**String Expansion `$(VAR)` in Conditions:** +- When you use `'$(VAR)'` in a condition, Azure Pipelines attempts to expand it as a string +- If the variable is undefined or empty, it becomes an empty string `''` +- The condition `eq('', 'true')` will always be false +- This makes debugging difficult because there's no error message + +**Variables Array Syntax `variables['VAR']`:** +- This is the proper way to reference runtime variables in conditions +- Azure Pipelines correctly evaluates the variable's value +- Undefined variables are handled properly by the condition evaluator +- This is the standard pattern used throughout Azure Pipelines + +## Reference Examples + +Working examples can be found in: +- `.pipelines/templates/linux.yml` - Build.SourceBranch conditions +- `.pipelines/templates/windows-hosted-build.yml` - Architecture conditions +- `.pipelines/templates/compliance/apiscan.yml` - CODEQL_ENABLED conditions +- `.pipelines/templates/insert-nuget-config-azfeed.yml` - Complex AND/OR conditions + +## Quick Reference Table + +| Context | Syntax | Example | +|---------|--------|---------| +| Condition expression | `variables['Name']` | `condition: eq(variables['PREVIEW'], 'true')` | +| Script content | `$(Name)` | `pwsh: Write-Host "$(PREVIEW)"` | +| Task input | `$(Name)` | `inputs: path: '$(Build.SourcesDirectory)'` | +| Template parameter | `${{ parameters.Name }}` | `condition: eq('${{ parameters.Official }}', 'true')` | + +## Troubleshooting + +### Condition Always False +If your condition is always evaluating to false: +1. Check if you're using `'$(VAR)'` instead of `variables['VAR']` +2. Verify the variable is actually set (add a debug step to print the variable) +3. Check the variable value is exactly what you expect (case-sensitive) + +### Variable Not Found +If you get errors about variables not being found: +1. Ensure the variable is set before the condition is evaluated +2. Check that the variable name is spelled correctly +3. Verify the variable is in scope (job vs. stage vs. pipeline level) + +## Best Practices + +1. **Always use `variables['Name']` in conditions** - This is the correct Azure Pipelines pattern +2. **Use `$(Name)` for string expansion** in scripts and inputs +3. **Use `${{ parameters.Name }}` for template parameters** (compile-time) +4. **Add debug steps** to verify variable values when troubleshooting conditions +5. **Follow existing patterns** in the repository - grep for `condition:` to see examples + +## Common Mistakes + +❌ **Mistake 1: String expansion in condition** +```yaml +condition: eq('$(PREVIEW)', 'true') # WRONG +``` + +✅ **Fix:** +```yaml +condition: eq(variables['PREVIEW'], 'true') # CORRECT +``` + +❌ **Mistake 2: Missing quotes around parameter** +```yaml +condition: eq(${{ parameters.Official }}, true) # WRONG +``` + +✅ **Fix:** +```yaml +condition: eq('${{ parameters.Official }}', 'true') # CORRECT +``` + +❌ **Mistake 3: Mixing syntax** +```yaml +condition: or(eq('$(STABLE)', 'true'), eq(variables['LTS'], 'true')) # INCONSISTENT +``` + +✅ **Fix:** +```yaml +condition: or(eq(variables['STABLE'], 'true'), eq(variables['LTS'], 'true')) # CORRECT +``` diff --git a/.github/instructions/onebranch-restore-phase-pattern.instructions.md b/.github/instructions/onebranch-restore-phase-pattern.instructions.md new file mode 100644 index 00000000000..0945bb47c0b --- /dev/null +++ b/.github/instructions/onebranch-restore-phase-pattern.instructions.md @@ -0,0 +1,83 @@ +--- +applyTo: ".pipelines/**/*.{yml,yaml}" +--- + +# OneBranch Restore Phase Pattern + +## Overview +When steps need to run in the OneBranch restore phase (before the main build phase), the `ob_restore_phase` environment variable must be set in the `env:` block of **each individual step**. + +## Pattern + +### ✅ Correct (Working Pattern) +```yaml +parameters: +- name: "ob_restore_phase" + type: boolean + default: true # or false if you don't want restore phase + +steps: +- powershell: | + # script content + displayName: 'Step Name' + env: + ob_restore_phase: ${{ parameters.ob_restore_phase }} +``` + +The key is to: +1. Define `ob_restore_phase` as a **boolean** parameter +2. Set `ob_restore_phase: ${{ parameters.ob_restore_phase }}` directly in each step's `env:` block +3. Pass `true` to run in restore phase, `false` to run in normal build phase + +### ❌ Incorrect (Does Not Work) +```yaml +steps: +- powershell: | + # script content + displayName: 'Step Name' + ${{ if eq(parameters.useRestorePhase, 'yes') }}: + env: + ob_restore_phase: true +``` + +Using conditionals at the same indentation level as `env:` causes only the first step to execute in restore phase. + +## Parameters + +Templates using this pattern should accept an `ob_restore_phase` boolean parameter: + +```yaml +parameters: +- name: "ob_restore_phase" + type: boolean + default: true # Set to true to run in restore phase by default +``` + +## Reference Examples + +Working examples of this pattern can be found in: +- `.pipelines/templates/insert-nuget-config-azfeed.yml` - Demonstrates the correct pattern +- `.pipelines/templates/SetVersionVariables.yml` - Updated to use this pattern + +## Why This Matters + +The restore phase in OneBranch pipelines runs before signing and other build operations. Steps that need to: +- Set environment variables for the entire build +- Configure authentication +- Prepare the repository structure + +Must run in the restore phase to be available when subsequent stages execute. + +## Common Use Cases + +- Setting `REPOROOT` variable +- Configuring NuGet feeds with authentication +- Setting version variables +- Repository preparation and validation + +## Troubleshooting + +If only the first step in your template is running in restore phase: +1. Check that `env:` block exists for **each step** +2. Verify the conditional `${{ if ... }}:` is **inside** the `env:` block +3. Confirm indentation is correct (conditional is indented under `env:`) diff --git a/.github/instructions/onebranch-signing-configuration.instructions.md b/.github/instructions/onebranch-signing-configuration.instructions.md new file mode 100644 index 00000000000..747fcaffdd6 --- /dev/null +++ b/.github/instructions/onebranch-signing-configuration.instructions.md @@ -0,0 +1,195 @@ +--- +applyTo: + - ".pipelines/**/*.yml" + - ".pipelines/**/*.yaml" +--- + +# OneBranch Signing Configuration + +This guide explains how to configure OneBranch signing variables in Azure Pipeline jobs, particularly when signing is not required. + +## Purpose + +OneBranch pipelines include signing infrastructure by default. For build-only jobs where signing happens in a separate stage, you should disable signing setup to improve performance and avoid unnecessary overhead. + +## Disable Signing for Build-Only Jobs + +When a job does not perform signing (e.g., it only builds artifacts that will be signed in a later stage), disable both signing setup and code sign validation: + +```yaml +variables: + - name: ob_signing_setup_enabled + value: false # Disable signing setup - this is a build-only stage + - name: ob_sdl_codeSignValidation_enabled + value: false # Skip signing validation in build-only stage +``` + +### Why Disable These Variables? + +**`ob_signing_setup_enabled: false`** +- Prevents OneBranch from setting up the signing infrastructure +- Reduces job startup time +- Avoids unnecessary credential validation +- Only disable when the job will NOT sign any artifacts + +**`ob_sdl_codeSignValidation_enabled: false`** +- Skips validation that checks if files are properly signed +- Appropriate for build stages where artifacts are unsigned +- Must be enabled in signing/release stages to validate signatures + +## Common Patterns + +### Build-Only Job (No Signing) + +```yaml +jobs: +- job: build_artifacts + variables: + - name: ob_signing_setup_enabled + value: false + - name: ob_sdl_codeSignValidation_enabled + value: false + steps: + - checkout: self + - pwsh: | + # Build unsigned artifacts + Start-PSBuild +``` + +### Signing Job + +```yaml +jobs: +- job: sign_artifacts + variables: + - name: ob_signing_setup_enabled + value: true + - name: ob_sdl_codeSignValidation_enabled + value: true + steps: + - checkout: self + env: + ob_restore_phase: true # Steps before first signing operation + - pwsh: | + # Prepare artifacts for signing + env: + ob_restore_phase: true # Steps before first signing operation + - task: onebranch.pipeline.signing@1 + displayName: 'Sign artifacts' + # Signing step runs in build phase (no ob_restore_phase) + - pwsh: | + # Post-signing validation + # Post-signing steps run in build phase (no ob_restore_phase) +``` + +## Restore Phase Usage with Signing + +**The restore phase (`ob_restore_phase: true`) should only be used in jobs that perform signing operations.** It separates preparation steps from the actual signing and build steps. + +### When to Use Restore Phase + +Use `ob_restore_phase: true` **only** in jobs where `ob_signing_setup_enabled: true`: + +```yaml +jobs: +- job: sign_artifacts + variables: + - name: ob_signing_setup_enabled + value: true # Signing enabled + steps: + # Steps BEFORE first signing operation: use restore phase + - checkout: self + env: + ob_restore_phase: true + - template: prepare-for-signing.yml + parameters: + ob_restore_phase: true + + # SIGNING STEP: runs in build phase (no ob_restore_phase) + - task: onebranch.pipeline.signing@1 + displayName: 'Sign artifacts' + + # Steps AFTER signing: run in build phase (no ob_restore_phase) + - pwsh: | + # Validation or packaging +``` + +### When NOT to Use Restore Phase + +**Do not use restore phase in build-only jobs** where `ob_signing_setup_enabled: false`: + +```yaml +jobs: +- job: build_artifacts + variables: + - name: ob_signing_setup_enabled + value: false # No signing + - name: ob_sdl_codeSignValidation_enabled + value: false + steps: + - checkout: self + # NO ob_restore_phase - not needed without signing + - pwsh: | + Start-PSBuild +``` + +**Why?** The restore phase is part of OneBranch's signing infrastructure. Using it without signing enabled adds unnecessary overhead without benefit. + +## Related Variables + +Other OneBranch signing-related variables: + +- `ob_sdl_binskim_enabled`: Controls BinSkim security analysis (can be false in build-only, true in signing stages) + +## Best Practices + +1. **Separate build and signing stages**: Build artifacts in one job, sign in another +2. **Disable signing in build stages**: Improves performance and clarifies intent +3. **Only use restore phase with signing**: The restore phase should only be used in jobs where signing is enabled (`ob_signing_setup_enabled: true`) +4. **Restore phase before first signing step**: All steps before the first signing operation should use `ob_restore_phase: true` +5. **Always validate after signing**: Enable validation in signing stages to catch issues +6. **Document the reason**: Add comments explaining why signing is disabled or why restore phase is used + +## Example: Split Build and Sign Pipeline + +```yaml +stages: + - stage: Build + jobs: + - job: build_windows + variables: + - name: ob_signing_setup_enabled + value: false # Build-only, no signing + - name: ob_sdl_codeSignValidation_enabled + value: false # Artifacts are unsigned + steps: + - template: templates/build-unsigned.yml + + - stage: Sign + dependsOn: Build + jobs: + - job: sign_windows + variables: + - name: ob_signing_setup_enabled + value: true # Enable signing infrastructure + - name: ob_sdl_codeSignValidation_enabled + value: true # Validate signatures + steps: + - template: templates/sign-artifacts.yml +``` + +## Troubleshooting + +**Job fails with signing-related errors but signing is disabled:** +- Verify `ob_signing_setup_enabled: false` is set in variables +- Check that no template is overriding the setting +- Ensure `ob_sdl_codeSignValidation_enabled: false` is also set + +**Signed artifacts fail validation:** +- Confirm `ob_sdl_codeSignValidation_enabled: true` in signing job +- Verify signing actually occurred +- Check certificate configuration + +## Reference + +- PowerShell signing templates: `.pipelines/templates/packaging/windows/sign.yml` diff --git a/.github/instructions/powershell-automatic-variables.instructions.md b/.github/instructions/powershell-automatic-variables.instructions.md new file mode 100644 index 00000000000..5015847f41f --- /dev/null +++ b/.github/instructions/powershell-automatic-variables.instructions.md @@ -0,0 +1,159 @@ +--- +applyTo: + - "**/*.ps1" + - "**/*.psm1" +--- + +# PowerShell Automatic Variables - Naming Guidelines + +## Purpose + +This instruction provides guidelines for avoiding conflicts with PowerShell's automatic variables when writing PowerShell scripts and modules. + +## What Are Automatic Variables? + +PowerShell has built-in automatic variables that are created and maintained by PowerShell itself. Assigning values to these variables can cause unexpected behavior and side effects. + +## Common Automatic Variables to Avoid + +### Critical Variables (Never Use) + +- **`$matches`** - Contains the results of regular expression matches. Overwriting this can break regex operations. +- **`$_`** - Represents the current object in the pipeline. Only use within pipeline blocks. +- **`$PSItem`** - Alias for `$_`. Same rules apply. +- **`$args`** - Contains an array of undeclared parameters. Don't use as a regular variable. +- **`$input`** - Contains an enumerator of all input passed to a function. Don't reassign. +- **`$LastExitCode`** - Exit code of the last native command. Don't overwrite unless intentional. +- **`$?`** - Success status of the last command. Don't use as a variable name. +- **`$$`** - Last token in the last line received by the session. Don't use. +- **`$^`** - First token in the last line received by the session. Don't use. + +### Context Variables (Use with Caution) + +- **`$Error`** - Array of error objects. Don't replace, but can modify (e.g., `$Error.Clear()`). +- **`$PSBoundParameters`** - Parameters passed to the current function. Read-only. +- **`$MyInvocation`** - Information about the current command. Read-only. +- **`$PSCmdlet`** - Cmdlet object for advanced functions. Read-only. + +### Other Common Automatic Variables + +- `$true`, `$false`, `$null` - Boolean and null constants +- `$HOME`, `$PSHome`, `$PWD` - Path-related variables +- `$PID` - Process ID of the current PowerShell session +- `$Host` - Host application object +- `$PSVersionTable` - PowerShell version information + +For a complete list, see: https://learn.microsoft.com/powershell/module/microsoft.powershell.core/about/about_automatic_variables + +## Best Practices + +### ❌ Bad - Using Automatic Variable Names + +```powershell +# Bad: $matches is an automatic variable used for regex capture groups +$matches = Select-String -Path $file -Pattern $pattern + +# Bad: $args is an automatic variable for undeclared parameters +$args = Get-ChildItem + +# Bad: $input is an automatic variable for pipeline input +$input = Read-Host "Enter value" +``` + +### ✅ Good - Using Descriptive Alternative Names + +```powershell +# Good: Use descriptive names that avoid conflicts +$matchedLines = Select-String -Path $file -Pattern $pattern + +# Good: Use specific names for arguments +$arguments = Get-ChildItem + +# Good: Use specific names for user input +$userInput = Read-Host "Enter value" +``` + +## Naming Alternatives + +When you encounter a situation where you might use an automatic variable name, use these alternatives: + +| Avoid | Use Instead | +|-------|-------------| +| `$matches` | `$matchedLines`, `$matchResults`, `$regexMatches` | +| `$args` | `$arguments`, `$parameters`, `$commandArgs` | +| `$input` | `$userInput`, `$inputValue`, `$inputData` | +| `$_` (outside pipeline) | Use a named parameter or explicit variable | +| `$Error` (reassignment) | Don't reassign; use `$Error.Clear()` if needed | + +## How to Check + +### PSScriptAnalyzer Rule + +PSScriptAnalyzer has a built-in rule that detects assignments to automatic variables: + +```powershell +# This will trigger PSAvoidAssignmentToAutomaticVariable +$matches = Get-Something +``` + +**Rule ID**: PSAvoidAssignmentToAutomaticVariable + +### Manual Review + +When writing PowerShell code, always: +1. Avoid variable names that match PowerShell keywords or automatic variables +2. Use descriptive, specific names that clearly indicate the variable's purpose +3. Run PSScriptAnalyzer on your code before committing +4. Review code for variable naming during PR reviews + +## Examples from the Codebase + +### Example 1: Regex Matching + +```powershell +# ❌ Bad - Overwrites automatic $matches variable +$matches = [regex]::Matches($content, $pattern) + +# ✅ Good - Uses descriptive name +$regexMatches = [regex]::Matches($content, $pattern) +``` + +### Example 2: Select-String Results + +```powershell +# ❌ Bad - Conflicts with automatic $matches +$matches = Select-String -Path $file -Pattern $pattern + +# ✅ Good - Clear and specific +$matchedLines = Select-String -Path $file -Pattern $pattern +``` + +### Example 3: Collecting Arguments + +```powershell +# ❌ Bad - Conflicts with automatic $args +function Process-Items { + $args = $MyItems + # ... process items +} + +# ✅ Good - Descriptive parameter name +function Process-Items { + [CmdletBinding()] + param( + [Parameter(ValueFromRemainingArguments)] + [string[]]$Items + ) + # ... process items +} +``` + +## References + +- [PowerShell Automatic Variables Documentation](https://learn.microsoft.com/powershell/module/microsoft.powershell.core/about/about_automatic_variables) +- [PSScriptAnalyzer Rules](https://github.com/PowerShell/PSScriptAnalyzer/blob/master/docs/Rules/README.md) +- [PowerShell Best Practices](https://learn.microsoft.com/powershell/scripting/developer/cmdlet/strongly-encouraged-development-guidelines) + +## Summary + +**Key Takeaway**: Always use descriptive, specific variable names that clearly indicate their purpose and avoid conflicts with PowerShell's automatic variables. When in doubt, choose a longer, more descriptive name over a short one that might conflict. diff --git a/.github/instructions/powershell-module-organization.instructions.md b/.github/instructions/powershell-module-organization.instructions.md new file mode 100644 index 00000000000..461d19fb5df --- /dev/null +++ b/.github/instructions/powershell-module-organization.instructions.md @@ -0,0 +1,201 @@ +--- +applyTo: + - "tools/ci.psm1" + - "build.psm1" + - "tools/packaging/**/*.psm1" + - ".github/**/*.yml" + - ".github/**/*.yaml" +--- + +# Guidelines for PowerShell Code Organization + +## When to Move Code from YAML to PowerShell Modules + +PowerShell code in GitHub Actions YAML files should be kept minimal. Move code to a module when: + +### Size Threshold +- **More than ~30 lines** of PowerShell in a YAML file step +- **Any use of .NET types** like `[regex]`, `[System.IO.Path]`, etc. +- **Complex logic** requiring multiple nested loops or conditionals +- **Reusable functionality** that might be needed elsewhere + +### Indicators to Move Code +1. Using .NET type accelerators (`[regex]`, `[PSCustomObject]`, etc.) +2. Complex string manipulation or parsing +3. File system operations beyond basic reads/writes +4. Logic that would benefit from unit testing +5. Code that's difficult to read/maintain in YAML format + +## Which Module to Use + +### ci.psm1 (`tools/ci.psm1`) +**Purpose**: CI/CD-specific operations and workflows + +**Use for**: +- Build orchestration (invoking builds, tests, packaging) +- CI environment setup and configuration +- Test execution and result processing +- Artifact handling and publishing +- CI-specific validations and checks +- Environment variable management for CI + +**Examples**: +- `Invoke-CIBuild` - Orchestrates build process +- `Invoke-CITest` - Runs Pester tests +- `Test-MergeConflictMarker` - Validates files for conflicts +- `Set-BuildVariable` - Manages CI variables + +**When NOT to use**: +- Core build operations (use build.psm1) +- Package creation logic (use packaging.psm1) +- Platform-specific build steps + +### build.psm1 (`build.psm1`) +**Purpose**: Core build operations and utilities + +**Use for**: +- Compiling source code +- Resource generation +- Build configuration management +- Core build utilities (New-PSOptions, Get-PSOutput, etc.) +- Bootstrap operations +- Cross-platform build helpers + +**Examples**: +- `Start-PSBuild` - Main build function +- `Start-PSBootstrap` - Bootstrap dependencies +- `New-PSOptions` - Create build configuration +- `Start-ResGen` - Generate resources + +**When NOT to use**: +- CI workflow orchestration (use ci.psm1) +- Package creation (use packaging.psm1) +- Test execution + +### packaging.psm1 (`tools/packaging/packaging.psm1`) +**Purpose**: Package creation and distribution + +**Use for**: +- Creating distribution packages (MSI, RPM, DEB, etc.) +- Package-specific metadata generation +- Package signing operations +- Platform-specific packaging logic + +**Examples**: +- `Start-PSPackage` - Create packages +- `New-MSIPackage` - Create Windows MSI +- `New-DotnetSdkContainerFxdPackage` - Create container packages + +**When NOT to use**: +- Building binaries (use build.psm1) +- Running tests (use ci.psm1) +- General utilities + +## Best Practices + +### Keep YAML Minimal +```yaml +# ❌ Bad - too much logic in YAML +- name: Check files + shell: pwsh + run: | + $files = Get-ChildItem -Recurse + foreach ($file in $files) { + $content = Get-Content $file -Raw + if ($content -match $pattern) { + # ... complex processing ... + } + } + +# ✅ Good - call function from module +- name: Check files + shell: pwsh + run: | + Import-Module ./tools/ci.psm1 + Test-SomeCondition -Path ${{ github.workspace }} +``` + +### Document Functions +Always include comment-based help for functions: +```powershell +function Test-MyFunction +{ + <# + .SYNOPSIS + Brief description + .DESCRIPTION + Detailed description + .PARAMETER ParameterName + Parameter description + .EXAMPLE + Test-MyFunction -ParameterName Value + #> + [CmdletBinding()] + param( + [Parameter(Mandatory)] + [string] $ParameterName + ) + # Implementation +} +``` + +### Error Handling +Use proper error handling in modules: +```powershell +try { + # Operation +} +catch { + Write-Error "Detailed error message: $_" + throw +} +``` + +### Verbose Output +Use `Write-Verbose` for debugging information: +```powershell +Write-Verbose "Processing file: $filePath" +``` + +## Module Dependencies + +- **ci.psm1** imports both `build.psm1` and `packaging.psm1` +- **build.psm1** is standalone (minimal dependencies) +- **packaging.psm1** imports `build.psm1` + +When adding new functions, consider these import relationships to avoid circular dependencies. + +## Testing Modules + +Functions in modules should be testable: +```powershell +# Test locally +Import-Module ./tools/ci.psm1 -Force +Test-MyFunction -Parameter Value + +# Can be unit tested with Pester +Describe "Test-MyFunction" { + It "Should return expected result" { + # Test implementation + } +} +``` + +## Migration Checklist + +When moving code from YAML to a module: + +1. ✅ Determine which module is appropriate (ci, build, or packaging) +2. ✅ Create function with proper parameter validation +3. ✅ Add comment-based help documentation +4. ✅ Use `[CmdletBinding()]` for advanced function features +5. ✅ Include error handling +6. ✅ Add verbose output for debugging +7. ✅ Test the function independently +8. ✅ Update YAML to call the new function +9. ✅ Verify the workflow still works end-to-end + +## References + +- PowerShell Advanced Functions: https://learn.microsoft.com/powershell/module/microsoft.powershell.core/about/about_functions_advanced +- Comment-Based Help: https://learn.microsoft.com/powershell/scripting/developer/help/writing-help-for-windows-powershell-scripts-and-functions diff --git a/.github/instructions/powershell-parameter-naming.instructions.md b/.github/instructions/powershell-parameter-naming.instructions.md new file mode 100644 index 00000000000..155fd1a85c3 --- /dev/null +++ b/.github/instructions/powershell-parameter-naming.instructions.md @@ -0,0 +1,69 @@ +--- +applyTo: '**/*.ps1, **/*.psm1' +description: Naming conventions for PowerShell parameters +--- + +# PowerShell Parameter Naming Conventions + +## Purpose + +This instruction defines the naming conventions for parameters in PowerShell scripts and modules. Consistent parameter naming improves code readability, maintainability, and usability for users of PowerShell cmdlets and functions. + +## Parameter Naming Rules + +### General Conventions +- **Singular Nouns**: Use singular nouns for parameter names even if the parameter is expected to handle multiple values (e.g., `File` instead of `Files`). +- **Use PascalCase**: Parameter names must use PascalCase (e.g., `ParameterName`). +- **Descriptive Names**: Parameter names should be descriptive and convey their purpose clearly (e.g., `FilePath`, `UserName`). +- **Avoid Abbreviations**: Avoid using abbreviations unless they are widely recognized (e.g., `ID` for Identifier). +- **Avoid Reserved Words**: Do not use PowerShell reserved words as parameter names (e.g., `if`, `else`, `function`). + +### Units and Precision +- **Include Units in Parameter Names**: When a parameter represents a value with units, include the unit in the parameter name for clarity: + - `TimeoutSec` instead of `Timeout` + - `RetryIntervalSec` instead of `RetryInterval` + - `MaxSizeBytes` instead of `MaxSize` +- **Use Full Words for Clarity**: Spell out common terms to match PowerShell conventions: + - `MaximumRetryCount` instead of `MaxRetries` + - `MinimumLength` instead of `MinLength` + +### Alignment with Built-in Cmdlets +- **Follow Existing PowerShell Conventions**: When your parameter serves a similar purpose to a built-in cmdlet parameter, use the same or similar naming: + - Match `Invoke-WebRequest` parameters when making HTTP requests: `TimeoutSec`, `MaximumRetryCount`, `RetryIntervalSec` + - Follow common parameter patterns like `Path`, `Force`, `Recurse`, `WhatIf`, `Confirm` +- **Consistency Within Scripts**: If multiple parameters relate to the same concept, use consistent naming patterns (e.g., `TimeoutSec`, `RetryIntervalSec` both use `Sec` suffix). + +## Examples + +### Good Parameter Names +```powershell +param( + [string[]]$File, # Singular, even though it accepts arrays + [int]$TimeoutSec = 30, # Unit included + [int]$MaximumRetryCount = 2, # Full word "Maximum" + [int]$RetryIntervalSec = 2, # Consistent with TimeoutSec + [string]$Path, # Standard PowerShell convention + [switch]$Force # Common PowerShell parameter +) +``` + +### Names to Avoid +```powershell +param( + [string[]]$Files, # Should be singular: File + [int]$Timeout = 30, # Missing unit: TimeoutSec + [int]$MaxRetries = 2, # Should be: MaximumRetryCount + [int]$RetryInterval = 2, # Missing unit: RetryIntervalSec + [string]$FileLoc, # Avoid abbreviations: FilePath + [int]$Max # Ambiguous: MaximumWhat? +) +``` + +## Exceptions +- **Common Terms**: Some common terms may be used in plural form if they are widely accepted in the context (e.g., `Credentials`, `Permissions`). +- **Legacy Code**: Existing code that does not follow these conventions may be exempted to avoid breaking changes, but new code should adhere to these guidelines. +- **Well Established Naming Patterns**: If a naming pattern is well established in the PowerShell community, it may be used even if it does not strictly adhere to these guidelines. + +## References +- [PowerShell Cmdlet Design Guidelines](https://learn.microsoft.com/powershell/scripting/developer/cmdlet/strongly-encouraged-development-guidelines) +- [About Parameters - PowerShell Documentation](https://learn.microsoft.com/powershell/module/microsoft.powershell.core/about/about_parameters) diff --git a/.github/instructions/publishing-pester-result.instructions.md b/.github/instructions/publishing-pester-result.instructions.md new file mode 100644 index 00000000000..49010e65a99 --- /dev/null +++ b/.github/instructions/publishing-pester-result.instructions.md @@ -0,0 +1,272 @@ +--- +applyTo: ".github/**/*.{yml,yaml}" +--- + +# Publishing Pester Test Results Instructions + +This document describes how the PowerShell repository uses GitHub Actions to publish Pester test results. + +## Overview + +The PowerShell repository uses a custom composite GitHub Action located at `.github/actions/test/process-pester-results` to process and publish Pester test results in CI/CD workflows. +This action aggregates test results from NUnitXml formatted files, creates a summary in the GitHub Actions job summary, and uploads the results as artifacts. + +## How It Works + +### Action Location and Structure + +**Path**: `.github/actions/test/process-pester-results/` + +The action consists of two main files: + +1. **action.yml** - The composite action definition +1. **process-pester-results.ps1** - PowerShell script that processes test results + +### Action Inputs + +The action accepts the following inputs: + +- **name** (required): A descriptive name for the test run (e.g., "UnelevatedPesterTests-CI") + - Used for naming the uploaded artifact and in the summary + - Format: `junit-pester-{name}` + +- **testResultsFolder** (optional): Path to the folder containing test result XML files + - Default: `${{ runner.workspace }}/testResults` + - The script searches for all `*.xml` files in this folder recursively + +### Action Workflow + +The action performs the following steps: + +1. **Process Test Results** + - Runs `process-pester-results.ps1` with the provided name and test results folder + - Parses all NUnitXml formatted test result files (`*.xml`) + - Aggregates test statistics across all files: + - Total test cases + - Errors + - Failures + - Not run tests + - Inconclusive tests + - Ignored tests + - Skipped tests + - Invalid tests + +1. **Generate Summary** + - Creates a markdown summary using the `$GITHUB_STEP_SUMMARY` environment variable + - Uses `Write-Log` and `Write-LogGroupStart`/`Write-LogGroupEnd` functions from `build.psm1` + - Outputs a formatted summary with all test statistics + - Example format: + + ```markdown + # Summary of {Name} + + - Total Tests: X + - Total Errors: X + - Total Failures: X + - Total Not Run: X + - Total Inconclusive: X + - Total Ignored: X + - Total Skipped: X + - Total Invalid: X + ``` + +1. **Upload Artifacts** + - Uses `actions/upload-artifact@v4` to upload test results + - Artifact name: `junit-pester-{name}` + - Always runs (even if previous steps fail) via `if: always()` + - Uploads the entire test results folder + +1. **Exit Status** + - Fails the job (exit 1) if: + - Any test errors occurred (`$testErrorCount -gt 0`) + - Any test failures occurred (`$testFailureCount -gt 0`) + - No test cases were run (`$testCaseCount -eq 0`) + +## Usage in Test Actions + +The `process-pester-results` action is called by two platform-specific composite test actions: + +### Linux/macOS Tests: `.github/actions/test/nix` + +Used in: + +- `.github/workflows/linux-ci.yml` +- `.github/workflows/macos-ci.yml` + +Example usage (lines 99-104 in `nix/action.yml`): + +```yaml +- name: Convert, Publish, and Upload Pester Test Results + uses: "./.github/actions/test/process-pester-results" + with: + name: "${{ inputs.purpose }}-${{ inputs.tagSet }}" + testResultsFolder: "${{ runner.workspace }}/testResults" +``` + +### Windows Tests: `.github/actions/test/windows` + +Used in: + +- `.github/workflows/windows-ci.yml` + +Example usage (line 78-83 in `windows/action.yml`): + +```yaml +- name: Convert, Publish, and Upload Pester Test Results + uses: "./.github/actions/test/process-pester-results" + with: + name: "${{ inputs.purpose }}-${{ inputs.tagSet }}" + testResultsFolder: ${{ runner.workspace }}\testResults +``` + +## Workflow Integration + +The process-pester-results action is integrated into the CI workflows through a multi-level hierarchy: + +### Level 1: Main CI Workflows + +- `linux-ci.yml` +- `macos-ci.yml` +- `windows-ci.yml` + +### Level 2: Test Jobs + +Each workflow contains multiple test jobs with different purposes and tag sets: + +- `UnelevatedPesterTests` with tagSet `CI` +- `ElevatedPesterTests` with tagSet `CI` +- `UnelevatedPesterTests` with tagSet `Others` +- `ElevatedPesterTests` with tagSet `Others` + +### Level 3: Platform Test Actions + +Test jobs use platform-specific actions: + +- `nix` for Linux and macOS +- `windows` for Windows + +### Level 4: Process Results Action + +Platform actions call `process-pester-results` to publish results + +## Test Execution Flow + +1. **Build Phase**: Source code is built (e.g., in `ci_build` job) +1. **Test Preparation**: + - Build artifacts are downloaded + - PowerShell is bootstrapped + - Test binaries are extracted +1. **Test Execution**: + - `Invoke-CITest` is called with: + - `-Purpose`: Test purpose (e.g., "UnelevatedPesterTests") + - `-TagSet`: Test category (e.g., "CI", "Others") + - `-OutputFormat NUnitXml`: Results format + - Results are written to `${{ runner.workspace }}/testResults` +1. **Results Processing**: + - `process-pester-results` action runs + - Results are aggregated and summarized + - Artifacts are uploaded + - Job fails if any tests failed or errored + +## Key Dependencies + +### PowerShell Modules + +- **build.psm1**: Provides utility functions + - `Write-Log`: Logging function with GitHub Actions support + - `Write-LogGroupStart`: Creates collapsible log groups + - `Write-LogGroupEnd`: Closes collapsible log groups + +### GitHub Actions Features + +- **GITHUB_STEP_SUMMARY**: Environment variable for job summary +- **actions/upload-artifact@v4**: For uploading test results +- **Composite Actions**: For reusable workflow steps + +### Test Result Format + +- **NUnitXml**: XML format for test results +- Expected XML structure with `test-results` root element containing: + - `total`: Total number of tests + - `errors`: Number of errors + - `failures`: Number of failures + - `not-run`: Number of tests not run + - `inconclusive`: Number of inconclusive tests + - `ignored`: Number of ignored tests + - `skipped`: Number of skipped tests + - `invalid`: Number of invalid tests + +## Best Practices + +1. **Naming Convention**: Use descriptive names that include both purpose and tagSet: + - Format: `{purpose}-{tagSet}` + - Example: `UnelevatedPesterTests-CI` + +1. **Test Results Location**: + - Default location: `${{ runner.workspace }}/testResults` + - Use platform-appropriate path separators (Windows: `\`, Unix: `/`) + +1. **Always Upload**: The artifact upload step uses `if: always()` to ensure results are uploaded even when tests fail + +1. **Error Handling**: The action will fail the job if: + - Tests have errors or failures (intentional fail-fast behavior) + - No tests were executed (potential configuration issue) + - `GITHUB_STEP_SUMMARY` is not set (environment issue) + +## Customizing for Your Repository + +To use this pattern in another repository: + +1. **Copy the Action Files**: + - Copy `.github/actions/test/process-pester-results/` directory + - Ensure the PowerShell script has proper permissions + +1. **Adjust Dependencies**: + - Modify or remove the `Import-Module "$PSScriptRoot/../../../../build.psm1"` line + - Implement equivalent `Write-Log` and `Write-LogGroup*` functions if needed + +1. **Customize Summary Format**: + - Modify the here-string in `process-pester-results.ps1` to change summary format + - Add additional metrics or formatting as needed + +1. **Call from Your Workflows**: + + ```yaml + - name: Process Test Results + uses: "./.github/actions/test/process-pester-results" + with: + name: "my-test-run" + testResultsFolder: "path/to/results" + ``` + +## Related Documentation + +- [GitHub Actions: Creating composite actions](https://docs.github.com/en/actions/creating-actions/creating-a-composite-action) +- [GitHub Actions: Job summaries](https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions#adding-a-job-summary) +- [GitHub Actions: Uploading artifacts](https://docs.github.com/en/actions/using-workflows/storing-workflow-data-as-artifacts) +- [Pester: PowerShell testing framework](https://pester.dev/) +- [NUnit XML Format](https://docs.nunit.org/articles/nunit/technical-notes/usage/Test-Result-XML-Format.html) + +## Troubleshooting + +### No Test Results Found + +- Verify `testResultsFolder` path is correct +- Ensure tests are generating NUnitXml formatted output +- Check that `*.xml` files exist in the specified folder + +### Action Fails with "GITHUB_STEP_SUMMARY is not set" + +- Ensure the action runs within a GitHub Actions environment +- Cannot be run locally without mocking this environment variable + +### All Tests Pass but Job Fails + +- Check if any tests are marked as errors (different from failures) +- Verify that at least some tests executed (`$testCaseCount -eq 0`) + +### Artifact Upload Fails + +- Check artifact name for invalid characters +- Ensure the test results folder exists +- Verify actions/upload-artifact version compatibility diff --git a/.github/instructions/start-native-execution.instructions.md b/.github/instructions/start-native-execution.instructions.md new file mode 100644 index 00000000000..347e496b3bf --- /dev/null +++ b/.github/instructions/start-native-execution.instructions.md @@ -0,0 +1,149 @@ +--- +applyTo: + - "**/*.ps1" + - "**/*.psm1" +--- + +# Using Start-NativeExecution for Native Command Execution + +## Purpose + +`Start-NativeExecution` is the standard function for executing native commands (external executables) in PowerShell scripts within this repository. It provides consistent error handling and better diagnostics when native commands fail. + +## When to Use + +Use `Start-NativeExecution` whenever you need to: +- Execute external commands (e.g., `git`, `dotnet`, `pkgbuild`, `productbuild`, `fpm`, `rpmbuild`) +- Ensure proper exit code checking +- Get better error messages with caller information +- Handle verbose output on error + +## Basic Usage + +```powershell +Start-NativeExecution { + git clone https://github.com/PowerShell/PowerShell.git +} +``` + +## With Parameters + +Use backticks for line continuation within the script block: + +```powershell +Start-NativeExecution { + pkgbuild --root $pkgRoot ` + --identifier $pkgIdentifier ` + --version $Version ` + --scripts $scriptsDir ` + $outputPath +} +``` + +## Common Parameters + +### -VerboseOutputOnError + +Captures command output and displays it only if the command fails: + +```powershell +Start-NativeExecution -VerboseOutputOnError { + dotnet build --configuration Release +} +``` + +### -IgnoreExitcode + +Allows the command to fail without throwing an exception: + +```powershell +Start-NativeExecution -IgnoreExitcode { + git diff --exit-code # Returns 1 if differences exist +} +``` + +## Availability + +The function is defined in `tools/buildCommon/startNativeExecution.ps1` and is available in: +- `build.psm1` (dot-sourced automatically) +- `tools/packaging/packaging.psm1` (dot-sourced automatically) +- Test modules that include `HelpersCommon.psm1` + +To use in other scripts, dot-source the function: + +```powershell +. "$PSScriptRoot/../buildCommon/startNativeExecution.ps1" +``` + +## Error Handling + +When a native command fails (non-zero exit code), `Start-NativeExecution`: +1. Captures the exit code +2. Identifies the calling location (file and line number) +3. Throws a descriptive error with full context + +Example error message: +``` +Execution of {git clone ...} by /path/to/script.ps1: line 42 failed with exit code 1 +``` + +## Examples from the Codebase + +### Git Operations +```powershell +Start-NativeExecution { + git fetch --tags --quiet upstream +} +``` + +### Build Operations +```powershell +Start-NativeExecution -VerboseOutputOnError { + dotnet publish --configuration Release +} +``` + +### Packaging Operations +```powershell +Start-NativeExecution -VerboseOutputOnError { + pkgbuild --root $pkgRoot --identifier $pkgId --version $version $outputPath +} +``` + +### Permission Changes +```powershell +Start-NativeExecution { + find $staging -type d | xargs chmod 755 + find $staging -type f | xargs chmod 644 +} +``` + +## Anti-Patterns + +**Don't do this:** +```powershell +& somecommand $args +if ($LASTEXITCODE -ne 0) { + throw "Command failed" +} +``` + +**Do this instead:** +```powershell +Start-NativeExecution { + somecommand $args +} +``` + +## Best Practices + +1. **Always use Start-NativeExecution** for native commands to ensure consistent error handling +2. **Use -VerboseOutputOnError** for commands with useful diagnostic output +3. **Use backticks for readability** when commands have multiple arguments +4. **Don't capture output unnecessarily** - let the function handle it +5. **Use -IgnoreExitcode sparingly** - only when non-zero exit codes are expected and acceptable + +## Related Documentation + +- Source: `tools/buildCommon/startNativeExecution.ps1` +- Blog post: https://mnaoumov.wordpress.com/2015/01/11/execution-of-external-commands-in-powershell-done-right/ diff --git a/.github/instructions/start-psbuild-basics.instructions.md b/.github/instructions/start-psbuild-basics.instructions.md new file mode 100644 index 00000000000..18a0026eb2d --- /dev/null +++ b/.github/instructions/start-psbuild-basics.instructions.md @@ -0,0 +1,100 @@ +--- +applyTo: + - "build.psm1" + - "tools/ci.psm1" + - ".github/**/*.yml" + - ".github/**/*.yaml" +--- + +# Start-PSBuild Basics + +## Purpose + +`Start-PSBuild` builds PowerShell from source. It's defined in `build.psm1` and used in CI/CD workflows. + +## Default Usage + +For most scenarios, use with no parameters: + +```powershell +Import-Module ./tools/ci.psm1 +Start-PSBuild +``` + +**Default behavior:** +- Configuration: `Debug` +- PSModuleRestore: Enabled +- Runtime: Auto-detected for platform + +## Common Configurations + +### Debug Build (Default) + +```powershell +Start-PSBuild +``` + +Use for: +- Testing (xUnit, Pester) +- Development +- Debugging + +### Release Build + +```powershell +Start-PSBuild -Configuration 'Release' +``` + +Use for: +- Production packages +- Distribution +- Performance testing + +### Code Coverage Build + +```powershell +Start-PSBuild -Configuration 'CodeCoverage' +``` + +Use for: +- Code coverage analysis +- Test coverage reports + +## Common Parameters + +### -Configuration + +Values: `Debug`, `Release`, `CodeCoverage`, `StaticAnalysis` + +Default: `Debug` + +### -CI + +Restores Pester module for CI environments. + +```powershell +Start-PSBuild -CI +``` + +### -PSModuleRestore + +Now enabled by default. Use `-NoPSModuleRestore` to skip. + +### -ReleaseTag + +Specifies version tag for release builds: + +```powershell +$releaseTag = Get-ReleaseTag +Start-PSBuild -Configuration 'Release' -ReleaseTag $releaseTag +``` + +## Workflow Example + +```yaml +- name: Build PowerShell + shell: pwsh + run: | + Import-Module ./tools/ci.psm1 + Start-PSBuild +``` diff --git a/.github/instructions/troubleshooting-builds.instructions.md b/.github/instructions/troubleshooting-builds.instructions.md new file mode 100644 index 00000000000..e9b60cb8c80 --- /dev/null +++ b/.github/instructions/troubleshooting-builds.instructions.md @@ -0,0 +1,100 @@ +--- +applyTo: + - "build.psm1" + - "tools/ci.psm1" + - ".github/**/*.yml" + - ".github/**/*.yaml" +--- + +# Troubleshooting Build Issues + +## Git Describe Error + +**Error:** +``` +error MSB3073: The command "git describe --abbrev=60 --long" exited with code 128. +``` + +**Cause:** Insufficient git history (shallow clone) + +**Solution:** Add `fetch-depth: 1000` to checkout step + +```yaml +- name: Checkout + uses: actions/checkout@v4 + with: + fetch-depth: 1000 +``` + +## Version Information Incorrect + +**Symptom:** Build produces wrong version numbers + +**Cause:** Git tags not synchronized + +**Solution:** Run `Sync-PSTags -AddRemoteIfMissing`: + +```yaml +- name: Bootstrap + shell: pwsh + run: | + Import-Module ./tools/ci.psm1 + Invoke-CIInstall -SkipUser + Sync-PSTags -AddRemoteIfMissing +``` + +## PowerShell Binary Not Built + +**Error:** +``` +Exception: CoreCLR pwsh.exe was not built +``` + +**Causes:** +1. Build failed (check logs) +2. Wrong configuration used +3. Build output location incorrect + +**Solutions:** +1. Check build logs for errors +2. Verify correct configuration for use case +3. Use default parameters: `Start-PSBuild` + +## Module Restore Issues + +**Symptom:** Slow build or module restore failures + +**Causes:** +- Network issues +- Module cache problems +- Package source unavailable + +**Solutions:** +1. Retry the build +2. Check network connectivity +3. Use `-NoPSModuleRestore` if modules not needed +4. Clear package cache if persistent + +## .NET SDK Not Found + +**Symptom:** Build can't find .NET SDK + +**Solution:** Ensure .NET setup step runs first: + +```yaml +- name: Setup .NET + uses: actions/setup-dotnet@v4 + with: + global-json-file: ./global.json +``` + +## Bootstrap Failures + +**Symptom:** Invoke-CIInstall fails + +**Causes:** +- Missing dependencies +- Network issues +- Platform-specific requirements not met + +**Solution:** Check prerequisites for your platform in build system docs diff --git a/.github/issue_label_bot.yaml b/.github/issue_label_bot.yaml new file mode 100644 index 00000000000..4374b1996b5 --- /dev/null +++ b/.github/issue_label_bot.yaml @@ -0,0 +1,4 @@ +label-alias: + bug: 'Issue-Bug' + feature_request: 'Issue-Enhancement' + question: 'Issue-Question' diff --git a/.github/policies/IssueManagement.CloseResolutions.yml b/.github/policies/IssueManagement.CloseResolutions.yml new file mode 100644 index 00000000000..23ab9422e1a --- /dev/null +++ b/.github/policies/IssueManagement.CloseResolutions.yml @@ -0,0 +1,137 @@ +id: CloseResolutionTags +name: GitOps.PullRequestIssueManagement +description: Closing issues with Resolution* +owner: +resource: repository +disabled: false +where: +configuration: + resourceManagementConfiguration: + scheduledSearches: + - description: Close if marked as Resolution-Declined after one day of no activity + frequencies: + - hourly: + hour: 12 + filters: + - isIssue + - isOpen + - hasLabel: + label: Resolution-Declined + - noActivitySince: + days: 1 + actions: + - addReply: + reply: This issue has been marked as declined and has not had any activity for **1 day**. It has been closed for housekeeping purposes. + - closeIssue + + - description: Close if marked as Resolution-By Design after one day of no activity + frequencies: + - hourly: + hour: 12 + filters: + - isIssue + - isOpen + - hasLabel: + label: Resolution-By Design + - noActivitySince: + days: 1 + actions: + - addReply: + reply: This issue has been marked as by-design and has not had any activity for **1 day**. It has been closed for housekeeping purposes. + - closeIssue + + - description: Close if marked as Resolution-Won't Fix after one day of no activity + frequencies: + - hourly: + hour: 12 + filters: + - isIssue + - isOpen + - hasLabel: + label: Resolution-Won't Fix + - noActivitySince: + days: 1 + actions: + - addReply: + reply: This issue has been marked as won't fix and has not had any activity for **1 day**. It has been closed for housekeeping purposes. + - closeIssue + + - description: Close if marked as Resolution-No Activity after seven day of no activity, no reply + frequencies: + - hourly: + hour: 12 + filters: + - isOpen + - isIssue + - hasLabel: + label: Resolution-No Activity + - noActivitySince: + days: 7 + actions: + - closeIssue + + - description: Close if marked as Resolution-Duplicate after one day of no activity + frequencies: + - hourly: + hour: 3 + filters: + - isIssue + - isOpen + - hasLabel: + label: Resolution-Duplicate + - noActivitySince: + days: 1 + actions: + - addReply: + reply: This issue has been marked as duplicate and has not had any activity for **1 day**. It has been closed for housekeeping purposes. + - closeIssue + + - description: Close if marked as Resolution-External after one day of no activity + frequencies: + - hourly: + hour: 3 + filters: + - isIssue + - isOpen + - hasLabel: + label: Resolution-External + - noActivitySince: + days: 1 + actions: + - addReply: + reply: This issue has been marked as external and has not had any activity for **1 day**. It has been be closed for housekeeping purposes. + - closeIssue + + - description: Close if marked as Resolution-Answered after one day of no activity + frequencies: + - hourly: + hour: 12 + filters: + - isIssue + - isOpen + - hasLabel: + label: Resolution-Answered + - noActivitySince: + days: 1 + actions: + - addReply: + reply: This issue has been marked as answered and has not had any activity for **1 day**. It has been closed for housekeeping purposes. + - closeIssue + + - description: Close if marked as Resolution-Fixed after one day of no activity + frequencies: + - hourly: + hour: 12 + filters: + - isIssue + - isOpen + - hasLabel: + label: Resolution-Fixed + - noActivitySince: + days: 1 + actions: + - addReply: + reply: This issue has been marked as fixed and has not had any activity for **1 day**. It has been closed for housekeeping purposes. + - closeIssue +onFailure: +onSuccess: diff --git a/.github/policies/IssueManagement.ResolveStale.yml b/.github/policies/IssueManagement.ResolveStale.yml new file mode 100644 index 00000000000..fd254715ea9 --- /dev/null +++ b/.github/policies/IssueManagement.ResolveStale.yml @@ -0,0 +1,109 @@ +id: IssueManagement.ResolveStale +name: GitOps.PullRequestIssueManagement +description: Other issue management rules for closing stale and waiting on author requests +owner: +resource: repository +disabled: false +where: +configuration: + resourceManagementConfiguration: + scheduledSearches: + - description: Close if marked as Waiting on Author and no activity in 7 days + frequencies: + - hourly: + hour: 12 + filters: + - isOpen + - isIssue + - hasLabel: + label: Waiting on Author + - noActivitySince: + days: 7 + actions: + - addReply: + reply: This issue has been marked as "Waiting on Author" and has not had any activity for **7 day**. It has been closed for housekeeping purposes. + - closeIssue + + - description: Label as Resolution-No Activity if not labeled with KeepOpen and no activity in 6 months + frequencies: + - hourly: + hour: 24 + filters: + - isIssue + - isOpen + - isNotLabeledWith: + label: KeepOpen + - isNotLabeledWith: + label: In-PR + - isNotLabeledWith: + label: Needs-Triage + - isNotLabeledWith: + label: Resolution-No Activity + - isNotLabeledWith: + label: Issue-Meta + - isNotLabeledWith: + label: Review - Needed + - isNotLabeledWith: + label: Review - Committee + - isNotLabeledWith: + label: Review - Maintainer + - isNotLabeledWith: + label: WG-NeedsReview + # Up for grabs labeled issues will get closed after a 6 months of no activity unless KeepOpen label is included + - noActivitySince: + days: 180 + actions: + - addLabel: + label: Resolution-No Activity + - addReply: + reply: "This issue has not had any activity in 6 months, if there is no further activity in 7 days, the issue will be closed automatically.\n\nActivity in this case refers only to comments on the issue. If the issue is closed and you are the author, you can re-open the issue using the button below. Please add more information to be considered during retriage. If you are not the author but the issue is impacting you after it has been closed, please submit a new issue with updated details and a link to this issue and the original." + eventResponderTasks: + - description: Remove no resolution label if anyone comments while in 7 day window + if: + - payloadType: Issue_Comment + - hasLabel: + label: Resolution-No Activity + - isOpen + then: + - removeLabel: + label: Resolution-No Activity + + - description: If new issue comment is author then remove waiting on author + if: + - payloadType: Issue_Comment + - isActivitySender: + issueAuthor: True + - hasLabel: + label: Waiting on Author + then: + - removeLabel: + label: Waiting on Author + + - description: Remove Stale label if issue comment + if: + - payloadType: Issue_Comment + - hasLabel: + label: Stale + then: + - removeLabel: + label: Stale + + - description: Remove Needs-Triage label if issue is closed + if: + - payloadType: Issues + - isAction: + action: Closed + then: + - removeLabel: + label: Needs-Triage + + - description: Remove Keep Open label if closed by someone + if: + - payloadType: Issues + - isAction: + action: Closed + then: + - removeLabel: + label: KeepOpen +onFailure: +onSuccess: diff --git a/.github/policies/PRManagement.yml b/.github/policies/PRManagement.yml new file mode 100644 index 00000000000..9deaf0262bb --- /dev/null +++ b/.github/policies/PRManagement.yml @@ -0,0 +1,226 @@ +id: PRManagement +name: GitOps.PullRequestIssueManagement +description: Collection of PR bot triaging behaviors +owner: +resource: repository +disabled: false +where: +configuration: + resourceManagementConfiguration: + scheduledSearches: + - description: If Stale label and waiting on author and no activity since 10 days then close the PR + frequencies: + - hourly: + hour: 12 + filters: + - isPullRequest + - isOpen + - hasLabel: + label: Waiting on Author + - hasLabel: + label: Stale + - noActivitySince: + days: 10 + actions: + - closeIssue + + - description: If PR has Waiting on Author label and no activity in 15 days label as stale. + frequencies: + - hourly: + hour: 3 + filters: + - isPullRequest + - isOpen + - hasLabel: + label: Waiting on Author + - noActivitySince: + days: 15 + - isNotLabeledWith: + label: Stale + actions: + - addLabel: + label: Stale + - addReply: + reply: This pull request has been automatically marked as stale because it has been marked as requiring author feedback but has not had any activity for **15 days**. It will be closed if no further activity occurs **within 10 days of this comment**. + + - description: Label Review - Needed if PR is opened an no activity in 7 days but no other labels on it + frequencies: + - hourly: + hour: 12 + filters: + - isPullRequest + - isOpen + - isNotLabeledWith: + label: Waiting on Author + - noActivitySince: + days: 7 + - isNotLabeledWith: + label: Stale + - isNotLabeledWith: + label: Review - Needed + - isNotLabeledWith: + label: Review - Committee + - isNotDraftPullRequest + actions: + - addLabel: + label: Review - Needed + - addReply: + reply: >- + This pull request has been automatically marked as Review Needed because it has been there has not been any activity for **7 days**. + + Maintainer, please provide feedback and/or mark it as `Waiting on Author` + + - description: Add waiting on Author label if is draft PR, if no activity label + frequencies: + - hourly: + hour: 12 + filters: + - isOpen + - isDraftPullRequest + - isNotLabeledWith: + label: Review - Committee + - isNotLabeledWith: + label: Waiting on Author + - isNotLabeledWith: + label: Stale + - noActivitySince: + days: 3 + actions: + - addLabel: + label: Waiting on Author + eventResponderTasks: + + - description: If PR has AutoMerge Label then enable Automerge to squash + if: + - payloadType: Pull_Request + - hasLabel: + label: AutoMerge + then: + - enableAutoMerge: + mergeMethod: Squash + + - description: If PR has label AutoMerge Removed then disable Automerge + if: + - payloadType: Pull_Request + - labelRemoved: + label: AutoMerge + then: + - disableAutoMerge + + - description: If PR review requests changes then add label waiting on Author and remove review needed + if: + - payloadType: Pull_Request_Review + - isAction: + action: Submitted + - isReviewState: + reviewState: Changes_requested + then: + - addLabel: + label: Waiting on Author + - removeLabel: + label: Review - Needed + + - description: Remove Waiting on author if has label and activity from author + if: + - payloadType: Pull_Request + - isActivitySender: + issueAuthor: True + - not: + isAction: + action: Closed + - hasLabel: + label: Waiting on Author + - not: + titleContains: + pattern: "(WIP|Work in progress|\U0001F6A7)" + isRegex: True + then: + - removeLabel: + label: Waiting on Author + + - description: remove waiting on author if review by author and has waiting on author + if: + - payloadType: Pull_Request_Review + - isActivitySender: + issueAuthor: True + - hasLabel: + label: Waiting on Author + then: + - removeLabel: + label: Waiting on Author + + - description: Remove Stale label if PR has activity from author which is not closure + if: + - payloadType: Pull_Request + - not: + isAction: + action: Closed + - hasLabel: + label: Stale + - isActivitySender: + issueAuthor: True + then: + - removeLabel: + label: Stale + + - description: Remove Stale label if PR is reviewed + if: + - payloadType: Pull_Request_Review + - hasLabel: + label: Stale + then: + - removeLabel: + label: Stale + + - description: Remove Review Needed if PR is created or done any action by Admins and iSazonov + if: + - payloadType: Pull_Request + - hasLabel: + label: Review - Needed + - or: + - isAction: + action: Null + - isAction: + action: Closed + - isAction: + action: Reopened + - isAction: + action: Assigned + - isAction: + action: Unassigned + - isAction: + action: Unlabeled + - or: + - activitySenderHasPermission: + permission: Admin + - isActivitySender: + user: iSazonov + issueAuthor: False + then: + - removeLabel: + label: Review - Needed + + - description: Remove Review - Needed if issue comment is by admin or iSazonov + if: + - payloadType: Issue_Comment + - hasLabel: + label: Review - Needed + - or: + - activitySenderHasPermission: + permission: Admin + - isActivitySender: + user: iSazonov + issueAuthor: False + then: + - removeLabel: + label: Review - Needed + + - description: If inPRLabel then label in PR + if: + - payloadType: Pull_Request + then: + - inPrLabel: + label: In-PR + +onFailure: +onSuccess: diff --git a/.github/prompts/backport-pr-to-release-branch.prompt.md b/.github/prompts/backport-pr-to-release-branch.prompt.md new file mode 100644 index 00000000000..32bff10bd5e --- /dev/null +++ b/.github/prompts/backport-pr-to-release-branch.prompt.md @@ -0,0 +1,567 @@ +--- +description: Guide for backporting changes to PowerShell release branches +--- + +# Backport a Change to a PowerShell Release Branch + +## 1 — Goal + +Create a backport PR that applies changes from a merged PR to a release branch (e.g., `release/v7.4`, `release/v7.5`). The backport must follow the repository's established format and include proper references to the original PR. + +## 2 — Prerequisites for the model + +- You have full repository access +- You can run git commands +- You can read PR information from the repository +- Ask clarifying questions if the target release branch or original PR number is unclear + +## 3 — Required user inputs + +If the user hasn't specified a PR number, help them find one: + +### Finding PRs that need backporting + +1. Ask the user which release version they want to backport to (e.g., `7.4`, `7.5`) +2. Search for PRs with the appropriate label using GitHub CLI: + +```powershell +$Owner = "PowerShell" +$Repo = "PowerShell" +$version = "7.4" # or user-specified version +$considerLabel = "Backport-$version.x-Consider" + +$prsJson = gh pr list --repo "$Owner/$Repo" --label $considerLabel --state merged --json number,title,url,labels,mergedAt --limit 100 2>&1 +$prs = $prsJson | ConvertFrom-Json +# Sort PRs from oldest merged to newest merged +$prs = $prs | Sort-Object mergedAt +``` + +3. Present the list of PRs to the user with: + - PR number + - PR title + - Merged date + - URL + +4. Ask the user: "Which PR would you like to backport?" (provide the PR number) + +### After selecting a PR + +Once the user selects a PR (or if they provided one initially), confirm: +- **Original PR number**: The PR number that was merged to the main branch (e.g., 26193) +- **Target release**: The release number (e.g., `7.4`, `7.5`, `7.5.1`) + +Example: "Backport PR 26193 to release/v7.4" + +## 4 — Implementation steps (must be completed in order) + +### Step 1: Verify the original PR exists and is merged + +1. Fetch the original PR information using the PR number +2. Confirm the PR state is `MERGED` +3. Extract the following information: + - Merge commit SHA + - Original PR title + - Original PR author + - Original CL label (if present, typically starts with `CL-`) + +If the PR is not merged, stop and inform the user. + +4. Check if backport already exists or has been attempted: + ```powershell + gh pr list --repo PowerShell/PowerShell --search "in:title [release/v7.4] " --state all + ``` + + If a backport PR already exists, inform the user and ask if they want to continue. + +5. Check backport labels to understand status: + - `Backport-7.4.x-Migrated`: Indicates previous backport attempt (may have failed or had issues) + - `Backport-7.4.x-Done`: Already backported successfully + - `Backport-7.4.x-Approved`: Ready for backporting + - `Backport-7.4.x-Consider`: Under consideration for backporting + + If status is "Done", inform the user that backport may already be complete. + +### Step 2: Create the backport branch + +1. Identify the correct remote to fetch from: + ```bash + git remote -v + ``` + + Look for the remote that points to `https://github.com/PowerShell/PowerShell` (typically named `upstream` or `origin`). Use this remote name in subsequent commands. + +2. Ensure you have the latest changes from the target release branch: + ```bash + git fetch + ``` + + Example: `git fetch upstream release/v7.4` + +3. Create a new branch from the target release branch: + ```bash + git checkout -b backport- / + ``` + + Example: `git checkout -b backport-26193 upstream/release/v7.4` + +### Step 3: Cherry-pick the merge commit + +1. Cherry-pick the merge commit from the original PR: + ```bash + git cherry-pick + ``` + +2. If conflicts occur: + - Inform the user about the conflicts + - List the conflicting files + - Fetch the original PR diff to understand the changes: + ```bash + gh pr diff --repo PowerShell/PowerShell | Out-File pr-diff.txt + ``` + - Review the diff to understand what the PR changed + - Figure out why there is a conflict and resolve it + - Create a summary of the conflict resolution: + * Which files had conflicts + * Nature of each conflict (parameter changes, code removal, etc.) + * How you resolved it + * Whether any manual adjustments were needed beyond accepting one side + - Ask the user to review your conflict resolution summary before continuing + - After conflicts are resolved, continue with: + ```bash + git add + git cherry-pick --continue + ``` + +### Step 4: Push the backport branch + +Push to your fork (typically the remote that you have write access to): + +```bash +git push backport- +``` + +Example: `git push origin backport-26193` + +Note: If you're pushing to the official PowerShell repository and have permissions, you may push to `upstream` or the appropriate remote. + +### Step 5: Create the backport PR + +Create a new PR with the following format: + +**Title:** +``` +[] +``` + +Example: `[release/v7.4] GitHub Workflow cleanup` + +**Body:** +``` +Backport of # to + + + +Triggered by @ on behalf of @ + +Original CL Label: + +/cc @PowerShell/powershell-maintainers + +## Impact + +Choose either tooling or Customer impact. +### Tooling Impact + +- [ ] Required tooling change +- [ ] Optional tooling change (include reasoning) + +### Customer Impact + +- [ ] Customer reported +- [ ] Found internally + +[Select one or both of the boxes. Describe how this issue impacts customers, citing the expected and actual behaviors and scope of the issue. If customer-reported, provide the issue number.] + +## Regression + +- [ ] Yes +- [ ] No + +[If yes, specify when the regression was introduced. Provide the PR or commit if known.] + +## Testing + +[How was the fix verified? How was the issue missed previously? What tests were added?] + +## Risk + +- [ ] High +- [ ] Medium +- [ ] Low + +[High/Medium/Low. Justify the indication by mentioning how risks were measured and addressed.] +``` + +**Base branch:** `` (e.g., `release/v7.4`) + +**Head branch:** `backport-` (e.g., `backport-26193`) + +#### Guidelines for Filling Out the PR Body + +**For Impact Section**: +- If the original PR changed build/tooling/packaging, select "Tooling Impact" +- If it fixes a user-facing bug or changes user-visible behavior, select "Customer Impact" +- Copy relevant context from the original PR description +- Be specific about what changed and why + +**For Regression Section**: +- Mark "Yes" only if the original PR fixed a regression +- Include when the regression was introduced if known + +**For Testing Section**: +- Reference the original PR's testing approach +- Note any additional backport-specific testing needed +- Mention if manual testing was done to verify the backport + +**For Risk Assessment**: +- **High**: Changes core functionality, packaging, build systems, or security-related code +- **Medium**: Changes non-critical features, adds new functionality, or modifies existing behavior +- **Low**: Documentation, test-only changes, minor refactoring, or fixes with narrow scope +- Justify your assessment based on the scope of changes and potential impact +- **For CI/CD changes**: When backporting CI/CD infrastructure changes (workflows, build scripts, packaging), note in your justification that not taking these changes may create technical debt and make it difficult to apply future CI/CD changes that build on top of them. This doesn't change the risk level itself, but provides important context for why the change should be taken despite potentially higher risk + +**If there were merge conflicts**: +Add a note in the PR description after the Risk section describing what conflicts occurred and how they were resolved. + +### Step 6: Add the CL label to the backport PR + +After creating the backport PR, add the same changelog label (CL-*) from the original PR to the backport PR: + +```bash +gh pr edit --repo PowerShell/PowerShell --add-label "" +``` + +Example: `gh pr edit 26389 --repo PowerShell/PowerShell --add-label "CL-BuildPackaging"` + +This ensures the backport is properly categorized in the changelog for the release branch. + +### Step 7: Update the original PR's backport labels + +After successfully creating the backport PR, update the original PR to reflect that it has been backported: + +```bash +gh pr edit --repo PowerShell/PowerShell --add-label "Backport-.x-Migrated" --remove-label "Backport-.x-Consider" +``` + +Example: `gh pr edit 26193 --repo PowerShell/PowerShell --add-label "Backport-7.5.x-Migrated" --remove-label "Backport-7.5.x-Consider"` + +Notes: +- If the original PR had `Backport-.x-Approved` instead of `Consider`, remove that label +- This step helps track which PRs have been successfully backported +- The `Migrated` label indicates the backport PR has been created (not necessarily merged) +- The `Done` label should only be added once the backport PR is merged + +### Step 8: Clean up temporary files + +After successful PR creation and labeling, clean up any temporary files created during the process: + +```powershell +Remove-Item pr*.diff -ErrorAction SilentlyContinue +``` + +## 5 — Definition of Done (self-check list) + +- [ ] Original PR is verified as merged +- [ ] Checked for existing backport PRs +- [ ] Reviewed backport labels to understand status +- [ ] Backport branch created from correct release branch +- [ ] Merge commit cherry-picked successfully (or conflicts resolved) +- [ ] If conflicts occurred, provided resolution summary to user +- [ ] Branch pushed to origin +- [ ] PR created with correct title format: `[] ` +- [ ] CL label added to backport PR (matching original PR's CL label) +- [ ] Original PR labels updated (added Migrated, removed Consider/Approved) +- [ ] Temporary files cleaned up (pr*.diff) +- [ ] PR body includes: + - [ ] Backport reference: `Backport of (PR-number) to ` + - [ ] Auto-generated comment with original PR number + - [ ] Triggered by and original author attribution + - [ ] Original CL label (if available) + - [ ] CC to PowerShell maintainers + - [ ] Impact section filled out + - [ ] Regression section filled out + - [ ] Testing section filled out + - [ ] Risk section filled out +- [ ] Base branch set to target release branch +- [ ] No unrelated changes included + +## 6 — Branch naming convention + +**Format:** `backport/release//pr/` + +Examples: +- `backport/release/v7.5/pr/26193` +- `backport/release/v7.4.1/pr/26334` + +Note: Automated bot uses format `backport/release/v/-`, but manual backports should use the format `backport/release//pr/` as shown above. + +## 7 — Example backport PR + +Reference PR 26334 as the canonical example of a correct backport: + +**Original PR**: PR 26193 "GitHub Workflow cleanup" + +**Backport PR**: PR 26334 "[release/v7.4] GitHub Workflow cleanup" +- **Title**: `[release/v7.4] GitHub Workflow cleanup` +- **Body**: Started with backport reference to original PR and release branch +- **Branch**: `backport/release/v7.4/26193-4aff02475` (bot-created) +- **Base**: `release/v7.4` +- **Includes**: Auto-generated metadata, impact assessment, regression info, testing details, and risk level + +## 8 — Backport label system (for context) + +Backport labels follow pattern: `Backport-.x-` + +**Triage states:** +- `Consider` - Under review for backporting +- `Approved` - Approved and ready to be backported +- `Done` - Backport completed + +**Examples:** `Backport-7.4.x-Approved`, `Backport-7.5.x-Consider`, `Backport-7.3.x-Done` + +Note: The PowerShell repository has an automated bot (pwshBot) that creates backport PRs automatically when a merged PR has a backport approval label. Manual backports follow the same format. + +## Manual Backport Using PowerShell Tools + +For situations where automated backports fail or manual intervention is needed, use the `Invoke-PRBackport` function from `tools/releaseTools.psm1`. + +### Prerequisites + +1. **GitHub CLI**: Install from https://cli.github.com/ + - Required version: 2.17 or later + - Authenticate with `gh auth login` + +2. **Upstream Remote**: Configure a Git remote named `upstream` pointing to `PowerShell/PowerShell`: + ```powershell + git remote add upstream https://github.com/PowerShell/PowerShell.git + ``` + +### Using Invoke-PRBackport + +```powershell +# Import the release tools module +Import-Module ./tools/releaseTools.psm1 + +# Backport a single PR +Invoke-PRBackport -PrNumber 26193 -Target release/v7.4.1 + +# Backport with custom branch postfix +Invoke-PRBackport -PrNumber 26193 -Target release/v7.4.1 -BranchPostFix "retry" + +# Overwrite existing local branch if it exists +Invoke-PRBackport -PrNumber 26193 -Target release/v7.4.1 -Overwrite +``` + +### Parameters + +- **PrNumber** (Required): The PR number to backport +- **Target** (Required): Target release branch (must match pattern `release/v\d+\.\d+(\.\d+)?`) +- **Overwrite**: Switch to overwrite local branch if it already exists +- **BranchPostFix**: Add a postfix to the branch name (e.g., for retry attempts) +- **UpstreamRemote**: Name of the upstream remote (default: `upstream`) + +### How It Works + +1. Verifies the PR is merged +2. Fetches the target release branch from upstream +3. Creates a new branch: `backport-[-]` +4. Cherry-picks the merge commit +5. If conflicts occur, prompts you to resolve them +6. Creates the backport PR using GitHub CLI + +## Handling Merge Conflicts + +When cherry-picking fails due to conflicts: + +1. The script will pause and prompt you to fix conflicts +2. Resolve conflicts in your editor: + ```powershell + # Check which files have conflicts + git status + + # Edit files to resolve conflicts + # After resolving, stage the changes + git add + + # Continue the cherry-pick + git cherry-pick --continue + ``` +3. Type 'Yes' when prompted to continue the script +4. The script will create the PR + +### Understanding Conflict Patterns + +When resolving conflicts during backports, follow this approach: + +1. **Analyze the diff first**: Before resolving conflicts, fetch and review the original PR's diff to understand what changed: + ```powershell + gh pr diff --repo PowerShell/PowerShell | Out-File pr-diff.txt + ``` + +2. **Identify conflict types**: + - **Parameter additions**: New parameters added to functions (e.g., ValidateSet values) + - **Code removal**: Features removed in main but still exist in release branch + - **Code additions**: New code blocks that don't exist in release branch + - **Refactoring conflicts**: Code structure changes between branches + +3. **Resolution priorities**: + - Preserve the intent of the backported change + - Keep release branch-specific code that doesn't conflict with the fix + - When in doubt, favor the incoming change from the backport + - Document significant manual changes in the PR description + +4. **Verification**: + - After resolving conflicts, verify the file compiles/runs + - Check that the resolved code matches the original PR's intent + - Look for orphaned code that references removed functions + +5. **Create a conflict resolution summary**: + - List which files had conflicts + - Briefly explain the nature of each conflict + - Describe how you resolved it + - Ask user to review the resolution before continuing + +### Context-Aware Conflict Resolution + +**Key Principle**: The release branch may have different code than main. Your goal is to apply the *change* from the PR, not necessarily make the code identical to main. + +**Common Scenarios**: +1. **Function parameters differ**: If the release branch has fewer parameters than main, and the backport adds functionality unrelated to new parameters, keep the release branch parameters unless the new parameters are part of the fix +2. **Dependencies removed in main**: If main removed a dependency but the release branch still has it, and the backport is unrelated to that dependency, keep the release branch code +3. **New features in main**: If main has new features not in the release, focus on backporting only the specific fix, not the new features + +## Bulk Backporting Approved PRs + +To backport all PRs labeled as approved for a specific version: + +```powershell +Import-Module ./tools/releaseTools.psm1 + +# Backport all approved PRs for version 7.2.12 +Invoke-PRBackportApproved -Version 7.2.12 +``` + +This function: +1. Queries all merged PRs with the `Backport-.x-Approved` label +2. Attempts to backport each PR in order of merge date +3. Creates individual backport PRs for each + +## Viewing Backport Reports + +Get a list of PRs that need backporting: + +```powershell +Import-Module ./tools/releaseTools.psm1 + +# List all approved backports for 7.4 +Get-PRBackportReport -Version 7.4 -TriageState Approved + +# Open all approved backports in browser +Get-PRBackportReport -Version 7.4 -TriageState Approved -Web + +# Check which backports are done +Get-PRBackportReport -Version 7.4 -TriageState Done +``` + +## Branch Naming Conventions + +### Automated Bot Branches +Format: `backport/release/v/-` + +Example: `backport/release/v7.4/26193-4aff02475` + +### Manual Backport Branches +Format: `backport-[-]` + +Examples: +- `backport-26193` +- `backport-26193-retry` + +## PR Title and Description Format + +### Title +Format: `[release/v] ` + +Example: `[release/v7.4] GitHub Workflow cleanup` + +### Description +The backport PR description includes: +- Reference to original PR number +- Target release branch +- Auto-generated comment with original PR metadata +- Maintainer information +- Original CL label +- CC to PowerShell maintainers team + +Example description structure: +```text +Backport of (original-pr-number) to release/v + + + +Triggered by @ on behalf of @ + +Original CL Label: + +/cc @PowerShell/powershell-maintainers +``` + +## Best Practices + +1. **Verify PR is merged**: Only backport merged PRs +2. **Test backports**: Ensure backported changes work in the target release context +3. **Check for conflicts early**: Large PRs are more likely to have conflicts +4. **Use appropriate labels**: Apply correct version and triage state labels +5. **Document special cases**: If manual changes were needed, note them in the PR description +6. **Follow up on CI failures**: Backports should pass all CI checks before merging + +## Troubleshooting + +### "PR is not merged" Error +**Cause**: Attempting to backport a PR that hasn't been merged yet +**Solution**: Wait for the PR to be merged to the main branch first + +### "Please create an upstream remote" Error +**Cause**: No upstream remote configured +**Solution**: +```powershell +git remote add upstream https://github.com/PowerShell/PowerShell.git +git fetch upstream +``` + +### "GitHub CLI is not installed" Error +**Cause**: gh CLI not found in PATH +**Solution**: Install from https://cli.github.com/ and restart terminal + +### Cherry-pick Conflicts +**Cause**: Changes conflict with the target branch +**Solution**: Manually resolve conflicts, stage files, and continue cherry-pick + +### "Commit does not exist" Error +**Cause**: Local Git doesn't have the commit +**Solution**: +```powershell +git fetch upstream +``` + +## Related Resources + +- **Release Process**: See `docs/maintainers/releasing.md` +- **Release Tools**: See `tools/releaseTools.psm1` +- **Issue Management**: See `docs/maintainers/issue-management.md` diff --git a/.github/prquantifier.yaml b/.github/prquantifier.yaml new file mode 100644 index 00000000000..ea891ba4988 --- /dev/null +++ b/.github/prquantifier.yaml @@ -0,0 +1,11 @@ +# https://github.com/microsoft/PullRequestQuantifier/blob/main/docs/prquantifier-yaml.md +Excluded: +# defaults +- '*.csproj' +- prquantifier.yaml +- package-lock.json +- '*.md' +- '*.sln' +# autogenerated files +- tools/cgmanifest.json +- assets/wix/files.wxs diff --git a/.github/workflows/GHWorkflowHelper/GHWorkflowHelper.psm1 b/.github/workflows/GHWorkflowHelper/GHWorkflowHelper.psm1 new file mode 100644 index 00000000000..f0524ce6f23 --- /dev/null +++ b/.github/workflows/GHWorkflowHelper/GHWorkflowHelper.psm1 @@ -0,0 +1,27 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +function Set-GWVariable { + param( + [Parameter(Mandatory = $true)] + [string]$Name, + [Parameter(Mandatory = $true)] + [string]$Value + ) + + Write-Verbose "Setting CI variable $Name to $Value" -Verbose + + if ($env:GITHUB_ENV) { + "$Name=$Value" | Out-File $env:GITHUB_ENV -Append + } +} + +function Get-GWTempPath { + $temp = [System.IO.Path]::GetTempPath() + if ($env:RUNNER_TEMP) { + $temp = $env:RUNNER_TEMP + } + + Write-Verbose "Get CI Temp path: $temp" -Verbose + return $temp +} diff --git a/.github/workflows/analyze-reusable.yml b/.github/workflows/analyze-reusable.yml new file mode 100644 index 00000000000..3271b534794 --- /dev/null +++ b/.github/workflows/analyze-reusable.yml @@ -0,0 +1,77 @@ +name: CodeQL Analysis (Reusable) + +on: + workflow_call: + inputs: + runner_os: + description: 'Runner OS for CodeQL analysis' + type: string + required: false + default: ubuntu-latest + +permissions: + actions: read # for github/codeql-action/init to get workflow details + contents: read # for actions/checkout to fetch code + security-events: write # for github/codeql-action/analyze to upload SARIF results + +env: + DOTNET_CLI_TELEMETRY_OPTOUT: 1 + DOTNET_NOLOGO: 1 + POWERSHELL_TELEMETRY_OPTOUT: 1 + __SuppressAnsiEscapeSequences: 1 + nugetMultiFeedWarnLevel: none + +jobs: + analyze: + name: Analyze + runs-on: ${{ inputs.runner_os }} + + strategy: + fail-fast: false + matrix: + # Override automatic language detection by changing the below list + # Supported options are ['csharp', 'cpp', 'go', 'java', 'javascript', 'python'] + language: ['csharp'] + # Learn more... + # https://docs.github.com/en/github/finding-security-vulnerabilities-and-errors-in-your-code/configuring-code-scanning#overriding-automatic-language-detection + + steps: + - name: Checkout repository + uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + with: + fetch-depth: '0' + + - uses: actions/setup-dotnet@v5 + with: + global-json-file: ./global.json + + # Initializes the CodeQL tools for scanning. + - name: Initialize CodeQL + uses: github/codeql-action/init@1b168cd39490f61582a9beae412bb7057a6b2c4e # v3.29.5 + with: + languages: ${{ matrix.language }} + # If you wish to specify custom queries, you can do so here or in a config file. + # By default, queries listed here will override any specified in a config file. + # Prefix the list here with "+" to use these queries and those in the config file. + # queries: ./path/to/local/query, your-org/your-repo/queries@main + + - run: | + Import-Module .\tools\ci.psm1 + Show-Environment + name: Capture Environment + shell: pwsh + + - run: | + Import-Module .\tools\ci.psm1 + Invoke-CIInstall -SkipUser + name: Bootstrap + shell: pwsh + + - run: | + Import-Module .\tools\ci.psm1 + Invoke-CIBuild -Configuration 'StaticAnalysis' + name: Build + shell: pwsh + + - name: Perform CodeQL Analysis + uses: github/codeql-action/analyze@1b168cd39490f61582a9beae412bb7057a6b2c4e # v3.29.5 diff --git a/.github/workflows/copilot-setup-steps.yml b/.github/workflows/copilot-setup-steps.yml new file mode 100644 index 00000000000..be2dd55df7d --- /dev/null +++ b/.github/workflows/copilot-setup-steps.yml @@ -0,0 +1,61 @@ +name: "Copilot Setup Steps" + +# Allow testing of the setup steps from your repository's "Actions" tab. +on: + workflow_dispatch: + + pull_request: + branches: + - master + paths: + - ".github/workflows/copilot-setup-steps.yml" + +jobs: + # The job MUST be called `copilot-setup-steps` or it will not be picked up by Copilot. + # See https://docs.github.com/en/copilot/customizing-copilot/customizing-the-development-environment-for-copilot-coding-agent + copilot-setup-steps: + runs-on: ubuntu-latest + + permissions: + contents: read + + # You can define any steps you want, and they will run before the agent starts. + # If you do not check out your code, Copilot will do this for you. + steps: + - uses: actions/checkout@v6 + with: + fetch-depth: 1000 + + - name: Bootstrap + if: success() + run: |- + $title = 'Import Build.psm1' + Write-Host "::group::$title" + Import-Module ./build.psm1 -Verbose -ErrorAction Stop + Write-LogGroupEnd -Title $title + + $title = 'Switch to public feed' + Write-LogGroupStart -Title $title + Switch-PSNugetConfig -Source Public + Write-LogGroupEnd -Title $title + + $title = 'Bootstrap' + Write-LogGroupStart -Title $title + Start-PSBootstrap -Scenario DotNet + Write-LogGroupEnd -Title $title + + $title = 'Install .NET Tools' + Write-LogGroupStart -Title $title + Start-PSBootstrap -Scenario Tools + Write-LogGroupEnd -Title $title + + $title = 'Sync Tags' + Write-LogGroupStart -Title $title + Sync-PSTags -AddRemoteIfMissing + Write-LogGroupEnd -Title $title + + $title = 'Setup .NET environment variables' + Write-LogGroupStart -Title $title + Find-DotNet -SetDotnetRoot + Write-LogGroupEnd -Title $title + shell: pwsh diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml new file mode 100644 index 00000000000..dbef2e1c8b2 --- /dev/null +++ b/.github/workflows/dependency-review.yml @@ -0,0 +1,22 @@ +# Dependency Review Action +# +# This Action will scan dependency manifest files that change as part of a Pull Request, +# surfacing known-vulnerable versions of the packages declared or updated in the PR. +# Once installed, if the workflow run is marked as required, +# PRs introducing known-vulnerable packages will be blocked from merging. +# +# Source repository: https://github.com/actions/dependency-review-action +name: 'Dependency Review' +on: [pull_request] + +permissions: + contents: read + +jobs: + dependency-review: + runs-on: ubuntu-latest + steps: + - name: 'Checkout Repository' + uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + - name: 'Dependency Review' + uses: actions/dependency-review-action@3c4e3dcb1aa7874d2c16be7d79418e9b7efd6261 # v4.8.2 diff --git a/.github/workflows/labels.yml b/.github/workflows/labels.yml new file mode 100644 index 00000000000..61b5eebb88f --- /dev/null +++ b/.github/workflows/labels.yml @@ -0,0 +1,31 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +name: Verify PR Labels + +on: + pull_request: + types: [opened, reopened, edited, labeled, unlabeled, synchronize] + +permissions: + contents: read + pull-requests: read + +jobs: + verify-labels: + if: github.repository_owner == 'PowerShell' + runs-on: ubuntu-latest + + steps: + - name: Check out the repository + uses: actions/checkout@v6 + + - name: Verify PR has label starting with 'cl-' + id: verify-labels + uses: actions/github-script@v8 + with: + script: | + const labels = context.payload.pull_request.labels.map(label => label.name.toLowerCase()); + if (!labels.some(label => label.startsWith('cl-'))) { + core.setFailed("Every PR must have at least one label starting with 'cl-'."); + } diff --git a/.github/workflows/linux-ci.yml b/.github/workflows/linux-ci.yml new file mode 100644 index 00000000000..49d1c0a055f --- /dev/null +++ b/.github/workflows/linux-ci.yml @@ -0,0 +1,262 @@ +name: Linux-CI + +run-name: "${{ github.ref_name }} - ${{ github.run_number }}" + +on: + workflow_dispatch: + + push: + branches: + - master + - release/** + - github-mirror + paths: + - "**" + - "*" + - ".globalconfig" + - "!.github/ISSUE_TEMPLATE/**" + - "!.dependabot/config.yml" + - "!.pipelines/**" + - "!test/perf/**" + pull_request: + branches: + - master + - release/** + - github-mirror + - "*-feature" +# Path filters for PRs need to go into the changes job + +concurrency: + group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.ref }} + cancel-in-progress: ${{ contains(github.ref, 'merge')}} + +env: + DOTNET_CLI_TELEMETRY_OPTOUT: 1 + DOTNET_NOLOGO: 1 + FORCE_FEATURE: 'False' + FORCE_PACKAGE: 'False' + NUGET_KEY: none + POWERSHELL_TELEMETRY_OPTOUT: 1 + __SuppressAnsiEscapeSequences: 1 + nugetMultiFeedWarnLevel: none + system_debug: 'false' +jobs: + changes: + if: startsWith(github.repository_owner, 'azure') || github.repository_owner == 'PowerShell' + name: Change Detection + runs-on: ubuntu-latest + # Required permissions + permissions: + pull-requests: read + contents: read + + # Set job outputs to values from filter step + outputs: + source: ${{ steps.filter.outputs.source }} + buildModuleChanged: ${{ steps.filter.outputs.buildModuleChanged }} + packagingChanged: ${{ steps.filter.outputs.packagingChanged }} + steps: + - name: checkout + uses: actions/checkout@v6 + with: + persist-credentials: false + + - name: Change Detection + id: filter + uses: "./.github/actions/infrastructure/path-filters" + with: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + merge_conflict_check: + name: Check for Merge Conflict Markers + runs-on: ubuntu-latest + if: github.event_name == 'pull_request' && (startsWith(github.repository_owner, 'azure') || github.repository_owner == 'PowerShell') + permissions: + pull-requests: read + contents: read + steps: + - name: checkout + uses: actions/checkout@v6 + + - name: Check for merge conflict markers + uses: "./.github/actions/infrastructure/merge-conflict-checker" + + ci_build: + name: Build PowerShell + runs-on: ubuntu-latest + needs: changes + if: ${{ needs.changes.outputs.source == 'true' || needs.changes.outputs.buildModuleChanged == 'true' }} + steps: + - name: checkout + uses: actions/checkout@v6 + with: + fetch-depth: 1000 + + - name: Build + uses: "./.github/actions/build/ci" + linux_test_unelevated_ci: + name: Linux Unelevated CI + needs: + - ci_build + - changes + if: ${{ needs.changes.outputs.source == 'true' || needs.changes.outputs.buildModuleChanged == 'true' }} + runs-on: ubuntu-latest + steps: + - name: checkout + uses: actions/checkout@v6 + with: + fetch-depth: 1000 + - name: Linux Unelevated CI + uses: "./.github/actions/test/nix" + with: + purpose: UnelevatedPesterTests + tagSet: CI + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + linux_test_elevated_ci: + name: Linux Elevated CI + needs: + - ci_build + - changes + if: ${{ needs.changes.outputs.source == 'true' || needs.changes.outputs.buildModuleChanged == 'true' }} + runs-on: ubuntu-latest + steps: + - name: checkout + uses: actions/checkout@v6 + with: + fetch-depth: 1000 + - name: Linux Elevated CI + uses: "./.github/actions/test/nix" + with: + purpose: ElevatedPesterTests + tagSet: CI + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + linux_test_unelevated_others: + name: Linux Unelevated Others + needs: + - ci_build + - changes + if: ${{ needs.changes.outputs.source == 'true' || needs.changes.outputs.buildModuleChanged == 'true' }} + runs-on: ubuntu-latest + steps: + - name: checkout + uses: actions/checkout@v6 + with: + fetch-depth: 1000 + - name: Linux Unelevated Others + uses: "./.github/actions/test/nix" + with: + purpose: UnelevatedPesterTests + tagSet: Others + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + linux_test_elevated_others: + name: Linux Elevated Others + needs: + - ci_build + - changes + if: ${{ needs.changes.outputs.source == 'true' || needs.changes.outputs.buildModuleChanged == 'true' }} + runs-on: ubuntu-latest + steps: + - name: checkout + uses: actions/checkout@v6 + with: + fetch-depth: 1000 + - name: Linux Elevated Others + uses: "./.github/actions/test/nix" + with: + purpose: ElevatedPesterTests + tagSet: Others + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + xunit_tests: + name: xUnit Tests + needs: + - changes + if: ${{ needs.changes.outputs.source == 'true' || needs.changes.outputs.buildModuleChanged == 'true' }} + uses: ./.github/workflows/xunit-tests.yml + with: + runner_os: ubuntu-latest + test_results_artifact_name: testResults-xunit + + infrastructure_tests: + name: Infrastructure Tests + runs-on: ubuntu-latest + steps: + - name: checkout + uses: actions/checkout@v6 + with: + fetch-depth: 1 + + - name: Install Pester + shell: pwsh + run: | + Import-Module ./tools/ci.psm1 + Install-CIPester + + - name: Run Infrastructure Tests + shell: pwsh + run: | + $testResultsFolder = Join-Path $PWD "testResults" + New-Item -ItemType Directory -Path $testResultsFolder -Force | Out-Null + + $config = New-PesterConfiguration + $config.Run.Path = './test/infrastructure/' + $config.Run.PassThru = $true + $config.TestResult.Enabled = $true + $config.TestResult.OutputFormat = 'NUnitXml' + $config.TestResult.OutputPath = "$testResultsFolder/InfrastructureTests.xml" + $config.Output.Verbosity = 'Detailed' + + $result = Invoke-Pester -Configuration $config + + if ($result.FailedCount -gt 0 -or $result.Result -eq 'Failed') { + throw "Infrastructure tests failed" + } + + - name: Publish Test Results + uses: "./.github/actions/test/process-pester-results" + if: always() + with: + name: "InfrastructureTests" + testResultsFolder: "${{ github.workspace }}/testResults" + + ## Temporarily disable the CodeQL analysis on Linux as it doesn't work for .NET SDK 10-rc.2. + # analyze: + # name: CodeQL Analysis + # needs: changes + # if: ${{ needs.changes.outputs.source == 'true' }} + # uses: ./.github/workflows/analyze-reusable.yml + # permissions: + # actions: read + # contents: read + # security-events: write + # with: + # runner_os: ubuntu-latest + + ready_to_merge: + name: Linux ready to merge + needs: + - xunit_tests + - linux_test_elevated_ci + - linux_test_elevated_others + - linux_test_unelevated_ci + - linux_test_unelevated_others + - linux_packaging + - merge_conflict_check + - infrastructure_tests + # - analyze + if: always() + uses: PowerShell/compliance/.github/workflows/ready-to-merge.yml@v1.0.0 + with: + needs_context: ${{ toJson(needs) }} + linux_packaging: + name: Linux Packaging + needs: + - changes + if: ${{ needs.changes.outputs.packagingChanged == 'true' }} + runs-on: ubuntu-latest + steps: + - name: checkout + uses: actions/checkout@v6 + with: + fetch-depth: 0 + - name: Linux Packaging + uses: "./.github/actions/test/linux-packaging" diff --git a/.github/workflows/macos-ci.yml b/.github/workflows/macos-ci.yml new file mode 100644 index 00000000000..8f15a2f3a6d --- /dev/null +++ b/.github/workflows/macos-ci.yml @@ -0,0 +1,247 @@ +name: macOS-CI + +run-name: "${{ github.ref_name }} - ${{ github.run_number }}" + +on: + push: + branches: + - master + - release/** + - github-mirror + paths: + - "**" + - "*" + - ".globalconfig" + - "!.github/ISSUE_TEMPLATE/**" + - "!.dependabot/config.yml" + - "!.pipelines/**" + - "!test/perf/**" + pull_request: + branches: + - master + - release/** + - github-mirror + - "*-feature" +# Path filters for PRs need to go into the changes job + +concurrency: + group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.ref }} + cancel-in-progress: ${{ contains(github.ref, 'merge')}} + +env: + DOTNET_CLI_TELEMETRY_OPTOUT: 1 + DOTNET_NOLOGO: 1 + FORCE_FEATURE: 'False' + FORCE_PACKAGE: 'False' + HOMEBREW_NO_ANALYTICS: 1 + NUGET_KEY: none + POWERSHELL_TELEMETRY_OPTOUT: 1 + __SuppressAnsiEscapeSequences: 1 + nugetMultiFeedWarnLevel: none + system_debug: 'false' + +jobs: + changes: + name: Change Detection + runs-on: ubuntu-latest + if: startsWith(github.repository_owner, 'azure') || github.repository_owner == 'PowerShell' + # Required permissions + permissions: + pull-requests: read + contents: read + + # Set job outputs to values from filter step + outputs: + source: ${{ steps.filter.outputs.source }} + buildModuleChanged: ${{ steps.filter.outputs.buildModuleChanged }} + steps: + - name: checkout + uses: actions/checkout@v6 + + - name: Change Detection + id: filter + uses: "./.github/actions/infrastructure/path-filters" + with: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + ci_build: + name: Build PowerShell + runs-on: macos-15-large + needs: changes + if: ${{ needs.changes.outputs.source == 'true' || needs.changes.outputs.buildModuleChanged == 'true' }} + steps: + - name: checkout + uses: actions/checkout@v6 + with: + fetch-depth: 1000 + - name: Build + uses: "./.github/actions/build/ci" + macos_test_unelevated_ci: + name: macos Unelevated CI + needs: + - ci_build + - changes + if: ${{ needs.changes.outputs.source == 'true' || needs.changes.outputs.buildModuleChanged == 'true' }} + runs-on: macos-15-large + steps: + - name: checkout + uses: actions/checkout@v6 + with: + fetch-depth: 1000 + - name: macOS Unelevated CI + uses: "./.github/actions/test/nix" + with: + purpose: UnelevatedPesterTests + tagSet: CI + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + macos_test_elevated_ci: + name: macOS Elevated CI + needs: + - ci_build + - changes + if: ${{ needs.changes.outputs.source == 'true' || needs.changes.outputs.buildModuleChanged == 'true' }} + runs-on: macos-15-large + steps: + - name: checkout + uses: actions/checkout@v6 + with: + fetch-depth: 1000 + - name: macOS Elevated CI + uses: "./.github/actions/test/nix" + with: + purpose: ElevatedPesterTests + tagSet: CI + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + macos_test_unelevated_others: + name: macOS Unelevated Others + needs: + - ci_build + - changes + if: ${{ needs.changes.outputs.source == 'true' || needs.changes.outputs.buildModuleChanged == 'true' }} + runs-on: macos-15-large + steps: + - name: checkout + uses: actions/checkout@v6 + with: + fetch-depth: 1000 + - name: macOS Unelevated Others + uses: "./.github/actions/test/nix" + with: + purpose: UnelevatedPesterTests + tagSet: Others + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + macos_test_elevated_others: + name: macOS Elevated Others + needs: + - ci_build + - changes + if: ${{ needs.changes.outputs.source == 'true' || needs.changes.outputs.buildModuleChanged == 'true' }} + runs-on: macos-15-large + steps: + - name: checkout + uses: actions/checkout@v6 + with: + fetch-depth: 1000 + - name: macOS Elevated Others + uses: "./.github/actions/test/nix" + with: + purpose: ElevatedPesterTests + tagSet: Others + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + xunit_tests: + name: xUnit Tests + needs: + - changes + if: ${{ needs.changes.outputs.source == 'true' || needs.changes.outputs.buildModuleChanged == 'true' }} + uses: ./.github/workflows/xunit-tests.yml + with: + runner_os: macos-15-large + test_results_artifact_name: testResults-xunit + PackageMac-macos_packaging: + name: macOS packaging and testing + needs: + - changes + if: ${{ needs.changes.outputs.source == 'true' || needs.changes.outputs.buildModuleChanged == 'true' }} + runs-on: + - macos-15-large + steps: + - name: checkout + uses: actions/checkout@v6 + with: + fetch-depth: 1000 + - uses: actions/setup-dotnet@v5 + with: + global-json-file: ./global.json + - name: Bootstrap packaging + if: success() + run: |- + import-module ./build.psm1 + start-psbootstrap -Scenario package + shell: pwsh + - name: Build PowerShell and Create macOS package + if: success() + run: |- + import-module ./build.psm1 + import-module ./tools/ci.psm1 + import-module ./tools/packaging/packaging.psm1 + Switch-PSNugetConfig -Source Public + Sync-PSTags -AddRemoteIfMissing + $releaseTag = Get-ReleaseTag + Start-PSBuild -Configuration Release -PSModuleRestore -ReleaseTag $releaseTag + $macOSRuntime = if ([System.Runtime.InteropServices.RuntimeInformation]::OSArchitecture -eq 'Arm64') { 'osx-arm64' } else { 'osx-x64' } + Start-PSPackage -Type osxpkg -ReleaseTag $releaseTag -MacOSRuntime $macOSRuntime -SkipReleaseChecks + shell: pwsh + + - name: Install Pester + if: success() + run: |- + Import-Module ./tools/ci.psm1 + Install-CIPester + shell: pwsh + + - name: Test package contents + if: success() + run: |- + $env:PACKAGE_FOLDER = Get-Location + $testResultsPath = Join-Path $env:RUNNER_WORKSPACE "testResults" + if (-not (Test-Path $testResultsPath)) { + New-Item -ItemType Directory -Path $testResultsPath -Force | Out-Null + } + Import-Module Pester + $pesterConfig = New-PesterConfiguration + $pesterConfig.Run.Path = './test/packaging/macos/package-validation.tests.ps1' + $pesterConfig.Run.PassThru = $true + $pesterConfig.Output.Verbosity = 'Detailed' + $pesterConfig.TestResult.Enabled = $true + $pesterConfig.TestResult.OutputFormat = 'NUnitXml' + $pesterConfig.TestResult.OutputPath = Join-Path $testResultsPath "macOSPackage.xml" + $result = Invoke-Pester -Configuration $pesterConfig + if ($result.FailedCount -gt 0) { + throw "Package validation failed with $($result.FailedCount) failed test(s)" + } + shell: pwsh + - name: Publish and Upload Pester Test Results + if: always() + uses: "./.github/actions/test/process-pester-results" + with: + name: "macOSPackage" + testResultsFolder: "${{ runner.workspace }}/testResults" + - name: Upload package artifact + if: always() + uses: actions/upload-artifact@v6 + with: + name: macos-package + path: "*.pkg" + ready_to_merge: + name: macos ready to merge + needs: + - xunit_tests + - PackageMac-macos_packaging + - macos_test_elevated_ci + - macos_test_elevated_others + - macos_test_unelevated_ci + - macos_test_unelevated_others + if: always() + uses: PowerShell/compliance/.github/workflows/ready-to-merge.yml@v1.0.0 + with: + needs_context: ${{ toJson(needs) }} diff --git a/.github/workflows/markdown-link/config.json b/.github/workflows/markdown-link/config.json new file mode 100644 index 00000000000..87d65922a91 --- /dev/null +++ b/.github/workflows/markdown-link/config.json @@ -0,0 +1,7 @@ +{ + "timeout": "40s", + "retryOn429": true, + "retryCount": 5, + "fallbackRetryDelay": "30s", + "aliveStatusCodes": [504, 503, 403, 200] +} diff --git a/.github/workflows/scorecards.yml b/.github/workflows/scorecards.yml new file mode 100644 index 00000000000..3d9ff2eba28 --- /dev/null +++ b/.github/workflows/scorecards.yml @@ -0,0 +1,72 @@ +# This workflow uses actions that are not certified by GitHub. They are provided +# by a third-party and are governed by separate terms of service, privacy +# policy, and support documentation. + +name: Scorecard supply-chain security +on: + # For Branch-Protection check. Only the default branch is supported. See + # https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection + branch_protection_rule: + # To guarantee Maintained check is occasionally updated. See + # https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained + schedule: + - cron: '20 7 * * 2' + push: + branches: ["master"] + +# Declare default permissions as read only. +permissions: read-all + +jobs: + analysis: + name: Scorecard analysis + if: github.repository_owner == 'PowerShell' + runs-on: ubuntu-latest + permissions: + # Needed to upload the results to code-scanning dashboard. + security-events: write + # Needed to publish results and get a badge (see publish_results below). + id-token: write + contents: read + actions: read + + steps: + - name: "Checkout code" + uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v6.0.0 + with: + persist-credentials: false + + - name: "Run analysis" + uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3 + with: + results_file: results.sarif + results_format: sarif + # (Optional) "write" PAT token. Uncomment the `repo_token` line below if: + # - you want to enable the Branch-Protection check on a *public* repository, or + # - you are installing Scorecards on a *private* repository + # To create the PAT, follow the steps in https://github.com/ossf/scorecard-action#authentication-with-pat. + # repo_token: ${{ secrets.SCORECARD_TOKEN }} + + # Public repositories: + # - Publish results to OpenSSF REST API for easy access by consumers + # - Allows the repository to include the Scorecard badge. + # - See https://github.com/ossf/scorecard-action#publishing-results. + # For private repositories: + # - `publish_results` will always be set to `false`, regardless + # of the value entered here. + publish_results: true + + # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF + # format to the repository Actions tab. + - name: "Upload artifact" + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 + with: + name: SARIF file + path: results.sarif + retention-days: 5 + + # Upload the results to GitHub's code scanning dashboard. + - name: "Upload to code-scanning" + uses: github/codeql-action/upload-sarif@1b168cd39490f61582a9beae412bb7057a6b2c4e # v3.29.5 + with: + sarif_file: results.sarif diff --git a/.github/workflows/verify-markdown-links.yml b/.github/workflows/verify-markdown-links.yml new file mode 100644 index 00000000000..df37ba3c513 --- /dev/null +++ b/.github/workflows/verify-markdown-links.yml @@ -0,0 +1,32 @@ +name: Verify Markdown Links + +on: + push: + branches: [ main, master ] + paths: + - '**/*.md' + - '.github/workflows/verify-markdown-links.yml' + - '.github/actions/infrastructure/markdownlinks/**' + pull_request: + branches: [ main, master ] + paths: + - '**/*.md' + schedule: + # Run weekly on Sundays at midnight UTC to catch external link rot + - cron: '0 0 * * 0' + workflow_dispatch: + +jobs: + verify-markdown-links: + name: Verify Markdown Links + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v6 + + - name: Verify markdown links + id: verify + uses: ./.github/actions/infrastructure/markdownlinks + with: + timeout-sec: 30 + maximum-retry-count: 2 diff --git a/.github/workflows/windows-ci.yml b/.github/workflows/windows-ci.yml new file mode 100644 index 00000000000..92bbf2f4c9e --- /dev/null +++ b/.github/workflows/windows-ci.yml @@ -0,0 +1,194 @@ +name: Windows-CI +on: + workflow_dispatch: + push: + branches: + - master + - release/** + - github-mirror + paths: + - "**" + - "*" + - ".globalconfig" + - "!.vsts-ci/misc-analysis.yml" + - "!.github/ISSUE_TEMPLATE/**" + - "!.dependabot/config.yml" + - "!test/perf/**" + - "!.pipelines/**" + pull_request: + branches: + - master + - release/** + - github-mirror + - "*-feature" + +# Path filters for PRs need to go into the changes job + +concurrency: + group: ${{ github.workflow }}-${{ github.event_name }}-${{ github.ref }} + cancel-in-progress: ${{ contains(github.ref, 'merge')}} + +permissions: + contents: read + +run-name: "${{ github.ref_name }} - ${{ github.run_number }}" + +env: + DOTNET_CLI_TELEMETRY_OPTOUT: 1 + DOTNET_NOLOGO: 1 + GIT_CONFIG_PARAMETERS: "'core.autocrlf=false'" + NugetSecurityAnalysisWarningLevel: none + POWERSHELL_TELEMETRY_OPTOUT: 1 + __SuppressAnsiEscapeSequences: 1 + nugetMultiFeedWarnLevel: none + SYSTEM_ARTIFACTSDIRECTORY: ${{ github.workspace }}/artifacts + BUILD_ARTIFACTSTAGINGDIRECTORY: ${{ github.workspace }}/artifacts +jobs: + changes: + name: Change Detection + runs-on: ubuntu-latest + if: startsWith(github.repository_owner, 'azure') || github.repository_owner == 'PowerShell' + # Required permissions + permissions: + pull-requests: read + contents: read + + # Set job outputs to values from filter step + outputs: + source: ${{ steps.filter.outputs.source }} + buildModuleChanged: ${{ steps.filter.outputs.buildModuleChanged }} + packagingChanged: ${{ steps.filter.outputs.packagingChanged }} + steps: + - name: checkout + uses: actions/checkout@v6 + + - name: Change Detection + id: filter + uses: "./.github/actions/infrastructure/path-filters" + with: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + ci_build: + name: Build PowerShell + needs: changes + if: ${{ needs.changes.outputs.source == 'true' || needs.changes.outputs.buildModuleChanged == 'true' }} + runs-on: windows-latest + steps: + - name: checkout + uses: actions/checkout@v6 + with: + fetch-depth: 1000 + - name: Build + uses: "./.github/actions/build/ci" + windows_test_unelevated_ci: + name: Windows Unelevated CI + needs: + - ci_build + - changes + if: ${{ needs.changes.outputs.source == 'true' || needs.changes.outputs.buildModuleChanged == 'true' }} + runs-on: windows-latest + steps: + - name: checkout + uses: actions/checkout@v6 + with: + fetch-depth: 1000 + - name: Windows Unelevated CI + uses: "./.github/actions/test/windows" + with: + purpose: UnelevatedPesterTests + tagSet: CI + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + windows_test_elevated_ci: + name: Windows Elevated CI + needs: + - ci_build + - changes + if: ${{ needs.changes.outputs.source == 'true' || needs.changes.outputs.buildModuleChanged == 'true' }} + runs-on: windows-latest + steps: + - name: checkout + uses: actions/checkout@v6 + with: + fetch-depth: 1000 + - name: Windows Elevated CI + uses: "./.github/actions/test/windows" + with: + purpose: ElevatedPesterTests + tagSet: CI + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + windows_test_unelevated_others: + name: Windows Unelevated Others + needs: + - ci_build + - changes + if: ${{ needs.changes.outputs.source == 'true' || needs.changes.outputs.buildModuleChanged == 'true' }} + runs-on: windows-latest + steps: + - name: checkout + uses: actions/checkout@v6 + with: + fetch-depth: 1000 + - name: Windows Unelevated Others + uses: "./.github/actions/test/windows" + with: + purpose: UnelevatedPesterTests + tagSet: Others + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + windows_test_elevated_others: + name: Windows Elevated Others + needs: + - ci_build + - changes + if: ${{ needs.changes.outputs.source == 'true' || needs.changes.outputs.buildModuleChanged == 'true' }} + runs-on: windows-latest + steps: + - name: checkout + uses: actions/checkout@v6 + with: + fetch-depth: 1000 + - name: Windows Elevated Others + uses: "./.github/actions/test/windows" + with: + purpose: ElevatedPesterTests + tagSet: Others + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + xunit_tests: + name: xUnit Tests + needs: + - changes + if: ${{ needs.changes.outputs.source == 'true' || needs.changes.outputs.buildModuleChanged == 'true' }} + uses: ./.github/workflows/xunit-tests.yml + with: + runner_os: windows-latest + test_results_artifact_name: testResults-xunit + analyze: + name: CodeQL Analysis + needs: changes + if: ${{ needs.changes.outputs.source == 'true' || needs.changes.outputs.buildModuleChanged == 'true' }} + uses: ./.github/workflows/analyze-reusable.yml + permissions: + actions: read + contents: read + security-events: write + with: + runner_os: windows-latest + windows_packaging: + name: Windows Packaging + needs: + - changes + if: ${{ needs.changes.outputs.packagingChanged == 'true' }} + uses: ./.github/workflows/windows-packaging-reusable.yml + ready_to_merge: + name: windows ready to merge + needs: + - xunit_tests + - windows_test_elevated_ci + - windows_test_elevated_others + - windows_test_unelevated_ci + - windows_test_unelevated_others + - analyze + - windows_packaging + if: always() + uses: PowerShell/compliance/.github/workflows/ready-to-merge.yml@v1.0.0 + with: + needs_context: ${{ toJson(needs) }} diff --git a/.github/workflows/windows-packaging-reusable.yml b/.github/workflows/windows-packaging-reusable.yml new file mode 100644 index 00000000000..bb4873adeb3 --- /dev/null +++ b/.github/workflows/windows-packaging-reusable.yml @@ -0,0 +1,89 @@ +name: Windows Packaging (Reusable) + +on: + workflow_call: + +env: + GIT_CONFIG_PARAMETERS: "'core.autocrlf=false'" + DOTNET_CLI_TELEMETRY_OPTOUT: 1 + POWERSHELL_TELEMETRY_OPTOUT: 1 + DOTNET_NOLOGO: 1 + __SuppressAnsiEscapeSequences: 1 + nugetMultiFeedWarnLevel: none + SYSTEM_ARTIFACTSDIRECTORY: ${{ github.workspace }}/artifacts + BUILD_ARTIFACTSTAGINGDIRECTORY: ${{ github.workspace }}/artifacts + +jobs: + package: + name: ${{ matrix.architecture }} - ${{ matrix.channel }} + runs-on: windows-latest + strategy: + fail-fast: false + matrix: + include: + - architecture: x64 + channel: preview + runtimePrefix: win7 + - architecture: x86 + channel: stable + runtimePrefix: win7 + - architecture: x86 + channel: preview + runtimePrefix: win7 + - architecture: arm64 + channel: preview + runtimePrefix: win + + steps: + - name: Checkout + uses: actions/checkout@v6 + with: + fetch-depth: 1000 + + - name: Capture Environment + if: success() || failure() + run: | + Import-Module .\tools\ci.psm1 + Show-Environment + shell: pwsh + + - name: Capture PowerShell Version Table + if: success() || failure() + run: | + $PSVersionTable + shell: pwsh + + - name: Switch to Public Feeds + if: success() + run: | + Import-Module .\tools\ci.psm1 + Switch-PSNugetConfig -Source Public + shell: pwsh + + - name: Setup .NET + uses: actions/setup-dotnet@v5 + with: + global-json-file: ./global.json + + - name: Bootstrap + if: success() + run: | + Import-Module .\tools\ci.psm1 + Invoke-CIInstall -SkipUser + shell: pwsh + + - name: Build and Package + run: | + Import-Module .\tools\ci.psm1 + New-CodeCoverageAndTestPackage + Invoke-CIFinish -Runtime ${{ matrix.runtimePrefix }}-${{ matrix.architecture }} -channel ${{ matrix.channel }} + shell: pwsh + + - name: Upload Build Artifacts + if: always() + uses: actions/upload-artifact@v6 + with: + name: windows-packaging-${{ matrix.architecture }}-${{ matrix.channel }} + path: | + ${{ github.workspace }}/artifacts/**/* + !${{ github.workspace }}/artifacts/**/*.pdb diff --git a/.github/workflows/xunit-tests.yml b/.github/workflows/xunit-tests.yml new file mode 100644 index 00000000000..5d225446cb7 --- /dev/null +++ b/.github/workflows/xunit-tests.yml @@ -0,0 +1,53 @@ +name: xUnit Tests (Reusable) + +on: + workflow_call: + inputs: + runner_os: + description: 'Runner OS for xUnit tests' + type: string + required: false + default: ubuntu-latest + test_results_artifact_name: + description: 'Artifact name for xUnit test results directory' + type: string + required: false + default: testResults-xunit + +jobs: + xunit: + name: Run xUnit Tests + runs-on: ${{ inputs.runner_os }} + steps: + - name: Checkout + uses: actions/checkout@v6 + with: + fetch-depth: 1000 + + - name: Setup .NET + uses: actions/setup-dotnet@v5 + with: + global-json-file: ./global.json + + - name: Bootstrap + shell: pwsh + run: | + Import-Module ./tools/ci.psm1 + Invoke-CIInstall -SkipUser + Sync-PSTags -AddRemoteIfMissing + + - name: Build PowerShell and run xUnit tests + shell: pwsh + run: | + Import-Module ./tools/ci.psm1 + Start-PSBuild + Write-Host "Running full xUnit test suite (no skipping)..." + Invoke-CIxUnit + Write-Host "Completed xUnit test run." + + - name: Upload xUnit results + uses: actions/upload-artifact@v6 + if: always() + with: + name: ${{ inputs.test_results_artifact_name }} + path: ${{ github.workspace }}/xUnitTestResults.xml diff --git a/.gitignore b/.gitignore index 500239db4e9..f115e61e22d 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,6 @@ bin/ obj/ +.ionide/ project.lock.json *-tests.xml /debug/ @@ -10,6 +11,7 @@ project.lock.json # dotnet cli install/uninstall scripts dotnet-install.ps1 dotnet-install.sh +dotnet-install.sh.* dotnet-uninstall-pkgs.sh dotnet-uninstall-debian-packages.sh @@ -21,6 +23,12 @@ dotnet-uninstall-debian-packages.sh # VS auto-generated files for csproj files *.csproj.user +# Visual Studio IDE directory +.vs/ + +# VSCode directories that are not at the repository root +/**/.vscode/ + # Project Rider IDE files .idea.powershell/ @@ -28,45 +36,88 @@ dotnet-uninstall-debian-packages.sh *.exe *.msi *.appx +*.msix # Ignore binaries and symbols *.pdb *.dll +*.wixpdb # Ignore packages *.deb +*.tar.gz *.zip *.rpm *.pkg *.nupkg - -# ignore the version file as it is generated at build time -powershell.version - -# ignore the telemetry semaphore file -DELETE_ME_TO_DISABLE_CONSOLEHOST_TELEMETRY +*.AppImage # default location for produced nuget packages /nuget-artifacts -# generated man files -/assets/powershell.1* - # resgen output gen # Per repo profile .profile.ps1 -#VS Code files -.vscode - -# OS X +# macOS .DS_Store +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns +.com.apple.timemachine.donotpresent +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk +.AppleDouble +.LSOverride # TestsResults TestsResults*.xml +ParallelXUnitResults.xml +xUnitResults.xml + +# Attack Surface Analyzer results +asa-results/ # Resharper settings PowerShell.sln.DotSettings.user +*.msp +StyleCop.Cache + +# Ignore SelfSignedCertificate autogenerated files +test/tools/Modules/SelfSignedCertificate/ + +# BenchmarkDotNet artifacts +test/perf/BenchmarkDotNet.Artifacts/ + +# Test generated module +test/tools/Modules/Microsoft.PowerShell.NamedPipeConnection/ + +# Test generated startup profile +StartupProfileData-NonInteractive + +# Ignore logfiles +logfile/* + +# Ignore nuget.config because it is dynamically generated +nuget.config + +# Ignore MSBuild Binary Logs +msbuild.binlog + +# Ignore gzip files in the manpage folder +assets/manpage/*.gz + +# Ignore files and folders generated by some gh cli extensions +tmp/* +.env.local +# Ignore CTRF report files +crtf/* diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 17765f6837c..00000000000 --- a/.gitmodules +++ /dev/null @@ -1,9 +0,0 @@ -[submodule "src/Modules/Pester"] - path = src/Modules/Shared/Pester - url = https://github.com/PowerShell/psl-pester.git - branch = develop - ignore = dirty -[submodule "src/libpsl-native/test/googletest"] - path = src/libpsl-native/test/googletest - url = https://github.com/google/googletest.git - ignore = dirty diff --git a/.globalconfig b/.globalconfig new file mode 100644 index 00000000000..21ecbc766aa --- /dev/null +++ b/.globalconfig @@ -0,0 +1,2289 @@ +is_global = true + +# CA1000: Do not declare static members on generic types +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1000 +dotnet_diagnostic.CA1000.severity = warning +dotnet_code_quality.CA1000.api_surface = all + +# CA1001: Types that own disposable fields should be disposable +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1001 +dotnet_diagnostic.CA1001.severity = silent + +# CA1002: Do not expose generic lists +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1002 +dotnet_diagnostic.CA1002.severity = none + +# CA1003: Use generic event handler instances +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1003 +dotnet_diagnostic.CA1003.severity = warning +dotnet_code_quality.CA1003.api_surface = private, internal + +# CA1005: Avoid excessive parameters on generic types +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1005 +dotnet_diagnostic.CA1005.severity = none + +# CA1008: Enums should have zero value +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1008 +dotnet_diagnostic.CA1008.severity = none +dotnet_code_quality.CA1008.api_surface = public + +# CA1010: Generic interface should also be implemented +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1010 +dotnet_diagnostic.CA1010.severity = silent +dotnet_code_quality.CA1010.api_surface = public + +# CA1012: Abstract types should not have public constructors +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1012 +dotnet_diagnostic.CA1012.severity = warning +dotnet_code_quality.CA1012.api_surface = all + +# CA1014: Mark assemblies with CLSCompliant +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1014 +dotnet_diagnostic.CA1014.severity = none + +# CA1016: Mark assemblies with assembly version +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1016 +dotnet_diagnostic.CA1016.severity = warning + +# CA1017: Mark assemblies with ComVisible +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1017 +dotnet_diagnostic.CA1017.severity = none + +# CA1018: Mark attributes with AttributeUsageAttribute +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1018 +dotnet_diagnostic.CA1018.severity = warning + +# CA1019: Define accessors for attribute arguments +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1019 +dotnet_diagnostic.CA1019.severity = none + +# CA1021: Avoid out parameters +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1021 +dotnet_diagnostic.CA1021.severity = none + +# CA1024: Use properties where appropriate +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1024 +dotnet_diagnostic.CA1024.severity = none +dotnet_code_quality.CA1024.api_surface = public + +# CA1027: Mark enums with FlagsAttribute +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1027 +dotnet_diagnostic.CA1027.severity = none +dotnet_code_quality.CA1027.api_surface = public + +# CA1028: Enum Storage should be Int32 +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1028 +dotnet_diagnostic.CA1028.severity = none +dotnet_code_quality.CA1028.api_surface = public + +# CA1030: Use events where appropriate +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1030 +dotnet_diagnostic.CA1030.severity = none +dotnet_code_quality.CA1030.api_surface = public + +# CA1031: Do not catch general exception types +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1031 +dotnet_diagnostic.CA1031.severity = none + +# CA1032: Implement standard exception constructors +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1032 +dotnet_diagnostic.CA1032.severity = none + +# CA1033: Interface methods should be callable by child types +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1033 +dotnet_diagnostic.CA1033.severity = none + +# CA1034: Nested types should not be visible +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1034 +dotnet_diagnostic.CA1034.severity = none + +# CA1036: Override methods on comparable types +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1036 +dotnet_diagnostic.CA1036.severity = silent +dotnet_code_quality.CA1036.api_surface = public + +# CA1040: Avoid empty interfaces +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1040 +dotnet_diagnostic.CA1040.severity = none +dotnet_code_quality.CA1040.api_surface = public + +# CA1041: Provide ObsoleteAttribute message +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1041 +dotnet_diagnostic.CA1041.severity = warning +dotnet_code_quality.CA1041.api_surface = public + +# CA1043: Use Integral Or String Argument For Indexers +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1043 +dotnet_diagnostic.CA1043.severity = warning +dotnet_code_quality.CA1043.api_surface = all + +# CA1044: Properties should not be write only +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1044 +dotnet_diagnostic.CA1044.severity = none +dotnet_code_quality.CA1044.api_surface = public + +# CA1045: Do not pass types by reference +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1045 +dotnet_diagnostic.CA1045.severity = none + +# CA1046: Do not overload equality operator on reference types +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1046 +dotnet_diagnostic.CA1046.severity = none + +# CA1047: Do not declare protected member in sealed type +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1047 +dotnet_diagnostic.CA1047.severity = warning + +# CA1050: Declare types in namespaces +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1050 +dotnet_diagnostic.CA1050.severity = warning + +# CA1051: Do not declare visible instance fields +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1051 +dotnet_diagnostic.CA1051.severity = silent +dotnet_code_quality.CA1051.api_surface = public + +# CA1052: Static holder types should be Static or NotInheritable +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1052 +dotnet_diagnostic.CA1052.severity = warning +dotnet_code_quality.CA1052.api_surface = all + +# CA1054: URI-like parameters should not be strings +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1054 +dotnet_diagnostic.CA1054.severity = none +dotnet_code_quality.CA1054.api_surface = public + +# CA1055: URI-like return values should not be strings +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1055 +dotnet_diagnostic.CA1055.severity = none +dotnet_code_quality.CA1055.api_surface = public + +# CA1056: URI-like properties should not be strings +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1056 +dotnet_diagnostic.CA1056.severity = none +dotnet_code_quality.CA1056.api_surface = public + +# CA1058: Types should not extend certain base types +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1058 +dotnet_diagnostic.CA1058.severity = none +dotnet_code_quality.CA1058.api_surface = public + +# CA1060: Move pinvokes to native methods class +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1060 +dotnet_diagnostic.CA1060.severity = none + +# CA1061: Do not hide base class methods +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1061 +dotnet_diagnostic.CA1061.severity = warning + +# CA1062: Validate arguments of public methods +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1062 +dotnet_diagnostic.CA1062.severity = none + +# CA1063: Implement IDisposable Correctly +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1063 +dotnet_diagnostic.CA1063.severity = none +dotnet_code_quality.CA1063.api_surface = public + +# CA1064: Exceptions should be public +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1064 +dotnet_diagnostic.CA1064.severity = none + +# CA1065: Do not raise exceptions in unexpected locations +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1065 +dotnet_diagnostic.CA1065.severity = warning + +# CA1066: Implement IEquatable when overriding Object.Equals +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1066 +dotnet_diagnostic.CA1066.severity = none + +# CA1067: Override Object.Equals(object) when implementing IEquatable +# # https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1067 +dotnet_diagnostic.CA1067.severity = warning + +# CA1068: CancellationToken parameters must come last +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1068 +dotnet_diagnostic.CA1068.severity = warning + +# CA1069: Enums values should not be duplicated +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1069 +dotnet_diagnostic.CA1069.severity = suggestion + +# CA1070: Do not declare event fields as virtual +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1070 +dotnet_diagnostic.CA1070.severity = warning + +# CA1200: Avoid using cref tags with a prefix +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1200 +dotnet_diagnostic.CA1200.severity = warning + +# CA1303: Do not pass literals as localized parameters +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1303 +dotnet_diagnostic.CA1303.severity = none + +# CA1304: Specify CultureInfo +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1304 +dotnet_diagnostic.CA1304.severity = silent + +# CA1305: Specify IFormatProvider +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1305 +dotnet_diagnostic.CA1305.severity = silent + +# CA1307: Specify StringComparison for clarity +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1307 +dotnet_diagnostic.CA1307.severity = none + +# CA1308: Normalize strings to uppercase +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1308 +dotnet_diagnostic.CA1308.severity = none + +# CA1309: Use ordinal string comparison +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1309 +dotnet_diagnostic.CA1309.severity = silent + +# CA1310: Specify StringComparison for correctness +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1310 +dotnet_diagnostic.CA1310.severity = silent + +# CA1401: P/Invokes should not be visible +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1401 +dotnet_diagnostic.CA1401.severity = warning + +# CA1416: Validate platform compatibility +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1416 +dotnet_diagnostic.CA1416.severity = warning + +# CA1417: Do not use 'OutAttribute' on string parameters for P/Invokes +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1417 +dotnet_diagnostic.CA1417.severity = warning + +# CA1418: Use valid platform string +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1418 +dotnet_diagnostic.CA1418.severity = warning + +# CA1501: Avoid excessive inheritance +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1501 +dotnet_diagnostic.CA1501.severity = none + +# CA1502: Avoid excessive complexity +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1502 +dotnet_diagnostic.CA1502.severity = none + +# CA1505: Avoid unmaintainable code +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1505 +dotnet_diagnostic.CA1505.severity = none + +# CA1506: Avoid excessive class coupling +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1506 +dotnet_diagnostic.CA1506.severity = none + +# CA1507: Use nameof to express symbol names +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1507 +dotnet_diagnostic.CA1507.severity = suggestion + +# CA1508: Avoid dead conditional code +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1508 +dotnet_diagnostic.CA1508.severity = none + +# CA1509: Invalid entry in code metrics rule specification file +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1509 +dotnet_diagnostic.CA1509.severity = none + +# CA1700: Do not name enum values 'Reserved' +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1700 +dotnet_diagnostic.CA1700.severity = none + +# CA1707: Identifiers should not contain underscores +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1707 +dotnet_diagnostic.CA1707.severity = silent + +# CA1708: Identifiers should differ by more than case +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1708 +dotnet_diagnostic.CA1708.severity = silent +dotnet_code_quality.CA1708.api_surface = public + +# CA1710: Identifiers should have correct suffix +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1710 +dotnet_diagnostic.CA1710.severity = silent +dotnet_code_quality.CA1710.api_surface = public + +# CA1711: Identifiers should not have incorrect suffix +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1711 +dotnet_diagnostic.CA1711.severity = silent +dotnet_code_quality.CA1711.api_surface = public + +# CA1712: Do not prefix enum values with type name +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1712 +dotnet_diagnostic.CA1712.severity = silent + +# CA1713: Events should not have 'Before' or 'After' prefix +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1713 +dotnet_diagnostic.CA1713.severity = none + +# CA1715: Identifiers should have correct prefix +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1715 +dotnet_diagnostic.CA1715.severity = silent +dotnet_code_quality.CA1715.api_surface = public + +# CA1716: Identifiers should not match keywords +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1716 +dotnet_diagnostic.CA1716.severity = silent +dotnet_code_quality.CA1716.api_surface = public + +# CA1720: Identifier contains type name +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1720 +dotnet_diagnostic.CA1720.severity = silent +dotnet_code_quality.CA1720.api_surface = public + +# CA1721: Property names should not match get methods +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1721 +dotnet_diagnostic.CA1721.severity = none +dotnet_code_quality.CA1721.api_surface = public + +# CA1724: Type names should not match namespaces +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1724 +dotnet_diagnostic.CA1724.severity = none + +# CA1725: Parameter names should match base declaration +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1725 +dotnet_diagnostic.CA1725.severity = silent +dotnet_code_quality.CA1725.api_surface = public + +# CA1801: Review unused parameters +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1801 +dotnet_diagnostic.CA1801.severity = none +dotnet_code_quality.CA1801.api_surface = all + +# CA1802: Use literals where appropriate +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1802 +dotnet_diagnostic.CA1802.severity = none +dotnet_code_quality.CA1802.api_surface = public + +# CA1805: Do not initialize unnecessarily +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1805 +dotnet_diagnostic.CA1805.severity = suggestion + +# CA1806: Do not ignore method results +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1806 +dotnet_diagnostic.CA1806.severity = suggestion + +# CA1810: Initialize reference type static fields inline +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1810 +dotnet_diagnostic.CA1810.severity = none + +# CA1812: Avoid uninstantiated internal classes +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1812 +dotnet_diagnostic.CA1812.severity = warning + +# CA1813: Avoid unsealed attributes +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1813 +dotnet_diagnostic.CA1813.severity = none + +# CA1814: Prefer jagged arrays over multidimensional +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1814 +dotnet_diagnostic.CA1814.severity = none + +# CA1815: Override equals and operator equals on value types +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1815 +dotnet_diagnostic.CA1815.severity = none +dotnet_code_quality.CA1815.api_surface = public + +# CA1816: Dispose methods should call SuppressFinalize +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1816 +dotnet_diagnostic.CA1816.severity = warning + +# CA1819: Properties should not return arrays +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1819 +dotnet_diagnostic.CA1819.severity = none +dotnet_code_quality.CA1819.api_surface = public + +# CA1820: Test for empty strings using string length +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1820 +dotnet_diagnostic.CA1820.severity = none + +# CA1821: Remove empty Finalizers +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1821 +dotnet_diagnostic.CA1821.severity = warning + +# CA1822: Mark members as static +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1822 +dotnet_diagnostic.CA1822.severity = warning +dotnet_code_quality.CA1822.api_surface = private + +# CA1823: Avoid unused private fields +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1823 +dotnet_diagnostic.CA1823.severity = none + +# CA1824: Mark assemblies with NeutralResourcesLanguageAttribute +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1824 +dotnet_diagnostic.CA1824.severity = warning + +# CA1825: Avoid zero-length array allocations +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1825 +dotnet_diagnostic.CA1825.severity = warning + +# CA1826: Do not use Enumerable methods on indexable collections +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1826 +dotnet_diagnostic.CA1826.severity = warning + +# CA1827: Do not use Count() or LongCount() when Any() can be used +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1827 +dotnet_diagnostic.CA1827.severity = warning + +# CA1828: Do not use CountAsync() or LongCountAsync() when AnyAsync() can be used +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1828 +dotnet_diagnostic.CA1828.severity = warning + +# CA1829: Use Length/Count property instead of Count() when available +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1829 +dotnet_diagnostic.CA1829.severity = warning + +# CA1830: Prefer strongly-typed Append and Insert method overloads on StringBuilder +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1830 +dotnet_diagnostic.CA1830.severity = warning + +# CA1831: Use AsSpan or AsMemory instead of Range-based indexers when appropriate +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1831 +dotnet_diagnostic.CA1831.severity = warning + +# CA1832: Use AsSpan or AsMemory instead of Range-based indexers when appropriate +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1832 +dotnet_diagnostic.CA1832.severity = warning + +# CA1833: Use AsSpan or AsMemory instead of Range-based indexers when appropriate +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1833 +dotnet_diagnostic.CA1833.severity = warning + +# CA1834: Consider using 'StringBuilder.Append(char)' when applicable +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1834 +dotnet_diagnostic.CA1834.severity = warning + +# CA1835: Prefer the 'Memory'-based overloads for 'ReadAsync' and 'WriteAsync' +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1835 +dotnet_diagnostic.CA1835.severity = suggestion + +# CA1836: Prefer IsEmpty over Count +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1836 +dotnet_diagnostic.CA1836.severity = warning + +# CA1837: Use 'Environment.ProcessId' +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1837 +dotnet_diagnostic.CA1837.severity = warning + +# CA1838: Avoid 'StringBuilder' parameters for P/Invokes +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1838 +dotnet_diagnostic.CA1838.severity = silent + +# CA1839: Use 'Environment.ProcessPath' +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1839 +dotnet_diagnostic.CA1839.severity = warning + +# CA1840: Use 'Environment.CurrentManagedThreadId' +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1840 +dotnet_diagnostic.CA1840.severity = warning + +# CA1841: Prefer Dictionary.Contains methods +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1841 +dotnet_diagnostic.CA1841.severity = warning + +# CA1842: Do not use 'WhenAll' with a single task +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1842 +dotnet_diagnostic.CA1842.severity = warning + +# CA1843: Do not use 'WaitAll' with a single task +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1843 +dotnet_diagnostic.CA1843.severity = warning + +# CA1844: Provide memory-based overrides of async methods when subclassing 'Stream' +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1844 +dotnet_diagnostic.CA1844.severity = warning + +# CA1845: Use span-based 'string.Concat' +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1845 +dotnet_diagnostic.CA1845.severity = warning + +# CA1846: Prefer 'AsSpan' over 'Substring' +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1846 +dotnet_diagnostic.CA1846.severity = warning + +# CA1847: Use char literal for a single character lookup +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1847 +dotnet_diagnostic.CA1847.severity = warning + +# CA1853: Unnecessary call to 'Dictionary.ContainsKey(key)' +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1853 +dotnet_diagnostic.CA1853.severity = warning + +# CA1858: Use 'StartsWith' instead of 'IndexOf' +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1858 +dotnet_diagnostic.CA1858.severity = warning + +# CA1860: Avoid using 'Enumerable.Any()' extension method +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1860 +dotnet_diagnostic.CA1860.severity = warning + +# CA1865: Use char overload +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1865 +dotnet_diagnostic.CA1865.severity = warning + +# CA1866: Use char overload +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1866 +dotnet_diagnostic.CA1866.severity = warning + +# CA1867: Use char overload +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1867 +dotnet_diagnostic.CA1867.severity = warning + +# CA1868: Unnecessary call to 'Contains' for sets +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca1868 +dotnet_diagnostic.CA1868.severity = warning + +# CA2000: Dispose objects before losing scope +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2000 +dotnet_diagnostic.CA2000.severity = none + +# CA2002: Do not lock on objects with weak identity +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2002 +dotnet_diagnostic.CA2002.severity = none + +# CA2007: Consider calling ConfigureAwait on the awaited task +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2007 +dotnet_diagnostic.CA2007.severity = none + +# CA2008: Do not create tasks without passing a TaskScheduler +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2008 +dotnet_diagnostic.CA2008.severity = none + +# CA2009: Do not call ToImmutableCollection on an ImmutableCollection value +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2009 +dotnet_diagnostic.CA2009.severity = warning + +# CA2011: Avoid infinite recursion +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2011 +dotnet_diagnostic.CA2011.severity = warning + +# CA2012: Use ValueTasks correctly +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2012 +dotnet_diagnostic.CA2012.severity = warning + +# CA2013: Do not use ReferenceEquals with value types +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2013 +dotnet_diagnostic.CA2013.severity = warning + +# CA2014: Do not use stackalloc in loops +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2014 +dotnet_diagnostic.CA2014.severity = warning + +# CA2015: Do not define finalizers for types derived from MemoryManager +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2015 +dotnet_diagnostic.CA2015.severity = warning + +# CA2016: Forward the 'CancellationToken' parameter to methods that take one +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2016 +dotnet_diagnostic.CA2016.severity = suggestion + +# CA2021: Do not call Enumerable.Cast or Enumerable.OfType with incompatible types +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2021 +dotnet_diagnostic.CA2021.severity = warning + +# CA2022: Avoid inexact read with 'Stream.Read' +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2022 +dotnet_diagnostic.CA2022.severity = warning + +# CA2100: Review SQL queries for security vulnerabilities +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2100 +dotnet_diagnostic.CA2100.severity = none + +# CA2101: Specify marshaling for P/Invoke string arguments +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2101 +dotnet_diagnostic.CA2101.severity = suggestion + +# CA2109: Review visible event handlers +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2109 +dotnet_diagnostic.CA2109.severity = none + +# CA2119: Seal methods that satisfy private interfaces +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2119 +dotnet_diagnostic.CA2119.severity = none + +# CA2153: Do Not Catch Corrupted State Exceptions +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2153 +dotnet_diagnostic.CA2153.severity = none + +# CA2200: Rethrow to preserve stack details +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2200 +dotnet_diagnostic.CA2200.severity = warning + +# CA2201: Do not raise reserved exception types +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2201 +dotnet_diagnostic.CA2201.severity = silent + +# CA2207: Initialize value type static fields inline +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2207 +dotnet_diagnostic.CA2207.severity = warning + +# CA2208: Instantiate argument exceptions correctly +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2208 +dotnet_diagnostic.CA2208.severity = suggestion +dotnet_code_quality.CA2208.api_surface = all + +# CA2211: Non-constant fields should not be visible +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2211 +dotnet_diagnostic.CA2211.severity = warning + +# CA2213: Disposable fields should be disposed +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2213 +dotnet_diagnostic.CA2213.severity = none + +# CA2214: Do not call overridable methods in constructors +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2214 +dotnet_diagnostic.CA2214.severity = none + +# CA2215: Dispose methods should call base class dispose +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2215 +dotnet_diagnostic.CA2215.severity = silent + +# CA2216: Disposable types should declare finalizer +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2216 +dotnet_diagnostic.CA2216.severity = warning + +# CA2217: Do not mark enums with FlagsAttribute +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2217 +dotnet_diagnostic.CA2217.severity = none +dotnet_code_quality.CA2217.api_surface = public + +# CA2218: Override GetHashCode on overriding Equals +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2218 +dotnet_diagnostic.CA2218.severity = suggestion + +# CA2219: Do not raise exceptions in finally clauses +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2219 +dotnet_diagnostic.CA2219.severity = suggestion + +# CA2224: Override Equals on overloading operator equals +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2224 +dotnet_diagnostic.CA2224.severity = suggestion + +# CA2225: Operator overloads have named alternates +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2225 +dotnet_diagnostic.CA2225.severity = none +dotnet_code_quality.CA2225.api_surface = public + +# CA2226: Operators should have symmetrical overloads +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2226 +dotnet_diagnostic.CA2226.severity = none +dotnet_code_quality.CA2226.api_surface = public + +# CA2227: Collection properties should be read only +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2227 +dotnet_diagnostic.CA2227.severity = none + +# CA2229: Implement serialization constructors +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2229 +dotnet_diagnostic.CA2229.severity = silent + +# CA2231: Overload operator equals on overriding value type Equals +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2231 +dotnet_diagnostic.CA2231.severity = suggestion +dotnet_code_quality.CA2231.api_surface = public + +# CA2234: Pass system uri objects instead of strings +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2234 +dotnet_diagnostic.CA2234.severity = none +dotnet_code_quality.CA2234.api_surface = public + +# CA2235: Mark all non-serializable fields +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2235 +dotnet_diagnostic.CA2235.severity = none + +# CA2237: Mark ISerializable types with serializable +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2237 +dotnet_diagnostic.CA2237.severity = none + +# CA2241: Provide correct arguments to formatting methods +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2241 +dotnet_diagnostic.CA2241.severity = suggestion + +# CA2242: Test for NaN correctly +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2242 +dotnet_diagnostic.CA2242.severity = suggestion + +# CA2243: Attribute string literals should parse correctly +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2243 +dotnet_diagnostic.CA2243.severity = warning + +# CA2244: Do not duplicate indexed element initializations +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2244 +dotnet_diagnostic.CA2244.severity = suggestion + +# CA2245: Do not assign a property to itself +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2245 +dotnet_diagnostic.CA2245.severity = suggestion + +# CA2246: Assigning symbol and its member in the same statement +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2246 +dotnet_diagnostic.CA2246.severity = suggestion + +# CA2247: Argument passed to TaskCompletionSource constructor should be TaskCreationOptions enum instead of TaskContinuationOptions enum +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2247 +dotnet_diagnostic.CA2247.severity = warning + +# CA2248: Provide correct 'enum' argument to 'Enum.HasFlag' +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2248 +dotnet_diagnostic.CA2248.severity = suggestion + +# CA2249: Consider using 'string.Contains' instead of 'string.IndexOf' +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2249 +dotnet_diagnostic.CA2249.severity = warning + +# CA2250: Use 'ThrowIfCancellationRequested' +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2250 +dotnet_diagnostic.CA2250.severity = warning + +# CA2251: Use 'string.Equals' +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2251 +dotnet_diagnostic.CA2251.severity = warning + +# CA2252: This API requires opting into preview features +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2252 +dotnet_diagnostic.CA2251.severity = none + +# CA2300: Do not use insecure deserializer BinaryFormatter +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2300 +dotnet_diagnostic.CA2300.severity = none + +# CA2301: Do not call BinaryFormatter.Deserialize without first setting BinaryFormatter.Binder +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2301 +dotnet_diagnostic.CA2301.severity = none + +# CA2302: Ensure BinaryFormatter.Binder is set before calling BinaryFormatter.Deserialize +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2302 +dotnet_diagnostic.CA2302.severity = none + +# CA2305: Do not use insecure deserializer LosFormatter +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2305 +dotnet_diagnostic.CA2305.severity = none + +# CA2310: Do not use insecure deserializer NetDataContractSerializer +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2310 +dotnet_diagnostic.CA2310.severity = none + +# CA2311: Do not deserialize without first setting NetDataContractSerializer.Binder +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2311 +dotnet_diagnostic.CA2311.severity = none + +# CA2312: Ensure NetDataContractSerializer.Binder is set before deserializing +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2312 +dotnet_diagnostic.CA2312.severity = none + +# CA2315: Do not use insecure deserializer ObjectStateFormatter +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2315 +dotnet_diagnostic.CA2315.severity = none + +# CA2321: Do not deserialize with JavaScriptSerializer using a SimpleTypeResolver +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2321 +dotnet_diagnostic.CA2321.severity = none + +# CA2322: Ensure JavaScriptSerializer is not initialized with SimpleTypeResolver before deserializing +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2322 +dotnet_diagnostic.CA2322.severity = none + +# CA2326: Do not use TypeNameHandling values other than None +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2326 +dotnet_diagnostic.CA2326.severity = none + +# CA2327: Do not use insecure JsonSerializerSettings +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2327 +dotnet_diagnostic.CA2327.severity = none + +# CA2328: Ensure that JsonSerializerSettings are secure +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2328 +dotnet_diagnostic.CA2328.severity = none + +# CA2329: Do not deserialize with JsonSerializer using an insecure configuration +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2329 +dotnet_diagnostic.CA2329.severity = none + +# CA2330: Ensure that JsonSerializer has a secure configuration when deserializing +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2330 +dotnet_diagnostic.CA2330.severity = none + +# CA2350: Do not use DataTable.ReadXml() with untrusted data +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2350 +dotnet_diagnostic.CA2350.severity = none + +# CA2351: Do not use DataSet.ReadXml() with untrusted data +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2351 +dotnet_diagnostic.CA2351.severity = none + +# CA2352: Unsafe DataSet or DataTable in serializable type can be vulnerable to remote code execution attacks +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2352 +dotnet_diagnostic.CA2352.severity = none + +# CA2353: Unsafe DataSet or DataTable in serializable type +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2353 +dotnet_diagnostic.CA2353.severity = none + +# CA2354: Unsafe DataSet or DataTable in deserialized object graph can be vulnerable to remote code execution attacks +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2354 +dotnet_diagnostic.CA2354.severity = none + +# CA2355: Unsafe DataSet or DataTable type found in deserializable object graph +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2355 +dotnet_diagnostic.CA2355.severity = none + +# CA2356: Unsafe DataSet or DataTable type in web deserializable object graph +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2356 +dotnet_diagnostic.CA2356.severity = none + +# CA2361: Ensure autogenerated class containing DataSet.ReadXml() is not used with untrusted data +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2361 +dotnet_diagnostic.CA2361.severity = none + +# CA2362: Unsafe DataSet or DataTable in autogenerated serializable type can be vulnerable to remote code execution attacks +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca2362 +dotnet_diagnostic.CA2362.severity = none + +# CA3001: Review code for SQL injection vulnerabilities +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca3001 +dotnet_diagnostic.CA3001.severity = none + +# CA3002: Review code for XSS vulnerabilities +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca3002 +dotnet_diagnostic.CA3002.severity = none + +# CA3003: Review code for file path injection vulnerabilities +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca3003 +dotnet_diagnostic.CA3003.severity = none + +# CA3004: Review code for information disclosure vulnerabilities +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca3004 +dotnet_diagnostic.CA3004.severity = none + +# CA3005: Review code for LDAP injection vulnerabilities +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca3005 +dotnet_diagnostic.CA3005.severity = none + +# CA3006: Review code for process command injection vulnerabilities +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca3006 +dotnet_diagnostic.CA3006.severity = none + +# CA3007: Review code for open redirect vulnerabilities +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca3007 +dotnet_diagnostic.CA3007.severity = none + +# CA3008: Review code for XPath injection vulnerabilities +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca3008 +dotnet_diagnostic.CA3008.severity = none + +# CA3009: Review code for XML injection vulnerabilities +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca3009 +dotnet_diagnostic.CA3009.severity = none + +# CA3010: Review code for XAML injection vulnerabilities +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca3010 +dotnet_diagnostic.CA3010.severity = none + +# CA3011: Review code for DLL injection vulnerabilities +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca3011 +dotnet_diagnostic.CA3011.severity = none + +# CA3012: Review code for regex injection vulnerabilities +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca3012 +dotnet_diagnostic.CA3012.severity = none + +# CA3061: Do Not Add Schema By URL +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca3061 +dotnet_diagnostic.CA3061.severity = silent + +# CA3075: Insecure DTD processing in XML +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca3075 +dotnet_diagnostic.CA3075.severity = silent + +# CA3076: Insecure XSLT script processing. +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca3076 +dotnet_diagnostic.CA3076.severity = silent + +# CA3077: Insecure Processing in API Design, XmlDocument and XmlTextReader +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca3077 +dotnet_diagnostic.CA3077.severity = silent + +# CA3147: Mark Verb Handlers With Validate Antiforgery Token +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca3147 +dotnet_diagnostic.CA3147.severity = silent + +# CA5350: Do Not Use Weak Cryptographic Algorithms +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca5350 +dotnet_diagnostic.CA5350.severity = silent + +# CA5351: Do Not Use Broken Cryptographic Algorithms +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca5351 +dotnet_diagnostic.CA5351.severity = silent + +# CA5358: Review cipher mode usage with cryptography experts +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca5358 +dotnet_diagnostic.CA5358.severity = none + +# CA5359: Do Not Disable Certificate Validation +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca5359 +dotnet_diagnostic.CA5359.severity = silent + +# CA5360: Do Not Call Dangerous Methods In Deserialization +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca5360 +dotnet_diagnostic.CA5360.severity = silent + +# CA5361: Do Not Disable SChannel Use of Strong Crypto +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca5361 +dotnet_diagnostic.CA5361.severity = none + +# CA5362: Potential reference cycle in deserialized object graph +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca5362 +dotnet_diagnostic.CA5362.severity = none + +# CA5363: Do Not Disable Request Validation +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca5363 +dotnet_diagnostic.CA5363.severity = silent + +# CA5364: Do Not Use Deprecated Security Protocols +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca5364 +dotnet_diagnostic.CA5364.severity = silent + +# CA5365: Do Not Disable HTTP Header Checking +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca5365 +dotnet_diagnostic.CA5365.severity = silent + +# CA5366: Use XmlReader For DataSet Read Xml +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca5366 +dotnet_diagnostic.CA5366.severity = silent + +# CA5367: Do Not Serialize Types With Pointer Fields +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca5367 +dotnet_diagnostic.CA5367.severity = none + +# CA5368: Set ViewStateUserKey For Classes Derived From Page +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca5368 +dotnet_diagnostic.CA5368.severity = silent + +# CA5369: Use XmlReader For Deserialize +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca5369 +dotnet_diagnostic.CA5369.severity = silent + +# CA5370: Use XmlReader For Validating Reader +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca5370 +dotnet_diagnostic.CA5370.severity = silent + +# CA5371: Use XmlReader For Schema Read +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca5371 +dotnet_diagnostic.CA5371.severity = silent + +# CA5372: Use XmlReader For XPathDocument +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca5372 +dotnet_diagnostic.CA5372.severity = silent + +# CA5373: Do not use obsolete key derivation function +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca5373 +dotnet_diagnostic.CA5373.severity = silent + +# CA5374: Do Not Use XslTransform +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca5374 +dotnet_diagnostic.CA5374.severity = silent + +# CA5375: Do Not Use Account Shared Access Signature +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca5375 +dotnet_diagnostic.CA5375.severity = none + +# CA5376: Use SharedAccessProtocol HttpsOnly +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca5376 +dotnet_diagnostic.CA5376.severity = none + +# CA5377: Use Container Level Access Policy +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca5377 +dotnet_diagnostic.CA5377.severity = none + +# CA5378: Do not disable ServicePointManagerSecurityProtocols +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca5378 +dotnet_diagnostic.CA5378.severity = none + +# CA5379: Do Not Use Weak Key Derivation Function Algorithm +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca5379 +dotnet_diagnostic.CA5379.severity = silent + +# CA5380: Do Not Add Certificates To Root Store +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca5380 +dotnet_diagnostic.CA5380.severity = none + +# CA5381: Ensure Certificates Are Not Added To Root Store +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca5381 +dotnet_diagnostic.CA5381.severity = none + +# CA5382: Use Secure Cookies In ASP.Net Core +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca5382 +dotnet_diagnostic.CA5382.severity = none + +# CA5383: Ensure Use Secure Cookies In ASP.Net Core +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca5383 +dotnet_diagnostic.CA5383.severity = none + +# CA5384: Do Not Use Digital Signature Algorithm (DSA) +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca5384 +dotnet_diagnostic.CA5384.severity = silent + +# CA5385: Use Rivest–Shamir–Adleman (RSA) Algorithm With Sufficient Key Size +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca5385 +dotnet_diagnostic.CA5385.severity = silent + +# CA5386: Avoid hardcoding SecurityProtocolType value +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca5386 +dotnet_diagnostic.CA5386.severity = none + +# CA5387: Do Not Use Weak Key Derivation Function With Insufficient Iteration Count +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca5387 +dotnet_diagnostic.CA5387.severity = none + +# CA5388: Ensure Sufficient Iteration Count When Using Weak Key Derivation Function +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca5388 +dotnet_diagnostic.CA5388.severity = none + +# CA5389: Do Not Add Archive Item's Path To The Target File System Path +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca5389 +dotnet_diagnostic.CA5389.severity = none + +# CA5390: Do not hard-code encryption key +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca5390 +dotnet_diagnostic.CA5390.severity = none + +# CA5391: Use antiforgery tokens in ASP.NET Core MVC controllers +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca5391 +dotnet_diagnostic.CA5391.severity = none + +# CA5392: Use DefaultDllImportSearchPaths attribute for P/Invokes +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca5392 +dotnet_diagnostic.CA5392.severity = none + +# CA5393: Do not use unsafe DllImportSearchPath value +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca5393 +dotnet_diagnostic.CA5393.severity = none + +# CA5394: Do not use insecure randomness +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca5394 +dotnet_diagnostic.CA5394.severity = none + +# CA5395: Miss HttpVerb attribute for action methods +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca5395 +dotnet_diagnostic.CA5395.severity = none + +# CA5396: Set HttpOnly to true for HttpCookie +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca5396 +dotnet_diagnostic.CA5396.severity = none + +# CA5397: Do not use deprecated SslProtocols values +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca5397 +dotnet_diagnostic.CA5397.severity = silent + +# CA5398: Avoid hardcoded SslProtocols values +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca5398 +dotnet_diagnostic.CA5398.severity = none + +# CA5399: HttpClients should enable certificate revocation list checks +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca5399 +dotnet_diagnostic.CA5399.severity = none + +# CA5400: Ensure HttpClient certificate revocation list check is not disabled +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca5400 +dotnet_diagnostic.CA5400.severity = none + +# CA5401: Do not use CreateEncryptor with non-default IV +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca5401 +dotnet_diagnostic.CA5401.severity = none + +# CA5402: Use CreateEncryptor with the default IV +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca5402 +dotnet_diagnostic.CA5402.severity = none + +# CA5403: Do not hard-code certificate +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/ca5403 +dotnet_diagnostic.CA5403.severity = none + +# IL3000: Avoid using accessing Assembly file path when publishing as a single-file +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/il3000 +dotnet_diagnostic.IL3000.severity = warning + +# IL3001: Avoid using accessing Assembly file path when publishing as a single-file +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/il3001 +dotnet_diagnostic.IL3001.severity = warning + +# IL3002: Using member with RequiresAssemblyFilesAttribute can break functionality when embedded in a single-file app +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/quality-rules/il3002 +dotnet_diagnostic.IL3002.severity = warning + +# DOC100: PlaceTextInParagraphs +# https://github.com/DotNetAnalyzers/DocumentationAnalyzers/blob/master/docs/DOC100.md +dotnet_diagnostic.DOC100.severity = none + +# DOC101: UseChildBlocksConsistently +# https://github.com/DotNetAnalyzers/DocumentationAnalyzers/blob/master/docs/DOC101.md +dotnet_diagnostic.DOC101.severity = none + +# DOC102: UseChildBlocksConsistentlyAcrossElementsOfTheSameKind +# https://github.com/DotNetAnalyzers/DocumentationAnalyzers/blob/master/docs/DOC102.md +dotnet_diagnostic.DOC102.severity = none + +# DOC103: UseUnicodeCharacters +# https://github.com/DotNetAnalyzers/DocumentationAnalyzers/blob/master/docs/DOC103.md +dotnet_diagnostic.DOC103.severity = none + +# DOC104: UseSeeLangword +# https://github.com/DotNetAnalyzers/DocumentationAnalyzers/blob/master/docs/DOC104.md +dotnet_diagnostic.DOC104.severity = suggestion + +# DOC105: UseParamref +# https://github.com/DotNetAnalyzers/DocumentationAnalyzers/blob/master/docs/DOC105.md +dotnet_diagnostic.DOC105.severity = none + +# DOC106: UseTypeparamref +# https://github.com/DotNetAnalyzers/DocumentationAnalyzers/blob/master/docs/DOC106.md +dotnet_diagnostic.DOC106.severity = none + +# DOC107: UseSeeCref +# https://github.com/DotNetAnalyzers/DocumentationAnalyzers/blob/master/docs/DOC107.md +dotnet_diagnostic.DOC107.severity = none + +# DOC108: AvoidEmptyParagraphs +# https://github.com/DotNetAnalyzers/DocumentationAnalyzers/blob/master/docs/DOC108.md +dotnet_diagnostic.DOC108.severity = none + +# DOC200: UseXmlDocumentationSyntax +# https://github.com/DotNetAnalyzers/DocumentationAnalyzers/blob/master/docs/DOC200.md +dotnet_diagnostic.DOC200.severity = none + +# DOC201: ItemShouldHaveDescription +# https://github.com/DotNetAnalyzers/DocumentationAnalyzers/blob/master/docs/DOC201.md +dotnet_diagnostic.DOC201.severity = none + +# DOC202: UseSectionElementsCorrectly +# https://github.com/DotNetAnalyzers/DocumentationAnalyzers/blob/master/docs/DOC202.md +dotnet_diagnostic.DOC202.severity = none + +# DOC203: UseBlockElementsCorrectly +# https://github.com/DotNetAnalyzers/DocumentationAnalyzers/blob/master/docs/DOC203.md +dotnet_diagnostic.DOC203.severity = none + +# DOC204: UseInlineElementsCorrectly +# https://github.com/DotNetAnalyzers/DocumentationAnalyzers/blob/master/docs/DOC204.md +dotnet_diagnostic.DOC204.severity = none + +# DOC207: UseSeeLangwordCorrectly +# https://github.com/DotNetAnalyzers/DocumentationAnalyzers/blob/master/docs/DOC207.md +dotnet_diagnostic.DOC207.severity = none + +# DOC209: UseSeeHrefCorrectly +# https://github.com/DotNetAnalyzers/DocumentationAnalyzers/blob/master/docs/DOC209.md +dotnet_diagnostic.DOC209.severity = none + +# IDE0001: SimplifyNames +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0001 +dotnet_diagnostic.IDE0001.severity = silent + +# IDE0002: SimplifyMemberAccess +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0002 +dotnet_diagnostic.IDE0002.severity = silent + +# IDE0003: RemoveQualification +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0003 +dotnet_diagnostic.IDE0003.severity = silent + +# IDE0004: RemoveUnnecessaryCast +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0004 +dotnet_diagnostic.IDE0004.severity = silent + +# IDE0005: RemoveUnnecessaryImports +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0005 +dotnet_diagnostic.IDE0005.severity = silent + +# IDE0006: IntellisenseBuildFailed +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0006 +dotnet_diagnostic.IDE0006.severity = silent + +# IDE0007: UseImplicitType +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0007 +dotnet_diagnostic.IDE0007.severity = silent + +# IDE0008: UseExplicitType +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0008 +dotnet_diagnostic.IDE0008.severity = silent + +# IDE0009: AddQualification +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0009 +dotnet_diagnostic.IDE0009.severity = silent + +# IDE0010: PopulateSwitchStatement +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0010 +dotnet_diagnostic.IDE0010.severity = silent + +# IDE0011: AddBraces +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0011 +dotnet_diagnostic.IDE0011.severity = silent + +# IDE0016: UseThrowExpression +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0016 +dotnet_diagnostic.IDE0016.severity = silent + +# IDE0017: UseObjectInitializer +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0017 +dotnet_diagnostic.IDE0017.severity = silent + +# IDE0018: InlineDeclaration +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0018 +dotnet_diagnostic.IDE0018.severity = silent + +# IDE0019: InlineAsTypeCheck +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0019 +dotnet_diagnostic.IDE0019.severity = warning + +# IDE0020: InlineIsTypeCheck +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0020 +dotnet_diagnostic.IDE0020.severity = silent + +# IDE0021: UseExpressionBodyForConstructors +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0021 +dotnet_diagnostic.IDE0021.severity = silent + +# IDE0022: UseExpressionBodyForMethods +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0022 +dotnet_diagnostic.IDE0022.severity = silent + +# IDE0023: UseExpressionBodyForConversionOperators +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0023 +dotnet_diagnostic.IDE0023.severity = silent + +# IDE0024: UseExpressionBodyForOperators +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0024 +dotnet_diagnostic.IDE0024.severity = silent + +# IDE0025: UseExpressionBodyForProperties +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0025 +dotnet_diagnostic.IDE0025.severity = silent + +# IDE0026: UseExpressionBodyForIndexers +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0026 +dotnet_diagnostic.IDE0026.severity = silent + +# IDE0027: UseExpressionBodyForAccessors +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0027 +dotnet_diagnostic.IDE0027.severity = silent + +# IDE0028: UseCollectionInitializer +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0028 +dotnet_diagnostic.IDE0028.severity = silent + +# IDE0029: UseCoalesceExpression +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0029 +dotnet_diagnostic.IDE0029.severity = warning + +# IDE0030: UseCoalesceExpressionForNullable +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0030 +dotnet_diagnostic.IDE0030.severity = warning + +# IDE0031: UseNullPropagation +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0031 +dotnet_diagnostic.IDE0031.severity = warning + +# IDE0032: UseAutoProperty +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0032 +dotnet_diagnostic.IDE0032.severity = silent + +# IDE0033: UseExplicitTupleName +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0033 +dotnet_diagnostic.IDE0033.severity = silent + +# IDE0034: UseDefaultLiteral +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0034 +dotnet_diagnostic.IDE0034.severity = silent + +# IDE0035: RemoveUnreachableCode +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0035 +dotnet_diagnostic.IDE0035.severity = silent + +# IDE0036: OrderModifiers +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0036 +dotnet_diagnostic.IDE0036.severity = warning + +# IDE0037: UseInferredMemberName +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0037 +dotnet_diagnostic.IDE0037.severity = silent + +# IDE0038: InlineIsTypeWithoutNameCheck +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0038 +dotnet_diagnostic.IDE0038.severity = silent + +# IDE0039: UseLocalFunction +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0039 +dotnet_diagnostic.IDE0039.severity = silent + +# IDE0040: AddAccessibilityModifiers +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0040 +dotnet_diagnostic.IDE0040.severity = warning + +# IDE0041: UseIsNullCheck +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0041 +dotnet_diagnostic.IDE0041.severity = warning + +# IDE0042: UseDeconstruction +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0042 +dotnet_diagnostic.IDE0042.severity = silent + +# IDE0043: ValidateFormatString +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0043 +dotnet_diagnostic.IDE0043.severity = silent + +# IDE0044: MakeFieldReadonly +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0044 +dotnet_diagnostic.IDE0044.severity = warning + +# IDE0045: UseConditionalExpressionForAssignment +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0045 +dotnet_diagnostic.IDE0045.severity = silent + +# IDE0046: UseConditionalExpressionForReturn +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0046 +dotnet_diagnostic.IDE0046.severity = silent + +# IDE0047: RemoveUnnecessaryParentheses +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0047 +dotnet_diagnostic.IDE0047.severity = silent + +# IDE0048: AddRequiredParentheses +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0048 +dotnet_diagnostic.IDE0048.severity = suggestion + +# IDE0049: PreferBuiltInOrFrameworkType +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0049 +dotnet_diagnostic.IDE0049.severity = suggestion + +# IDE0050: ConvertAnonymousTypeToTuple +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0050 +dotnet_diagnostic.IDE0050.severity = silent + +# IDE0051: RemoveUnusedMembers +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0051 +dotnet_diagnostic.IDE0051.severity = silent + +# IDE0052: RemoveUnreadMembers +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0052 +dotnet_diagnostic.IDE0052.severity = silent + +# IDE0053: UseExpressionBodyForLambdaExpressions +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0053 +dotnet_diagnostic.IDE0053.severity = silent + +# IDE0054: UseCompoundAssignment +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0054 +dotnet_diagnostic.IDE0054.severity = warning + +# IDE0055: Formatting +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0055 +dotnet_diagnostic.IDE0055.severity = silent + +# IDE0056: UseIndexOperator +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0056 +dotnet_diagnostic.IDE0056.severity = silent + +# IDE0057: UseRangeOperator +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0057 +dotnet_diagnostic.IDE0057.severity = silent + +# IDE0058: ExpressionValueIsUnused +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0058 +dotnet_diagnostic.IDE0058.severity = silent + +# IDE0059: ValueAssignedIsUnused +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0059 +dotnet_diagnostic.IDE0059.severity = silent + +# IDE0060: UnusedParameter +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0060 +dotnet_diagnostic.IDE0060.severity = silent + +# IDE0061: UseExpressionBodyForLocalFunctions +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0061 +dotnet_diagnostic.IDE0061.severity = silent + +# IDE0062: MakeLocalFunctionStatic +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0062 +dotnet_diagnostic.IDE0062.severity = warning + +# IDE0063: UseSimpleUsingStatement +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0063 +dotnet_diagnostic.IDE0063.severity = silent + +# IDE0064: MakeStructFieldsWritable +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0064 +dotnet_diagnostic.IDE0064.severity = warning + +# IDE0065: MoveMisplacedUsingDirectives +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0065 +dotnet_diagnostic.IDE0065.severity = silent + +# IDE0066: ConvertSwitchStatementToExpression +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0066 +dotnet_diagnostic.IDE0066.severity = silent + +# IDE0070: UseSystemHashCode +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0070 +dotnet_diagnostic.IDE0070.severity = warning + +# IDE0071: SimplifyInterpolation +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0071 +dotnet_diagnostic.IDE0071.severity = silent + +# IDE0072: PopulateSwitchExpression +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0072 +dotnet_diagnostic.IDE0072.severity = silent + +# IDE0073: FileHeaderMismatch +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0073 +dotnet_diagnostic.IDE0073.severity = suggestion + +# IDE0074: UseCoalesceCompoundAssignment +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0074 +dotnet_diagnostic.IDE0074.severity = warning + +# IDE0075: SimplifyConditionalExpression +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0075 +dotnet_diagnostic.IDE0075.severity = warning + +# IDE0076: InvalidSuppressMessageAttribute +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0076 +dotnet_diagnostic.IDE0076.severity = warning + +# IDE0077: LegacyFormatSuppressMessageAttribute +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0077 +dotnet_diagnostic.IDE0077.severity = warning + +# IDE0078: UsePatternCombinators +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0078 +dotnet_diagnostic.IDE0078.severity = silent + +# IDE0079: RemoveUnnecessarySuppression +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0079 +dotnet_diagnostic.IDE0079.severity = silent + +# IDE0080: RemoveConfusingSuppressionForIsExpression +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0080 +dotnet_diagnostic.IDE0080.severity = warning + +# IDE0081: RemoveUnnecessaryByVal +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0081 +dotnet_diagnostic.IDE0081.severity = silent + +# IDE0082: ConvertTypeOfToNameOf +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0082 +dotnet_diagnostic.IDE0082.severity = warning + +# IDE0083: UseNotPattern +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0083 +dotnet_diagnostic.IDE0083.severity = silent + +# IDE0084: UseIsNotExpression +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0084 +dotnet_diagnostic.IDE0084.severity = silent + +# IDE0090: UseNew +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0090 +dotnet_diagnostic.IDE0090.severity = suggestion + +# IDE0100: RemoveRedundantEquality +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0100 +dotnet_diagnostic.IDE0100.severity = warning + +# IDE0110: RemoveUnnecessaryDiscard +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0110 +dotnet_diagnostic.IDE0110.severity = suggestion + +# IDE0120: SimplifyLINQExpression +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0120 +dotnet_diagnostic.IDE0120.severity = warning + +# IDE0130: NamespaceDoesNotMatchFolderStructure +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide0130 +dotnet_diagnostic.IDE0130.severity = silent + +# IDE1001: AnalyzerChanged +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide1001 +dotnet_diagnostic.IDE1001.severity = silent + +# IDE1002: AnalyzerDependencyConflict +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide1002 +dotnet_diagnostic.IDE1002.severity = silent + +# IDE1003: MissingAnalyzerReference +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide1003 +dotnet_diagnostic.IDE1003.severity = silent + +# IDE1004: ErrorReadingRuleset +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide1004 +dotnet_diagnostic.IDE1004.severity = silent + +# IDE1005: InvokeDelegateWithConditionalAccess +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide1005 +dotnet_diagnostic.IDE1005.severity = warning + +# IDE1006: NamingRule +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide1006 +dotnet_diagnostic.IDE1006.severity = silent + +# IDE1007: UnboundIdentifier +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide1007 +dotnet_diagnostic.IDE1007.severity = silent + +# IDE1008: UnboundConstructor +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide1008 +dotnet_diagnostic.IDE1008.severity = silent + +# IDE2000: MultipleBlankLines +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide2000 +dotnet_diagnostic.IDE2000.severity = warning + +# IDE2001: EmbeddedStatementsMustBeOnTheirOwnLine +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide2001 +dotnet_diagnostic.IDE2001.severity = warning + +# IDE2002: ConsecutiveBracesMustNotHaveBlankLinesBetweenThem +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide2002 +dotnet_diagnostic.IDE2002.severity = warning + +# IDE2003: ConsecutiveStatementPlacement +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide2003 +dotnet_diagnostic.IDE2003.severity = warning + +# IDE2004: BlankLineNotAllowedAfterConstructorInitializerColon +# https://learn.microsoft.com/dotnet/fundamentals/code-analysis/style-rules/ide2004 +dotnet_diagnostic.IDE2004.severity = warning + +# SA0001: XML comment analysis disabled +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA0001.md +dotnet_diagnostic.SA0001.severity = none + +# SA0002: Invalid settings file +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA0002.md +dotnet_diagnostic.SA0002.severity = none + +# SA1000: Keywords should be spaced correctly +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1000.md +dotnet_diagnostic.SA1000.severity = warning + +# SA1001: Commas should be spaced correctly +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1001.md +dotnet_diagnostic.SA1001.severity = warning + +# SA1002: Semicolons should be spaced correctly +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1002.md +dotnet_diagnostic.SA1002.severity = warning + +# SA1003: Symbols should be spaced correctly +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1003.md +dotnet_diagnostic.SA1003.severity = warning + +# SA1004: Documentation lines should begin with single space +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1004.md +dotnet_diagnostic.SA1004.severity = none + +# SA1005: Single line comments should begin with single space +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1005.md +dotnet_diagnostic.SA1005.severity = none + +# SA1006: Preprocessor keywords should not be preceded by space +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1006.md +dotnet_diagnostic.SA1006.severity = warning + +# SA1007: Operator keyword should be followed by space +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1007.md +dotnet_diagnostic.SA1007.severity = warning + +# SA1008: Opening parenthesis should be spaced correctly +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1008.md +dotnet_diagnostic.SA1008.severity = warning + +# SA1009: Closing parenthesis should be spaced correctly +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1009.md +dotnet_diagnostic.SA1009.severity = none + +# SA1010: Opening square brackets should be spaced correctly +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1010.md +dotnet_diagnostic.SA1010.severity = none + +# SA1011: Closing square brackets should be spaced correctly +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1011.md +dotnet_diagnostic.SA1011.severity = none + +# SA1012: Opening braces should be spaced correctly +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1012.md +dotnet_diagnostic.SA1012.severity = none + +# SA1013: Closing braces should be spaced correctly +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1013.md +dotnet_diagnostic.SA1013.severity = none + +# SA1014: Opening generic brackets should be spaced correctly +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1014.md +dotnet_diagnostic.SA1014.severity = none + +# SA1015: Closing generic brackets should be spaced correctly +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1015.md +dotnet_diagnostic.SA1015.severity = none + +# SA1016: Opening attribute brackets should be spaced correctly +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1016.md +dotnet_diagnostic.SA1016.severity = none + +# SA1017: Closing attribute brackets should be spaced correctly +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1017.md +dotnet_diagnostic.SA1017.severity = none + +# SA1018: Nullable type symbols should be spaced correctly +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1018.md +dotnet_diagnostic.SA1018.severity = none + +# SA1019: Member access symbols should be spaced correctly +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1019.md +dotnet_diagnostic.SA1019.severity = none + +# SA1020: Increment decrement symbols should be spaced correctly +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1020.md +dotnet_diagnostic.SA1020.severity = none + +# SA1021: Negative signs should be spaced correctly +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1021.md +dotnet_diagnostic.SA1021.severity = none + +# SA1022: Positive signs should be spaced correctly +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1022.md +dotnet_diagnostic.SA1022.severity = none + +# SA1023: Dereference and access of symbols should be spaced correctly +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1023.md +dotnet_diagnostic.SA1023.severity = none + +# SA1024: Colons Should Be Spaced Correctly +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1024.md +dotnet_diagnostic.SA1024.severity = none + +# SA1025: Code should not contain multiple whitespace in a row +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1025.md +dotnet_diagnostic.SA1025.severity = none + +# SA1026: Code should not contain space after new or stackalloc keyword in implicitly typed array allocation +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1026.md +dotnet_diagnostic.SA1026.severity = none + +# SA1027: Use tabs correctly +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1027.md +dotnet_diagnostic.SA1027.severity = none + +# SA1028: Code should not contain trailing whitespace +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1028.md +dotnet_diagnostic.SA1028.severity = none + +# SA1100: Do not prefix calls with base unless local implementation exists +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1100.md +dotnet_diagnostic.SA1100.severity = none + +# SA1101: Prefix local calls with this +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1101.md +dotnet_diagnostic.SA1101.severity = none + +# SA1102: Query clause should follow previous clause +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1102.md +dotnet_diagnostic.SA1102.severity = none + +# SA1103: Query clauses should be on separate lines or all on one line +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1103.md +dotnet_diagnostic.SA1103.severity = none + +# SA1104: Query clause should begin on new line when previous clause spans multiple lines +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1104.md +dotnet_diagnostic.SA1104.severity = none + +# SA1105: Query clauses spanning multiple lines should begin on own line +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1105.md +dotnet_diagnostic.SA1105.severity = none + +# SA1106: Code should not contain empty statements +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1106.md +dotnet_diagnostic.SA1106.severity = warning + +# SA1107: Code should not contain multiple statements on one line +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1107.md +dotnet_diagnostic.SA1107.severity = none + +# SA1108: Block statements should not contain embedded comments +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1108.md +dotnet_diagnostic.SA1108.severity = none + +# SA1110: Opening parenthesis or bracket should be on declaration line +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1110.md +dotnet_diagnostic.SA1110.severity = none + +# SA1111: Closing parenthesis should be on line of last parameter +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1111.md +dotnet_diagnostic.SA1111.severity = none + +# SA1112: Closing parenthesis should be on line of opening parenthesis +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1112.md +dotnet_diagnostic.SA1112.severity = none + +# SA1113: Comma should be on the same line as previous parameter +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1113.md +dotnet_diagnostic.SA1113.severity = none + +# SA1114: Parameter list should follow declaration +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1114.md +dotnet_diagnostic.SA1114.severity = none + +# SA1115: Parameter should follow comma +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1115.md +dotnet_diagnostic.SA1115.severity = none + +# SA1116: Split parameters should start on line after declaration +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1116.md +dotnet_diagnostic.SA1116.severity = none + +# SA1117: Parameters should be on same line or separate lines +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1117.md +dotnet_diagnostic.SA1117.severity = none + +# SA1118: Parameter should not span multiple lines +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1118.md +dotnet_diagnostic.SA1118.severity = none + +# SA1119: Statement should not use unnecessary parenthesis +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1119.md +dotnet_diagnostic.SA1119.severity = none + +# SA1120: Comments should contain text +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1120.md +dotnet_diagnostic.SA1120.severity = none + +# SA1121: Use built-in type alias +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1121.md +dotnet_diagnostic.SA1121.severity = none + +# SA1122: Use string.Empty for empty strings +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1122.md +dotnet_diagnostic.SA1122.severity = warning + +# SA1123: Do not place regions within elements +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1123.md +dotnet_diagnostic.SA1123.severity = none + +# SA1124: Do not use regions +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1124.md +dotnet_diagnostic.SA1124.severity = none + +# SA1125: Use shorthand for nullable types +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1125.md +dotnet_diagnostic.SA1125.severity = none + +# SA1127: Generic type constraints should be on their own line +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1127.md +dotnet_diagnostic.SA1127.severity = none + +# SA1128: Put constructor initializers on their own line +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1128.md +dotnet_diagnostic.SA1128.severity = none + +# SA1129: Do not use default value type constructor +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1129.md +dotnet_diagnostic.SA1129.severity = none + +# SA1130: Use lambda syntax +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1130.md +dotnet_diagnostic.SA1130.severity = none + +# SA1131: Use readable conditions +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1131.md +dotnet_diagnostic.SA1131.severity = warning + +# SA1132: Do not combine fields +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1132.md +dotnet_diagnostic.SA1132.severity = none + +# SA1133: Do not combine attributes +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1133.md +dotnet_diagnostic.SA1133.severity = none + +# SA1134: Attributes should not share line +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1134.md +dotnet_diagnostic.SA1134.severity = none + +# SA1135: Using directives should be qualified +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1135.md +dotnet_diagnostic.SA1135.severity = none + +# SA1136: Enum values should be on separate lines +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1136.md +dotnet_diagnostic.SA1136.severity = none + +# SA1137: Elements should have the same indentation +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1137.md +dotnet_diagnostic.SA1137.severity = none + +# SA1139: Use literal suffix notation instead of casting +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1139.md +dotnet_diagnostic.SA1139.severity = none + +# SA1141: Use tuple syntax +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1141.md +dotnet_diagnostic.SA1141.severity = none + +# SA1142: Refer to tuple fields by name +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1142.md +dotnet_diagnostic.SA1142.severity = none + +# SA1200: Using directives should be placed correctly +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1200.md +dotnet_diagnostic.SA1200.severity = none + +# SA1201: Elements should appear in the correct order +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1201.md +dotnet_diagnostic.SA1201.severity = none + +# SA1202: Elements should be ordered by access +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1202.md +dotnet_diagnostic.SA1202.severity = none + +# SA1203: Constants should appear before fields +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1203.md +dotnet_diagnostic.SA1203.severity = none + +# SA1204: Static elements should appear before instance elements +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1204.md +dotnet_diagnostic.SA1204.severity = none + +# SA1205: Partial elements should declare access +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1205.md +dotnet_diagnostic.SA1205.severity = warning + +# SA1206: Declaration keywords should follow order +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1206.md +dotnet_diagnostic.SA1206.severity = warning + +# SA1207: Protected should come before internal +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1207.md +dotnet_diagnostic.SA1207.severity = none + +# SA1208: System using directives should be placed before other using directives +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1208.md +dotnet_diagnostic.SA1208.severity = none + +# SA1209: Using alias directives should be placed after other using directives +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1209.md +dotnet_diagnostic.SA1209.severity = none + +# SA1210: Using directives should be ordered alphabetically by namespace +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1210.md +dotnet_diagnostic.SA1210.severity = none + +# SA1211: Using alias directives should be ordered alphabetically by alias name +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1211.md +dotnet_diagnostic.SA1211.severity = none + +# SA1212: Property accessors should follow order +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1212.md +dotnet_diagnostic.SA1212.severity = warning + +# SA1213: Event accessors should follow order +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1213.md +dotnet_diagnostic.SA1213.severity = warning + +# SA1214: Readonly fields should appear before non-readonly fields +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1214.md +dotnet_diagnostic.SA1214.severity = none + +# SA1216: Using static directives should be placed at the correct location +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1216.md +dotnet_diagnostic.SA1216.severity = warning + +# SA1217: Using static directives should be ordered alphabetically +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1217.md +dotnet_diagnostic.SA1217.severity = warning + +# SA1300: Element should begin with upper-case letter +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1300.md +dotnet_diagnostic.SA1300.severity = none + +# SA1302: Interface names should begin with I +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1302.md +dotnet_diagnostic.SA1302.severity = none + +# SA1303: Const field names should begin with upper-case letter +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1303.md +dotnet_diagnostic.SA1303.severity = none + +# SA1304: Non-private readonly fields should begin with upper-case letter +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1304.md +dotnet_diagnostic.SA1304.severity = none + +# SA1305: Field names should not use Hungarian notation +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1305.md +dotnet_diagnostic.SA1305.severity = none + +# SA1306: Field names should begin with lower-case letter +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1306.md +dotnet_diagnostic.SA1306.severity = none + +# SA1307: Accessible fields should begin with upper-case letter +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1307.md +dotnet_diagnostic.SA1307.severity = none + +# SA1308: Variable names should not be prefixed +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1308.md +dotnet_diagnostic.SA1308.severity = none + +# SA1309: Field names should not begin with underscore +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1309.md +dotnet_diagnostic.SA1309.severity = none + +# SA1310: Field names should not contain underscore +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1310.md +dotnet_diagnostic.SA1310.severity = none + +# SA1311: Static readonly fields should begin with upper-case letter +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1311.md +dotnet_diagnostic.SA1311.severity = none + +# SA1312: Variable names should begin with lower-case letter +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1312.md +dotnet_diagnostic.SA1312.severity = none + +# SA1313: Parameter names should begin with lower-case letter +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1313.md +dotnet_diagnostic.SA1313.severity = none + +# SA1314: Type parameter names should begin with T +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1314.md +dotnet_diagnostic.SA1314.severity = warning + +# SA1316: Tuple element names should use correct casing +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1316.md +dotnet_diagnostic.SA1316.severity = none + +# SA1400: Access modifier should be declared +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1400.md +dotnet_diagnostic.SA1400.severity = none + +# SA1401: Fields should be private +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1401.md +dotnet_diagnostic.SA1401.severity = none + +# SA1402: File may only contain a single type +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1402.md +dotnet_diagnostic.SA1402.severity = none + +# SA1403: File may only contain a single namespace +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1403.md +dotnet_diagnostic.SA1403.severity = none + +# SA1404: Code analysis suppression should have justification +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1404.md +dotnet_diagnostic.SA1404.severity = none + +# SA1405: Debug.Assert should provide message text +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1405.md +dotnet_diagnostic.SA1405.severity = none + +# SA1406: Debug.Fail should provide message text +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1406.md +dotnet_diagnostic.SA1406.severity = none + +# SA1407: Arithmetic expressions should declare precedence +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1407.md +dotnet_diagnostic.SA1407.severity = none + +# SA1408: Conditional expressions should declare precedence +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1408.md +dotnet_diagnostic.SA1408.severity = none + +# SA1410: Remove delegate parenthesis when possible +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1410.md +dotnet_diagnostic.SA1410.severity = none + +# SA1411: Attribute constructor should not use unnecessary parenthesis +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1411.md +dotnet_diagnostic.SA1411.severity = none + +# SA1412: Store files as UTF-8 with byte order mark +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1412.md +dotnet_diagnostic.SA1412.severity = none + +# SA1413: Use trailing comma in multi-line initializers +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1413.md +dotnet_diagnostic.SA1413.severity = none + +# SA1414: Tuple types in signatures should have element names +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1414.md +dotnet_diagnostic.SA1414.severity = none + +# SA1500: Braces for multi-line statements should not share line +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1500.md +dotnet_diagnostic.SA1500.severity = none + +# SA1501: Statement should not be on a single line +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1501.md +dotnet_diagnostic.SA1501.severity = none + +# SA1502: Element should not be on a single line +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1502.md +dotnet_diagnostic.SA1502.severity = none + +# SA1503: Braces should not be omitted +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1503.md +dotnet_diagnostic.SA1503.severity = none + +# SA1504: All accessors should be single-line or multi-line +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1504.md +dotnet_diagnostic.SA1504.severity = warning + +# SA1505: Opening braces should not be followed by blank line +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1505.md +dotnet_diagnostic.SA1505.severity = none + +# SA1506: Element documentation headers should not be followed by blank line +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1506.md +dotnet_diagnostic.SA1506.severity = none + +# SA1507: Code should not contain multiple blank lines in a row +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1507.md +dotnet_diagnostic.SA1507.severity = warning + +# SA1508: Closing braces should not be preceded by blank line +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1508.md +dotnet_diagnostic.SA1508.severity = none + +# SA1509: Opening braces should not be preceded by blank line +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1509.md +dotnet_diagnostic.SA1509.severity = none + +# SA1510: Chained statement blocks should not be preceded by blank line +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1510.md +dotnet_diagnostic.SA1510.severity = none + +# SA1511: While-do footer should not be preceded by blank line +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1511.md +dotnet_diagnostic.SA1511.severity = none + +# SA1512: Single-line comments should not be followed by blank line +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1512.md +dotnet_diagnostic.SA1512.severity = none + +# SA1513: Closing brace should be followed by blank line +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1513.md +dotnet_diagnostic.SA1513.severity = none + +# SA1514: Element documentation header should be preceded by blank line +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1514.md +dotnet_diagnostic.SA1514.severity = none + +# SA1515: Single-line comment should be preceded by blank line +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1515.md +dotnet_diagnostic.SA1515.severity = none + +# SA1516: Elements should be separated by blank line +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1516.md +dotnet_diagnostic.SA1516.severity = warning + +# SA1517: Code should not contain blank lines at start of file +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1517.md +dotnet_diagnostic.SA1517.severity = warning + +# SA1518: Use line endings correctly at end of file +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1518.md +dotnet_diagnostic.SA1518.severity = warning + +# SA1519: Braces should not be omitted from multi-line child statement +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1519.md +dotnet_diagnostic.SA1519.severity = none + +# SA1520: Use braces consistently +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1520.md +dotnet_diagnostic.SA1520.severity = none + +# SA1600: Elements should be documented +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1600.md +dotnet_diagnostic.SA1600.severity = none + +# SA1601: Partial elements should be documented +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1601.md +dotnet_diagnostic.SA1601.severity = none + +# SA1602: Enumeration items should be documented +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1602.md +dotnet_diagnostic.SA1602.severity = none + +# SA1604: Element documentation should have summary +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1604.md +dotnet_diagnostic.SA1604.severity = none + +# SA1605: Partial element documentation should have summary +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1605.md +dotnet_diagnostic.SA1605.severity = none + +# SA1606: Element documentation should have summary text +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1606.md +dotnet_diagnostic.SA1606.severity = none + +# SA1607: Partial element documentation should have summary text +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1607.md +dotnet_diagnostic.SA1607.severity = none + +# SA1608: Element documentation should not have default summary +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1608.md +dotnet_diagnostic.SA1608.severity = none + +# SA1609: Property documentation should have value +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1609.md +dotnet_diagnostic.SA1609.severity = none + +# SA1610: Property documentation should have value text +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1610.md +dotnet_diagnostic.SA1610.severity = none + +# SA1611: Element parameters should be documented +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1611.md +dotnet_diagnostic.SA1611.severity = none + +# SA1612: Element parameter documentation should match element parameters +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1612.md +dotnet_diagnostic.SA1612.severity = none + +# SA1613: Element parameter documentation should declare parameter name +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1613.md +dotnet_diagnostic.SA1613.severity = none + +# SA1614: Element parameter documentation should have text +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1614.md +dotnet_diagnostic.SA1614.severity = none + +# SA1615: Element return value should be documented +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1615.md +dotnet_diagnostic.SA1615.severity = none + +# SA1616: Element return value documentation should have text +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1616.md +dotnet_diagnostic.SA1616.severity = none + +# SA1617: Void return value should not be documented +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1617.md +dotnet_diagnostic.SA1617.severity = none + +# SA1618: Generic type parameters should be documented +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1618.md +dotnet_diagnostic.SA1618.severity = none + +# SA1619: Generic type parameters should be documented partial class +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1619.md +dotnet_diagnostic.SA1619.severity = none + +# SA1620: Generic type parameter documentation should match type parameters +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1620.md +dotnet_diagnostic.SA1620.severity = none + +# SA1621: Generic type parameter documentation should declare parameter name +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1621.md +dotnet_diagnostic.SA1621.severity = none + +# SA1622: Generic type parameter documentation should have text +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1622.md +dotnet_diagnostic.SA1622.severity = none + +# SA1623: Property summary documentation should match accessors +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1623.md +dotnet_diagnostic.SA1623.severity = none + +# SA1624: Property summary documentation should omit accessor with restricted access +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1624.md +dotnet_diagnostic.SA1624.severity = none + +# SA1625: Element documentation should not be copied and pasted +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1625.md +dotnet_diagnostic.SA1625.severity = none + +# SA1626: Single-line comments should not use documentation style slashes +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1626.md +dotnet_diagnostic.SA1626.severity = none + +# SA1627: Documentation text should not be empty +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1627.md +dotnet_diagnostic.SA1627.severity = none + +# SA1629: Documentation text should end with a period +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1629.md +dotnet_diagnostic.SA1629.severity = none + +# SA1633: File should have header +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1633.md +dotnet_diagnostic.SA1633.severity = none + +# SA1634: File header should show copyright +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1634.md +dotnet_diagnostic.SA1634.severity = none + +# SA1635: File header should have copyright text +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1635.md +dotnet_diagnostic.SA1635.severity = none + +# SA1636: File header copyright text should match +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1636.md +dotnet_diagnostic.SA1636.severity = none + +# SA1637: File header should contain file name +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1637.md +dotnet_diagnostic.SA1637.severity = none + +# SA1638: File header file name documentation should match file name +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1638.md +dotnet_diagnostic.SA1638.severity = none + +# SA1639: File header should have summary +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1639.md +dotnet_diagnostic.SA1639.severity = none + +# SA1640: File header should have valid company text +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1640.md +dotnet_diagnostic.SA1640.severity = none + +# SA1641: File header company name text should match +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1641.md +dotnet_diagnostic.SA1641.severity = none + +# SA1642: Constructor summary documentation should begin with standard text +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1642.md +dotnet_diagnostic.SA1642.severity = none + +# SA1643: Destructor summary documentation should begin with standard text +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1643.md +dotnet_diagnostic.SA1643.severity = warning + +# SA1648: inheritdoc should be used with inheriting class +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1648.md +dotnet_diagnostic.SA1648.severity = none + +# SA1649: File name should match first type name +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1649.md +dotnet_diagnostic.SA1649.severity = none + +# SA1651: Do not use placeholder elements +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SA1651.md +dotnet_diagnostic.SA1651.severity = none + +# SX1101: Do not prefix local calls with 'this.' +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SX1101.md +dotnet_diagnostic.SX1101.severity = none + +# SX1309: Field names should begin with underscore +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SX1309.md +dotnet_diagnostic.SX1309.severity = none + +# SX1309S: Static field names should begin with underscore +# https://github.com/DotNetAnalyzers/StyleCopAnalyzers/blob/master/documentation/SX1309S.md +dotnet_diagnostic.SX1309S.severity = none diff --git a/.mailmap b/.mailmap new file mode 100644 index 00000000000..0bc786ac445 --- /dev/null +++ b/.mailmap @@ -0,0 +1 @@ +Andy Jordan diff --git a/.markdownlintignore b/.markdownlintignore new file mode 100644 index 00000000000..1d3c5b1ac92 --- /dev/null +++ b/.markdownlintignore @@ -0,0 +1 @@ +.github/SECURITY.md diff --git a/.pipelines/EV2Specs/ServiceGroupRoot/RolloutSpec.json b/.pipelines/EV2Specs/ServiceGroupRoot/RolloutSpec.json new file mode 100644 index 00000000000..9ed971068cc --- /dev/null +++ b/.pipelines/EV2Specs/ServiceGroupRoot/RolloutSpec.json @@ -0,0 +1,28 @@ +{ + "$schema": "https://ev2schema.azure.net/schemas/2020-01-01/rolloutSpecification.json", + "contentVersion": "1.0.0.0", + "rolloutMetadata": { + "serviceModelPath": "ServiceModel.json", + "ScopeBindingsPath": "ScopeBindings.json", + "name": "OneBranch-Demo-Container-Deployment", + "rolloutType": "Major", + "buildSource": { + "parameters": { + "versionFile": "buildver.txt" + } + }, + "Notification": { + "Email": { + "To": "default" + } + } + }, + "orchestratedSteps": [ + { + "name": "UploadLinuxContainer", + "targetType": "ServiceResource", + "targetName": "LinuxContainerUpload", + "actions": ["Shell/Run"] + } + ] +} diff --git a/.pipelines/EV2Specs/ServiceGroupRoot/ScopeBindings.json b/.pipelines/EV2Specs/ServiceGroupRoot/ScopeBindings.json new file mode 100644 index 00000000000..c3a98555867 --- /dev/null +++ b/.pipelines/EV2Specs/ServiceGroupRoot/ScopeBindings.json @@ -0,0 +1,23 @@ +{ + "$schema": "https://ev2schema.azure.net/schemas/2020-01-01/scopeBindings.json", + "contentVersion": "0.0.0.1", + "scopeBindings": [ + { + "scopeTagName": "Global", + "bindings": [ + { + "find": "__SUBSCRIPTION_ID__", + "replaceWith": "$azureSubscriptionId()" + }, + { + "find": "__RESOURCE_GROUP__", + "replaceWith": "$azureResourceGroup()" + }, + { + "find": "__BUILD_VERSION__", + "replaceWith": "$buildVersion()" + } + ] + } + ] +} diff --git a/.pipelines/EV2Specs/ServiceGroupRoot/ServiceModel.json b/.pipelines/EV2Specs/ServiceGroupRoot/ServiceModel.json new file mode 100644 index 00000000000..ce974fe69e5 --- /dev/null +++ b/.pipelines/EV2Specs/ServiceGroupRoot/ServiceModel.json @@ -0,0 +1,51 @@ +{ + "$schema": "https://ev2schema.azure.net/schemas/2020-01-01/serviceModel.json", + "contentVersion": "1.0.0.0", + "serviceMetadata": { + "serviceGroup": "OneBranch-PowerShellDocker", + "environment": "Test" + }, + "serviceResourceGroupDefinitions": [ + { + "name": "OneBranch-PowerShellDocker-RGDef", + "serviceResourceDefinitions": [ + { + "name": "OneBranch-PowerShellDocker.Shell-SRDef", + "composedOf": { + "extension": { + "shell": [ + { + "type": "Run", + "properties": { + "imageName": "adm-azurelinux-30-l", + "imageVersion": "v2" + } + } + ] + } + } + } + ] + } + ], + "serviceResourceGroups": [ + { + "azureResourceGroupName": "default", + "location": "West US 3", + "instanceOf": "OneBranch-PowerShellDocker-RGDef", + "azureSubscriptionId": "default", + "scopeTags": [ + { + "name": "Global" + } + ], + "serviceResources": [ + { + "Name": "LinuxContainerUpload", + "InstanceOf": "OneBranch-PowerShellDocker.Shell-SRDef", + "RolloutParametersPath": "UploadLinux.Rollout.json" + } + ] + } + ] +} diff --git a/.pipelines/EV2Specs/ServiceGroupRoot/Shell/Run/Run.ps1 b/.pipelines/EV2Specs/ServiceGroupRoot/Shell/Run/Run.ps1 new file mode 100644 index 00000000000..23f91c1bff2 --- /dev/null +++ b/.pipelines/EV2Specs/ServiceGroupRoot/Shell/Run/Run.ps1 @@ -0,0 +1,397 @@ +<# +This function gets info from pmc's derived list of all repositories and from mapping.json (which contains info on just the repositories powershell publishes packages to, their package formats, etc) +to create a list of repositories PowerShell cares about along with repository Ids, repository full Urls and associated package that will be published to it. +#> +function Get-MappedRepositoryIds { + param( + [Parameter(Mandatory)] + [hashtable] + $Mapping, + + [Parameter(Mandatory)] + $RepoList, + + # LTS is not consider a package in this context. + # LTS is just another package name. + [Parameter(Mandatory)] + [ValidateSet('stable', 'preview')] + $Channel + ) + + $mappedReposUsedByPwsh = @() + foreach ($package in $Mapping.Packages) + { + Write-Verbose "package: $package" + $packageChannel = $package.channel + if (!$packageChannel) { + $packageChannel = 'all' + } + + Write-Verbose "package channel: $packageChannel" + if ($packageChannel -eq 'all' -or $packageChannel -eq $Channel) + { + $repoIds = [System.Collections.Generic.List[string]]::new() + $packageFormat = $package.PackageFormat + Write-Verbose "package format: $packageFormat" -Verbose + $extension = [System.io.path]::GetExtension($packageFormat) + $packageType = $extension -replace '^\.' + + if ($package.distribution.count -gt 1) { + throw "Package $($package | out-string) has more than one Distribution." + } + + foreach ($distribution in $package.distribution) + { + $urlGlob = $package.url + switch ($packageType) + { + 'deb' { + $urlGlob = $urlGlob + '-apt' + } + 'rpm' { + $urlGlob = $urlGlob + '-yum' + } + default { + throw "Unknown package type: $packageType" + } + } + + Write-Verbose "---Finding repo id for: $urlGlob---" -Verbose + $repos = $RepoList | Where-Object { $_.name -eq $urlGlob } + + if ($repos.id) { + Write-Verbose "Found repo id: $($repos.id)" -Verbose + $repoIds.AddRange(([string[]]$repos.id)) + } + else { + Write-Failure "Could not find repo for $urlGlob" + } + + if ($repoIds.Count -gt 0) { + $mappedReposUsedByPwsh += ($package + @{ "RepoId" = $repoIds.ToArray() }) + } + } + } + } + + Write-Verbose -Verbose "mapped repos length: $($mappedReposUsedByPwsh.Length)" + return $mappedReposUsedByPwsh +} + +<# +This function creates package objects for the packages to be published, +with the package name (ie package name format resolve with channel based PackageName and pwsh version), repoId, distribution and package path. +#> +function Get-PackageObjects() { + param( + [Parameter(Mandatory)] + [psobject[]] + $RepoObjects, + + [Parameter(Mandatory)] + [string] + $ReleaseVersion, + + [Parameter(Mandatory)] + [string[]] + $PackageName + ) + + $packages = @() + + foreach ($pkg in $RepoObjects) + { + if ($pkg.RepoId.count -gt 1) { + throw "Package $($pkg.name) has more than one repo id." + } + + if ($pkg.Distribution.count -gt 1) { + throw "Package $($pkg.name) has more than one Distribution." + } + + $pkgRepo = $pkg.RepoId | Select-Object -First 1 + $pkgDistribution = $pkg.Distribution | Select-Object -First 1 + + foreach ($name in $PackageName) { + $pkgName = $pkg.PackageFormat.Replace('PACKAGE_NAME', $name).Replace('POWERSHELL_RELEASE', $ReleaseVersion) + + if ($pkgName.EndsWith('.rpm')) { + $pkgName = $pkgName.Replace($ReleaseVersion, $ReleaseVersion.Replace('-', '_')) + } + + $packagePath = "$pwshPackagesFolder/$pkgName" + $packagePathExists = Test-Path -Path $packagePath + if (!$packagePathExists) + { + throw "package path $packagePath does not exist" + } + + Write-Verbose "Creating package info object for package '$pkgName' for repo '$pkgRepo'" + $packages += @{ + PackagePath = $packagePath + PackageName = $pkgName + RepoId = $pkgRepo + Distribution = $pkgDistribution + } + + Write-Verbose -Verbose "package info obj: Name: $pkgName RepoId: $pkgRepo Distribution: $pkgDistribution PackagePath: $packagePath" + } + } + + Write-Verbose -Verbose "count of packages objects: $($packages.Length)" + return $packages +} + +<# +This function stages, uploads and publishes the powershell packages to their associated repositories in PMC. +#> +function Publish-PackageToPMC() { + param( + [Parameter(Mandatory)] + [pscustomobject[]] + $PackageObject, + + [Parameter(Mandatory)] + [string] + $ConfigPath, + + [Parameter(Mandatory)] + [bool] + $SkipPublish + ) + + # Don't fail outright when an error occurs, but instead pool them until + # after attempting to publish every package. That way we can choose to + # proceed for a partial failure. + $errorMessage = [System.Collections.Generic.List[string]]::new() + foreach ($finalPackage in $PackageObject) + { + Write-Verbose "---Staging package: $($finalPackage.PackageName)---" -Verbose + $packagePath = $finalPackage.PackagePath + $pkgRepo = $finalPackage.RepoId + + $extension = [System.io.path]::GetExtension($packagePath) + $packageType = $extension -replace '^\.' + Write-Verbose "packageType: $packageType" -Verbose + + $packageListJson = pmc --config $ConfigPath package $packageType list --file $packagePath + $list = $packageListJson | ConvertFrom-Json + + $packageId = @() + if ($list.count -ne 0) + { + Write-Verbose "Package '$packagePath' already exists, skipping upload" -Verbose + $packageId = $list.results.id | Select-Object -First 1 + } + else { + # PMC UPLOAD COMMAND + Write-Verbose -Verbose "Uploading package, config: '$ConfigPath' package: '$packagePath'" + $uploadResult = $null + try { + $uploadResult = pmc --config $ConfigPath package upload $packagePath --type $packageType + } + catch { + $errorMessage.Add("Uploading package $($finalPackage.PackageName) to $pkgRepo failed. See errors above for details.") + continue + } + + $packageId = ($uploadResult | ConvertFrom-Json).id + } + + Write-Verbose "Got package ID: '$packageId'" -Verbose + $distribution = $finalPackage.Distribution | select-object -First 1 + Write-Verbose "distribution: $distribution" -Verbose + + if (!$SkipPublish) + { + Write-Verbose "---Publishing package: $($finalPackage.PackageName) to $pkgRepo---" -Verbose + + if (($packageType -ne 'rpm') -and ($packageType -ne 'deb')) + { + throw "Unsupported package type: $packageType" + return 1 + } + else { + # PMC UPDATE COMMAND + $rawUpdateResponse = $null + try { + if ($packageType -eq 'rpm') { + $rawUpdateResponse = pmc --config $ConfigPath repo package update $pkgRepo --add-packages $packageId + } elseif ($packageType -eq 'deb') { + $rawUpdateResponse = pmc --config $ConfigPath repo package update $pkgRepo $distribution --add-packages $packageId + } + } + catch { + $errorMessage.Add("Invoking update for package $($finalPackage.PackageName) to $pkgRepo failed. See errors above for details.") + continue + } + + $state = ($rawUpdateResponse | ConvertFrom-Json).state + Write-Verbose -Verbose "update response state: $state" + if ($state -ne 'completed') { + $errorMessage.Add("Publishing package $($finalPackage.PackageName) to $pkgRepo failed: $rawUpdateResponse") + continue + } + } + + # PMC PUBLISH COMMAND + # The CLI outputs messages and JSON in the same stream, so we must sift through it for now + # This is planned to be fixed with a switch in a later release + Write-Verbose -Verbose ([pscustomobject]($package + @{ + PackageId = $packageId + })) + + # At this point, the changes are staged and will eventually be publish. + # Running publish, causes them to go live "immediately" + $rawPublishResponse = $null + try { + $rawPublishResponse = pmc --config $ConfigPath repo publish $pkgRepo + } + catch { + $errorMessage.Add("Invoking final publish for package $($finalPackage.PackageName) to $pkgRepo failed. See errors above for details.") + continue + } + + $publishState = ($rawPublishResponse | ConvertFrom-Json).state + Write-Verbose -Verbose "publish response state: $publishState" + if ($publishState -ne 'completed') { + $errorMessage.Add("Final publishing of package $($finalPackage.PackageName) to $pkgRepo failed: $rawPublishResponse") + continue + } + } else { + Write-Verbose -Verbose "Skipping Uploading package --config-file '$ConfigPath' package add '$packagePath' --repoID '$pkgRepo'" + } + } + + if ($errorMessage) { + throw $errorMessage -join [Environment]::NewLine + } +} + +if ($null -eq $env:MAPPING_FILE) +{ + Write-Verbose -Verbose "MAPPING_FILE variable didn't get passed correctly" + return 1 +} + +if ($null -eq $env:PWSH_PACKAGES_TARGZIP) +{ + Write-Verbose -Verbose "PWSH_PACKAGES_TARGZIP variable didn't get passed correctly" + return 1 +} + +if ($null -eq $env:PMC_METADATA) +{ + Write-Verbose -Verbose "PMC_METADATA variable didn't get passed correctly" + return 1 +} + +try { + Write-Verbose -Verbose "Downloading files" + Invoke-WebRequest -Uri $env:MAPPING_FILE -OutFile mapping.json + Invoke-WebRequest -Uri $env:PWSH_PACKAGES_TARGZIP -OutFile packages.tar.gz + Invoke-WebRequest -Uri $env:PMC_METADATA -OutFile pmcMetadata.json + + # create variables to those paths and test them + $mappingFilePath = Join-Path "/package/unarchive/" -ChildPath "mapping.json" + $mappingFilePathExists = Test-Path $mappingFilePath + if (!$mappingFilePathExists) + { + Write-Verbose -Verbose "mapping.json expected at $mappingFilePath does not exist" + return 1 + } + + $packagesTarPath = Join-Path -Path "/package/unarchive/" -ChildPath "packages.tar.gz" + $packagesTarPathExists = Test-Path $packagesTarPath + if (!$packagesTarPathExists) + { + Write-Verbose -Verbose "packages.tar.gz expected at $packagesTarPath does not exist" + return 1 + } + + # Extract files from 'packages.tar.gz' + Write-Verbose -Verbose "---Extracting files from packages.tar.gz---" + $pwshPackagesFolder = Join-Path -Path "/package/unarchive/" -ChildPath "packages" + New-Item -Path $pwshPackagesFolder -ItemType Directory + tar -xzvf $packagesTarPath -C $pwshPackagesFolder --force-local + Get-ChildItem $pwshPackagesFolder -Recurse + + $metadataFilePath = Join-Path -Path "/package/unarchive/" -ChildPath "pmcMetadata.json" + $metadataFilePathExists = Test-Path $metadataFilePath + if (!$metadataFilePathExists) + { + Write-Verbose -Verbose "pmcMetadata.json expected at $metadataFilePath does not exist" + return 1 + } + + # files in the extracted Run dir + $configPath = Join-Path '/package/unarchive/Run' -ChildPath 'settings.toml' + $configPathExists = Test-Path -Path $configPath + if (!$configPathExists) + { + Write-Verbose -Verbose "settings.toml expected at $configPath does not exist" + return 1 + } + + $pythonDlFolder = Join-Path '/package/unarchive/Run' -ChildPath 'python_dl' + $pyPathExists = Test-Path -Path $pythonDlFolder + if (!$pyPathExists) + { + Write-Verbose -Verbose "python_dl expected at $pythonDlFolder does not exist" + return 1 + } + + Write-Verbose -Verbose "Installing pmc-cli" + pip install --upgrade pip + pip --version --verbose + pip install /package/unarchive/Run/python_dl/*.whl + + # Get metadata + $channel = "" + $packageNames = @() + $metadataContent = Get-Content -Path $metadataFilePath | ConvertFrom-Json + $releaseVersion = $metadataContent.ReleaseTag.TrimStart('v') + $skipPublish = $metadataContent.SkipPublish + $lts = $metadataContent.LTS + + # Check if this is a rebuild version (e.g., 7.4.13-rebuild.5) + $isRebuild = $releaseVersion -match '-rebuild\.' + + if ($releaseVersion.Contains('-')) { + $channel = 'preview' + $packageNames = @('powershell-preview') + } + else { + $channel = 'stable' + $packageNames = @('powershell') + } + + # Only add LTS package if not a rebuild branch + if ($lts -and -not $isRebuild) { + $packageNames += @('powershell-lts') + } + + Write-Verbose -Verbose "---Getting repository list---" + $rawResponse = pmc --config $configPath repo list --limit 800 + $response = $rawResponse | ConvertFrom-Json + $limit = $($response.limit) + $count = $($response.count) + Write-Verbose -Verbose "'pmc repo list' limit is: $limit and count is: $count" + $repoList = $response.results + + Write-Verbose -Verbose "---Getting package info---" + + + Write-Verbose "Reading mapping file from '$mappingFilePath'" -Verbose + $mapping = Get-Content -Raw -LiteralPath $mappingFilePath | ConvertFrom-Json -AsHashtable + $mappedReposUsedByPwsh = Get-MappedRepositoryIds -Mapping $mapping -RepoList $repoList -Channel $channel + $packageObjects = Get-PackageObjects -RepoObjects $mappedReposUsedByPwsh -PackageName $packageNames -ReleaseVersion $releaseVersion + Write-Verbose -Verbose "skip publish $skipPublish" + Publish-PackageToPMC -PackageObject $packageObjects -ConfigPath $configPath -SkipPublish $skipPublish +} +catch { + Write-Error -ErrorAction Stop $_.Exception.Message + return 1 +} + +return 0 diff --git a/.pipelines/EV2Specs/ServiceGroupRoot/UploadLinux.Rollout.json b/.pipelines/EV2Specs/ServiceGroupRoot/UploadLinux.Rollout.json new file mode 100644 index 00000000000..d7c75c2e216 --- /dev/null +++ b/.pipelines/EV2Specs/ServiceGroupRoot/UploadLinux.Rollout.json @@ -0,0 +1,54 @@ +{ + "$schema": "https://ev2schema.azure.net/schemas/2020-01-01/rolloutParameters.json", + "contentVersion": "1.0.0.0", + "shellExtensions": [ + { + "name": "Run", + "type": "Run", + "properties": { + "maxExecutionTime": "PT2H" + }, + "package": { + "reference": { + "path": "Shell/Run.tar" + } + }, + "launch": { + "command": [ + "/bin/bash", + "-c", + "pwsh ./Run/Run.ps1" + ], + "environmentVariables": [ + { + "name": "MAPPING_FILE", + "reference": + { + "path": "Parameters\\mapping.json" + } + }, + { + "name": "PWSH_PACKAGES_TARGZIP", + "reference": + { + "path": "Parameters\\packages.tar.gz" + } + }, + { + "name": "PMC_METADATA", + "reference": + { + "path": "Parameters\\pmcMetadata.json" + } + } + ], + "identity": { + "type": "userAssigned", + "userAssignedIdentities": [ + "default" + ] + } + } + } + ] +} diff --git a/.pipelines/EV2Specs/ServiceGroupRoot/buildVer.txt b/.pipelines/EV2Specs/ServiceGroupRoot/buildVer.txt new file mode 100644 index 00000000000..7dea76edb3d --- /dev/null +++ b/.pipelines/EV2Specs/ServiceGroupRoot/buildVer.txt @@ -0,0 +1 @@ +1.0.1 diff --git a/.pipelines/MSIXBundle-vPack-Official.yml b/.pipelines/MSIXBundle-vPack-Official.yml new file mode 100644 index 00000000000..997b7c458be --- /dev/null +++ b/.pipelines/MSIXBundle-vPack-Official.yml @@ -0,0 +1,145 @@ +trigger: none + +parameters: # parameters are shown up in ADO UI in a build queue time +- name: 'createVPack' + displayName: 'Create and Submit VPack' + type: boolean + default: true +- name: 'debug' + displayName: 'Enable debug output' + type: boolean + default: false +- name: 'ReleaseTagVar' + type: string + displayName: 'Release Tag Var:' + default: 'fromBranch' + +name: msixbundle_vPack_$(date:yyMM).$(date:dd)$(rev:rrr) + +variables: + CDP_DEFINITION_BUILD_COUNT: $[counter('', 0)] + system.debug: ${{ parameters.debug }} + BuildSolution: $(Build.SourcesDirectory)\dirs.proj + ReleaseTagVar: ${{ parameters.ReleaseTagVar }} + BuildConfiguration: Release + WindowsContainerImage: 'onebranch.azurecr.io/windows/ltsc2019/vse2022:latest' + Codeql.Enabled: false # pipeline is not building artifacts; it repackages existing artifacts into a vpack + DOTNET_CLI_TELEMETRY_OPTOUT: 1 + POWERSHELL_TELEMETRY_OPTOUT: 1 + +resources: + repositories: + - repository: onebranchTemplates + type: git + name: OneBranch.Pipelines/GovernedTemplates + ref: refs/heads/main + + pipelines: + - pipeline: PSPackagesOfficial + source: 'PowerShell-Packages-Official' + trigger: + branches: + include: + - master + - releases/* + +extends: + template: v2/Microsoft.Official.yml@onebranchTemplates + parameters: + platform: + name: 'windows_undocked' # windows undocked + + cloudvault: + enabled: false + + globalSdl: + useCustomPolicy: true # for signing code + disableLegacyManifest: true + # disabled Armory as we dont have any ARM templates to scan. It fails on some sample ARM templates. + armory: + enabled: false + sbom: + enabled: true + compiled: + enabled: false + credscan: + enabled: true + scanFolder: $(Build.SourcesDirectory) + suppressionsFile: $(Build.SourcesDirectory)\.config\suppress.json + binskim: + enabled: false + exactToolVersion: 4.4.2 + # APIScan requires a non-Ready-To-Run build + apiscan: + enabled: false + tsaOptionsFile: .config/tsaoptions.json + + stages: + - stage: build + jobs: + - job: main + pool: + type: windows + + variables: + ob_outputDirectory: '$(BUILD.SOURCESDIRECTORY)\out' + ob_createvpack_enabled: ${{ parameters.createVPack }} + ob_createvpack_packagename: 'PowerShell.app' + ob_createvpack_owneralias: 'dongbow' + ob_createvpack_description: 'VPack for the PowerShell Application' + ob_createvpack_targetDestinationDirectory: '$(Destination)' + ob_createvpack_propsFile: false + ob_createvpack_provData: true + ob_createvpack_metadata: '$(Build.SourceVersion)' + ob_createvpack_versionAs: string + ob_createvpack_version: '$(version)' + ob_createvpack_verbose: true + + steps: + - template: .pipelines/templates/SetVersionVariables.yml@self + parameters: + ReleaseTagVar: $(ReleaseTagVar) + + - pwsh: | + Write-Verbose -Verbose 'PowerShell Version: $(version)' + if('$(version)' -match '-') { + throw "Don't release a preview build msixbundle package" + } + displayName: Stop any preview release + + - download: PSPackagesOfficial + artifact: 'drop_msixbundle_CreateMSIXBundle' + displayName: Download package + + - pwsh: | + $payloadDir = '$(Pipeline.Workspace)\PSPackagesOfficial\drop_msixbundle_CreateMSIXBundle' + Get-ChildItem $payloadDir -Recurse | Out-String -Width 150 + $vstsCommandString = "vso[task.setvariable variable=PayloadDir]$payloadDir" + Write-Host "sending " + $vstsCommandString + Write-Host "##$vstsCommandString" + displayName: 'Capture Artifact Listing' + + - pwsh: | + $bundlePackage = Get-ChildItem '$(PayloadDir)\*.msixbundle' + Write-Verbose -Verbose ("MSIX bundle package: " + $bundlePackage.FullName -join ', ') + if ($bundlePackage.Count -ne 1) { + throw "Expected to find 1 MSIX bundle package, but found $($bundlePackage.Count)" + } + + if (-not (Test-Path '$(ob_outputDirectory)' -PathType Container)) { + $null = New-Item '$(ob_outputDirectory)' -ItemType Directory -ErrorAction Stop + } + + $targetPath = Join-Path '$(ob_outputDirectory)' 'Microsoft.PowerShell_8wekyb3d8bbwe.msixbundle' + Copy-Item -Verbose -Path $bundlePackage.FullName -Destination $targetPath + displayName: 'Stage msixbundle for vpack' + + - pwsh: | + Write-Verbose "VPack Version: $(ob_createvpack_version)" -Verbose + $vpackFiles = Get-ChildItem -Path $(ob_outputDirectory)\* -Recurse + if($vpackFiles.Count -eq 0) { + throw "No files found in $(ob_outputDirectory)" + } + $vpackFiles | Out-String -Width 150 + displayName: Debug Output Directory and Version + condition: succeededOrFailed() diff --git a/.pipelines/PowerShell-Coordinated_Packages-Official.yml b/.pipelines/PowerShell-Coordinated_Packages-Official.yml new file mode 100644 index 00000000000..e4de1fe5c21 --- /dev/null +++ b/.pipelines/PowerShell-Coordinated_Packages-Official.yml @@ -0,0 +1,323 @@ +trigger: none + +parameters: + - name: InternalSDKBlobURL + displayName: URL to the blob having internal .NET SDK + type: string + default: ' ' + - name: ReleaseTagVar + displayName: Release Tag + type: string + default: 'fromBranch' + - name: SKIP_SIGNING + displayName: Debugging - Skip Signing + type: string + default: 'NO' + - name: RUN_TEST_AND_RELEASE + displayName: Debugging - Run Test and Release Artifacts Stage + type: boolean + default: true + - name: RUN_WINDOWS + displayName: Debugging - Enable Windows Stage + type: boolean + default: true + - name: ENABLE_MSBUILD_BINLOGS + displayName: Debugging - Enable MSBuild Binary Logs + type: boolean + default: false + - name: FORCE_CODEQL + displayName: Debugging - Enable CodeQL and set cadence to 1 hour + type: boolean + default: false + - name: OfficialBuild + type: boolean + default: false + +name: bins-$(BUILD.SOURCEBRANCHNAME)-prod.${{ parameters.OfficialBuild }}-$(Build.BuildId) + +resources: + repositories: + - repository: ComplianceRepo + type: github + endpoint: ComplianceGHRepo + name: PowerShell/compliance + ref: master + - repository: onebranchTemplates + type: git + name: OneBranch.Pipelines/GovernedTemplates + ref: refs/heads/main + +variables: + - name: PS_RELEASE_BUILD + value: 1 + - name: DOTNET_CLI_TELEMETRY_OPTOUT + value: 1 + - name: POWERSHELL_TELEMETRY_OPTOUT + value: 1 + - name: nugetMultiFeedWarnLevel + value: none + - name: NugetSecurityAnalysisWarningLevel + value: none + - name: skipNugetSecurityAnalysis + value: true + - name: branchCounterKey + value: $[format('{0:yyyyMMdd}-{1}', pipeline.startTime,variables['Build.SourceBranch'])] + - name: branchCounter + value: $[counter(variables['branchCounterKey'], 1)] + - name: BUILDSECMON_OPT_IN + value: true + - name: __DOTNET_RUNTIME_FEED + value: ${{ parameters.InternalSDKBlobURL }} + - name: LinuxContainerImage + value: mcr.microsoft.com/onebranch/azurelinux/build:3.0 + - name: WindowsContainerImage + value: onebranch.azurecr.io/windows/ltsc2019/vse2022:latest + - name: CDP_DEFINITION_BUILD_COUNT + value: $[counter('', 0)] + - name: ReleaseTagVar + value: ${{ parameters.ReleaseTagVar }} + - name: SKIP_SIGNING + value: ${{ parameters.SKIP_SIGNING }} + - group: mscodehub-feed-read-general + - group: mscodehub-feed-read-akv + - name: ENABLE_MSBUILD_BINLOGS + value: ${{ parameters.ENABLE_MSBUILD_BINLOGS }} + - ${{ if eq(parameters['FORCE_CODEQL'],'true') }}: + # Cadence is hours before CodeQL will allow a re-upload of the database + - name: CodeQL.Cadence + value: 1 + - name: CODEQL_ENABLED + ${{ if or(eq(variables['Build.SourceBranch'], 'refs/heads/master'), eq(parameters['FORCE_CODEQL'],'true')) }}: + value: true + ${{ else }}: + value: false + - name: templateFile + value: ${{ iif ( parameters.OfficialBuild, 'v2/OneBranch.Official.CrossPlat.yml@onebranchTemplates', 'v2/OneBranch.NonOfficial.CrossPlat.yml@onebranchTemplates' ) }} + # Fix for BinSkim ICU package error in Linux containers + - name: DOTNET_SYSTEM_GLOBALIZATION_INVARIANT + value: true + # Disable BinSkim at job level to override NonOfficial template defaults + - name: ob_sdl_binskim_enabled + value: false + - name: ps_official_build + value: ${{ parameters.OfficialBuild }} + +extends: + template: ${{ variables.templateFile }} + parameters: + customTags: 'ES365AIMigrationTooling' + featureFlags: + LinuxHostVersion: + Network: KS3 + WindowsHostVersion: + Network: KS3 + incrementalSDLBinaryAnalysis: true + globalSdl: + disableLegacyManifest: true + # disabled Armorty as we dont have any ARM templates to scan. It fails on some sample ARM templates. + armory: + enabled: false + sbom: + enabled: true + codeql: + compiled: + enabled: $(CODEQL_ENABLED) + tsaEnabled: true # This enables TSA bug filing only for CodeQL 3000 + credscan: + enabled: true + scanFolder: $(Build.SourcesDirectory) + suppressionsFile: $(Build.SourcesDirectory)\.config\suppress.json + cg: + enabled: true + ignoreDirectories: '.devcontainer,demos,docker,docs,src,test,tools/packaging' + binskim: + enabled: false + exactToolVersion: 4.4.2 + # APIScan requires a non-Ready-To-Run build + apiscan: + enabled: false + tsaOptionsFile: .config\tsaoptions.json + + stages: + - stage: prep + jobs: + - job: SetVars + displayName: Set Variables + pool: + type: linux + + variables: + - name: ob_outputDirectory + value: '$(Build.ArtifactStagingDirectory)/ONEBRANCH_ARTIFACT/BuildJson' + - name: ob_sdl_codeSignValidation_enabled + value: false + - name: ob_sdl_codeql_compiled_enabled + value: false + - name: ob_sdl_credscan_suppressionsFile + value: $(Build.SourcesDirectory)\PowerShell\.config\suppress.json + - name: ob_sdl_tsa_configFile + value: $(Build.SourcesDirectory)\PowerShell\.config\tsaoptions.json + - name: ob_signing_setup_enabled + value: false + - name: ob_sdl_sbom_enabled + value: false + + steps: + - checkout: self + clean: true + env: + ob_restore_phase: true # This ensures checkout is done at the beginning of the restore phase + + - pwsh: | + Get-ChildItem Env: | Out-String -width 9999 -Stream | write-Verbose -Verbose + displayName: Capture environment variables + env: + ob_restore_phase: true # This ensures checkout is done at the beginning of the restore phase + + - template: /.pipelines/templates/SetVersionVariables.yml@self + parameters: + ReleaseTagVar: $(ReleaseTagVar) + CreateJson: yes + + - stage: macos + displayName: macOS - build and sign + dependsOn: ['prep'] + jobs: + - template: /.pipelines/templates/mac.yml@self + parameters: + buildArchitecture: x64 + - template: /.pipelines/templates/mac.yml@self + parameters: + buildArchitecture: arm64 + + - stage: linux + displayName: linux - build and sign + dependsOn: ['prep'] + jobs: + - template: /.pipelines/templates/linux.yml@self + parameters: + Runtime: 'linux-x64' + JobName: 'linux_x64' + + - template: /.pipelines/templates/linux.yml@self + parameters: + Runtime: 'linux-x64' + JobName: 'linux_x64_minSize' + BuildConfiguration: 'minSize' + + - template: /.pipelines/templates/linux.yml@self + parameters: + Runtime: 'linux-arm' + JobName: 'linux_arm' + + - template: /.pipelines/templates/linux.yml@self + parameters: + Runtime: 'linux-arm64' + JobName: 'linux_arm64' + + - template: /.pipelines/templates/linux.yml@self + parameters: + Runtime: 'fxdependent-linux-x64' + JobName: 'linux_fxd_x64_mariner' + + - template: /.pipelines/templates/linux.yml@self + parameters: + Runtime: 'fxdependent-linux-arm64' + JobName: 'linux_fxd_arm64_mariner' + + - template: /.pipelines/templates/linux.yml@self + parameters: + Runtime: 'fxdependent-noopt-linux-musl-x64' + JobName: 'linux_fxd_x64_alpine' + + - template: /.pipelines/templates/linux.yml@self + parameters: + Runtime: 'fxdependent' + JobName: 'linux_fxd' + + - template: /.pipelines/templates/linux.yml@self + parameters: + Runtime: 'linux-musl-x64' + JobName: 'linux_x64_alpine' + + - stage: windows + displayName: windows - build and sign + dependsOn: ['prep'] + condition: and(succeeded(),eq('${{ parameters.RUN_WINDOWS }}','true')) + jobs: + - template: /.pipelines/templates/windows-hosted-build.yml@self + parameters: + Architecture: x64 + BuildConfiguration: release + JobName: build_windows_x64_release + - template: /.pipelines/templates/windows-hosted-build.yml@self + parameters: + Architecture: x64 + BuildConfiguration: minSize + JobName: build_windows_x64_minSize_release + - template: /.pipelines/templates/windows-hosted-build.yml@self + parameters: + Architecture: x86 + JobName: build_windows_x86_release + - template: /.pipelines/templates/windows-hosted-build.yml@self + parameters: + Architecture: arm64 + JobName: build_windows_arm64_release + - template: /.pipelines/templates/windows-hosted-build.yml@self + parameters: + Architecture: fxdependent + JobName: build_windows_fxdependent_release + - template: /.pipelines/templates/windows-hosted-build.yml@self + parameters: + Architecture: fxdependentWinDesktop + JobName: build_windows_fxdependentWinDesktop_release + + - stage: test_and_release_artifacts + displayName: Test and Release Artifacts + dependsOn: ['prep'] + condition: and(succeeded(),eq('${{ parameters.RUN_TEST_AND_RELEASE }}','true')) + jobs: + - template: /.pipelines/templates/testartifacts.yml@self + + - job: release_json + displayName: Create and Upload release.json + pool: + type: windows + variables: + - name: ob_outputDirectory + value: '$(Build.ArtifactStagingDirectory)/ONEBRANCH_ARTIFACT' + - name: ob_sdl_tsa_configFile + value: $(Build.SourcesDirectory)\PowerShell\.config\tsaoptions.json + - name: ob_sdl_credscan_suppressionsFile + value: $(Build.SourcesDirectory)\PowerShell\.config\suppress.json + steps: + - checkout: self + clean: true + - template: /.pipelines/templates/SetVersionVariables.yml@self + parameters: + ReleaseTagVar: $(ReleaseTagVar) + - template: /.pipelines/templates/rebuild-branch-check.yml@self + - powershell: | + $metadata = Get-Content '$(Build.SourcesDirectory)/PowerShell/tools/metadata.json' -Raw | ConvertFrom-Json + + # Use the rebuild branch check from the template + $isRebuildBranch = '$(RebuildBranchCheck.IsRebuildBranch)' -eq 'true' + + # Don't mark as LTS release for rebuild branches + $LTS = $metadata.LTSRelease.Package -and -not $isRebuildBranch + + if ($isRebuildBranch) { + Write-Verbose -Message "Rebuild branch detected, not marking as LTS release" -Verbose + } + + @{ ReleaseVersion = "$(Version)"; LTSRelease = $LTS } | ConvertTo-Json | Out-File "$(Build.StagingDirectory)\release.json" + Get-Content "$(Build.StagingDirectory)\release.json" + + if (-not (Test-Path "$(ob_outputDirectory)\metadata")) { + New-Item -ItemType Directory -Path "$(ob_outputDirectory)\metadata" + } + + Copy-Item -Path "$(Build.StagingDirectory)\release.json" -Destination "$(ob_outputDirectory)\metadata" -Force + displayName: Create and upload release.json file to build artifact + retryCountOnTaskFailure: 2 + - template: /.pipelines/templates/step/finalize.yml@self diff --git a/.pipelines/PowerShell-Packages-Official.yml b/.pipelines/PowerShell-Packages-Official.yml new file mode 100644 index 00000000000..18ef7b2d14c --- /dev/null +++ b/.pipelines/PowerShell-Packages-Official.yml @@ -0,0 +1,309 @@ +trigger: none + +parameters: # parameters are shown up in ADO UI in a build queue time + - name: ForceAzureBlobDelete + displayName: Delete Azure Blob + type: string + values: + - true + - false + default: false + - name: 'debug' + displayName: 'Enable debug output' + type: boolean + default: false + - name: InternalSDKBlobURL + displayName: URL to the blob having internal .NET SDK + type: string + default: ' ' + - name: ReleaseTagVar + displayName: Release Tag + type: string + default: 'fromBranch' + - name: SKIP_SIGNING + displayName: Skip Signing + type: string + default: 'NO' + - name: OfficialBuild + type: boolean + default: false + - name: disableNetworkIsolation + type: boolean + default: false + +name: pkgs-$(BUILD.SOURCEBRANCHNAME)-prod.${{ parameters.OfficialBuild }}-$(Build.BuildId) + +variables: + - name: CDP_DEFINITION_BUILD_COUNT + value: $[counter('', 0)] # needed for onebranch.pipeline.version task + - name: system.debug + value: ${{ parameters.debug }} + - name: ENABLE_PRS_DELAYSIGN + value: 1 + - name: ROOT + value: $(Build.SourcesDirectory) + - name: ForceAzureBlobDelete + value: ${{ parameters.ForceAzureBlobDelete }} + - name: NUGET_XMLDOC_MODE + value: none + - name: nugetMultiFeedWarnLevel + value: none + - name: NugetSecurityAnalysisWarningLevel + value: none + - name: skipNugetSecurityAnalysis + value: true + - name: ReleaseTagVar + value: ${{ parameters.ReleaseTagVar }} + - name: ob_outputDirectory + value: '$(Build.ArtifactStagingDirectory)/ONEBRANCH_ARTIFACT' + - name: WindowsContainerImage + value: 'onebranch.azurecr.io/windows/ltsc2022/vse2022:latest' # Docker image which is used to build the project + - name: LinuxContainerImage + value: mcr.microsoft.com/onebranch/azurelinux/build:3.0 + - group: mscodehub-feed-read-general + - group: mscodehub-feed-read-akv + - name: branchCounterKey + value: $[format('{0:yyyyMMdd}-{1}', pipeline.startTime,variables['Build.SourceBranch'])] + - name: branchCounter + value: $[counter(variables['branchCounterKey'], 1)] + - group: MSIXSigningProfile + - name: templateFile + value: ${{ iif ( parameters.OfficialBuild, 'v2/OneBranch.Official.CrossPlat.yml@onebranchTemplates', 'v2/OneBranch.NonOfficial.CrossPlat.yml@onebranchTemplates' ) }} + - name: disableNetworkIsolation + value: ${{ parameters.disableNetworkIsolation }} + +resources: + pipelines: + - pipeline: CoOrdinatedBuildPipeline + source: 'PowerShell-Coordinated Binaries-Official' + trigger: + branches: + include: + - master + - releases/* + + repositories: + - repository: onebranchTemplates + type: git + name: OneBranch.Pipelines/GovernedTemplates + ref: refs/heads/main + +extends: + template: ${{ variables.templateFile }} + parameters: + cloudvault: + enabled: false + featureFlags: + WindowsHostVersion: + Version: 2022 + Network: KS3 + LinuxHostVersion: + Network: KS3 + linuxEsrpSigning: true + incrementalSDLBinaryAnalysis: true + disableNetworkIsolation: ${{ variables.disableNetworkIsolation }} + globalSdl: + disableLegacyManifest: true + # disabled Armorty as we dont have any ARM templates to scan. It fails on some sample ARM templates. + armory: + enabled: false + sbom: + enabled: true + compiled: + enabled: false + credscan: + enabled: true + scanFolder: $(Build.SourcesDirectory) + suppressionsFile: $(Build.SourcesDirectory)\.config\suppress.json + cg: + enabled: true + ignoreDirectories: '.devcontainer,demos,docker,docs,src,test,tools/packaging' + binskim: + enabled: false + exactToolVersion: 4.4.2 + # APIScan requires a non-Ready-To-Run build + apiscan: + enabled: false + tsaOptionsFile: .config\tsaoptions.json + stages: + - stage: prep + displayName: 'Prep BuildInfo+Az' + jobs: + - template: /.pipelines/templates/checkAzureContainer.yml@self + + - stage: mac_package + displayName: 'macOS Pkg+Sign' + dependsOn: [] + jobs: + - template: /.pipelines/templates/mac-package-build.yml@self + parameters: + buildArchitecture: x64 + + - template: /.pipelines/templates/mac-package-build.yml@self + parameters: + buildArchitecture: arm64 + + - stage: windows_package_build + displayName: 'Win Pkg (unsigned)' + dependsOn: [] + jobs: + - template: /.pipelines/templates/packaging/windows/package.yml@self + parameters: + runtime: x64 + + - template: /.pipelines/templates/packaging/windows/package.yml@self + parameters: + runtime: arm64 + + - template: /.pipelines/templates/packaging/windows/package.yml@self + parameters: + runtime: x86 + + - template: /.pipelines/templates/packaging/windows/package.yml@self + parameters: + runtime: fxdependent + + - template: /.pipelines/templates/packaging/windows/package.yml@self + parameters: + runtime: fxdependentWinDesktop + + - template: /.pipelines/templates/packaging/windows/package.yml@self + parameters: + runtime: minsize + + - stage: windows_package_sign + displayName: 'Win Pkg Sign' + dependsOn: [windows_package_build] + jobs: + - template: /.pipelines/templates/packaging/windows/sign.yml@self + parameters: + runtime: x64 + + - template: /.pipelines/templates/packaging/windows/sign.yml@self + parameters: + runtime: arm64 + + - template: /.pipelines/templates/packaging/windows/sign.yml@self + parameters: + runtime: x86 + + - template: /.pipelines/templates/packaging/windows/sign.yml@self + parameters: + runtime: fxdependent + + - template: /.pipelines/templates/packaging/windows/sign.yml@self + parameters: + runtime: fxdependentWinDesktop + + - template: /.pipelines/templates/packaging/windows/sign.yml@self + parameters: + runtime: minsize + + - stage: linux_package + displayName: 'Linux Pkg+Sign' + dependsOn: [] + jobs: + - template: /.pipelines/templates/linux-package-build.yml@self + parameters: + unsignedDrop: 'drop_linux_build_linux_x64' + signedDrop: 'drop_linux_sign_linux_x64' + packageType: deb + jobName: deb + + - template: /.pipelines/templates/linux-package-build.yml@self + parameters: + unsignedDrop: 'drop_linux_build_linux_fxd_x64_mariner' + signedDrop: 'drop_linux_sign_linux_fxd_x64_mariner' + packageType: rpm-fxdependent #mariner-x64 + jobName: mariner_x64 + signingProfile: 'CP-459159-pgpdetached' + + - template: /.pipelines/templates/linux-package-build.yml@self + parameters: + unsignedDrop: 'drop_linux_build_linux_fxd_arm64_mariner' + signedDrop: 'drop_linux_sign_linux_fxd_arm64_mariner' + packageType: rpm-fxdependent-arm64 #mariner-arm64 + jobName: mariner_arm64 + signingProfile: 'CP-459159-pgpdetached' + + - template: /.pipelines/templates/linux-package-build.yml@self + parameters: + unsignedDrop: 'drop_linux_build_linux_x64' + signedDrop: 'drop_linux_sign_linux_x64' + packageType: rpm + jobName: rpm + + - template: /.pipelines/templates/linux-package-build.yml@self + parameters: + unsignedDrop: 'drop_linux_build_linux_arm' + signedDrop: 'drop_linux_sign_linux_arm' + packageType: tar-arm + jobName: tar_arm + + - template: /.pipelines/templates/linux-package-build.yml@self + parameters: + unsignedDrop: 'drop_linux_build_linux_arm64' + signedDrop: 'drop_linux_sign_linux_arm64' + packageType: tar-arm64 + jobName: tar_arm64 + + - template: /.pipelines/templates/linux-package-build.yml@self + parameters: + unsignedDrop: 'drop_linux_build_linux_x64_alpine' + signedDrop: 'drop_linux_sign_linux_x64_alpine' + packageType: tar-alpine + jobName: tar_alpine + + - template: /.pipelines/templates/linux-package-build.yml@self + parameters: + unsignedDrop: 'drop_linux_build_linux_fxd' + signedDrop: 'drop_linux_sign_linux_fxd' + packageType: fxdependent + jobName: fxdependent + + - template: /.pipelines/templates/linux-package-build.yml@self + parameters: + unsignedDrop: 'drop_linux_build_linux_x64' + signedDrop: 'drop_linux_sign_linux_x64' + packageType: tar + jobName: tar + + - template: /.pipelines/templates/linux-package-build.yml@self + parameters: + unsignedDrop: 'drop_linux_build_linux_fxd_x64_alpine' + signedDrop: 'drop_linux_sign_linux_fxd_x64_alpine' + packageType: tar-alpine-fxdependent + jobName: tar_alpine_fxd + + - template: /.pipelines/templates/linux-package-build.yml@self + parameters: + unsignedDrop: 'drop_linux_build_linux_x64_minSize' + signedDrop: 'drop_linux_sign_linux_x64_minSize' + packageType: min-size + jobName: minSize + + - stage: nupkg + displayName: 'NuGet Pkg+Sign' + dependsOn: [] + jobs: + - template: /.pipelines/templates/nupkg.yml@self + + - stage: msixbundle + displayName: 'MSIX Bundle+Sign' + dependsOn: [windows_package_build] # Only depends on unsigned packages + jobs: + - template: /.pipelines/templates/package-create-msix.yml@self + parameters: + OfficialBuild: ${{ parameters.OfficialBuild }} + + - stage: upload + displayName: 'Upload' + dependsOn: [prep, mac_package, windows_package_sign, linux_package, nupkg, msixbundle] # prep needed for BuildInfo JSON + jobs: + - template: /.pipelines/templates/uploadToAzure.yml@self + + - stage: validatePackages + displayName: 'Validate Packages' + dependsOn: [upload] + jobs: + - template: /.pipelines/templates/release-validate-packagenames.yml@self diff --git a/.pipelines/PowerShell-Release-Official-Azure.yml b/.pipelines/PowerShell-Release-Official-Azure.yml new file mode 100644 index 00000000000..f4c41143b5f --- /dev/null +++ b/.pipelines/PowerShell-Release-Official-Azure.yml @@ -0,0 +1,107 @@ +trigger: none + +parameters: # parameters are shown up in ADO UI in a build queue time + - name: 'debug' + displayName: 'Enable debug output' + type: boolean + default: false + - name: skipPublish + displayName: Skip PMC Publish + type: boolean + default: false + - name: SKIP_SIGNING + displayName: Skip Signing + type: string + default: 'NO' + - name: OfficialBuild + type: boolean + default: false + +name: ev2-$(BUILD.SOURCEBRANCHNAME)-prod.${{ parameters.OfficialBuild }}-$(Build.BuildId) + +variables: + - name: CDP_DEFINITION_BUILD_COUNT + value: $[counter('', 0)] + - name: system.debug + value: ${{ parameters.debug }} + - name: ENABLE_PRS_DELAYSIGN + value: 1 + - name: ROOT + value: $(Build.SourcesDirectory) + - name: REPOROOT + value: $(Build.SourcesDirectory) + - name: OUTPUTROOT + value: $(REPOROOT)\out + - name: NUGET_XMLDOC_MODE + value: none + - name: nugetMultiFeedWarnLevel + value: none + - name: NugetSecurityAnalysisWarningLevel + value: none + - name: skipNugetSecurityAnalysis + value: true + - name: ob_outputDirectory + value: '$(Build.ArtifactStagingDirectory)/ONEBRANCH_ARTIFACT' + - name: ob_sdl_tsa_configFile + value: $(Build.SourcesDirectory)\.config\tsaoptions.json + - name: WindowsContainerImage + value: 'onebranch.azurecr.io/windows/ltsc2022/vse2022:latest' + - name: LinuxContainerImage + value: mcr.microsoft.com/onebranch/azurelinux/build:3.0 + - group: PoolNames + - name: templateFile + value: ${{ iif ( parameters.OfficialBuild, 'v2/OneBranch.Official.CrossPlat.yml@onebranchTemplates', 'v2/OneBranch.NonOfficial.CrossPlat.yml@onebranchTemplates' ) }} + +resources: + repositories: + - repository: onebranchTemplates + type: git + name: OneBranch.Pipelines/GovernedTemplates + ref: refs/heads/main + + pipelines: + - pipeline: CoOrdinatedBuildPipeline + source: 'PowerShell-Coordinated Binaries-Official' + + - pipeline: PSPackagesOfficial + source: 'PowerShell-Packages-Official' + trigger: + branches: + include: + - master + - releases/* + +extends: + template: ${{ variables.templateFile }} + parameters: + featureFlags: + WindowsHostVersion: + Version: 2022 + Network: Netlock + linuxEsrpSigning: true + incrementalSDLBinaryAnalysis: true + cloudvault: + enabled: false + globalSdl: + disableLegacyManifest: true + # disabled Armory as we dont have any ARM templates to scan. It fails on some sample ARM templates. + armory: + enabled: false + tsa: + enabled: true + credscan: + enabled: true + scanFolder: $(Build.SourcesDirectory) + suppressionsFile: $(Build.SourcesDirectory)\.config\suppress.json + binskim: + break: false # always break the build on binskim issues in addition to TSA upload + exactToolVersion: 4.4.2 + policheck: + break: true # always break the build on policheck issues. You can disable it by setting to 'false' + tsaOptionsFile: .config\tsaoptions.json + stages: + - template: /.pipelines/templates/release-prep-for-ev2.yml@self + parameters: + skipPublish: ${{ parameters.skipPublish }} + + - template: /.pipelines/templates/release-publish-pmc.yml@self diff --git a/.pipelines/PowerShell-Release-Official.yml b/.pipelines/PowerShell-Release-Official.yml new file mode 100644 index 00000000000..868d61ebfd0 --- /dev/null +++ b/.pipelines/PowerShell-Release-Official.yml @@ -0,0 +1,443 @@ +trigger: none + +parameters: # parameters are shown up in ADO UI in a build queue time + - name: 'debug' + displayName: 'Enable debug output' + type: boolean + default: false + - name: InternalSDKBlobURL + displayName: URL to the blob having internal .NET SDK + type: string + default: ' ' + - name: ReleaseTagVar + displayName: Release Tag + type: string + default: 'fromBranch' + - name: SKIP_SIGNING + displayName: Skip Signing + type: string + default: 'NO' + - name: SkipPublish + displayName: Skip Publishing to Nuget + type: boolean + default: false + - name: SkipPSInfraInstallers + displayName: Skip Copying Archives and Installers to PSInfrastructure Public Location + type: boolean + default: false + - name: skipMSIXPublish + displayName: Skip MSIX Publish + type: boolean + default: false + - name: OfficialBuild + type: boolean + default: false + +name: release-$(BUILD.SOURCEBRANCHNAME)-prod.${{ parameters.OfficialBuild }}-$(Build.BuildId) + +variables: + - name: CDP_DEFINITION_BUILD_COUNT + value: $[counter('', 0)] + - name: system.debug + value: ${{ parameters.debug }} + - name: ENABLE_PRS_DELAYSIGN + value: 1 + - name: ROOT + value: $(Build.SourcesDirectory) + - name: REPOROOT + value: $(Build.SourcesDirectory) + - name: OUTPUTROOT + value: $(REPOROOT)\out + - name: NUGET_XMLDOC_MODE + value: none + - name: nugetMultiFeedWarnLevel + value: none + - name: NugetSecurityAnalysisWarningLevel + value: none + - name: skipNugetSecurityAnalysis + value: true + - name: ob_outputDirectory + value: '$(Build.ArtifactStagingDirectory)/ONEBRANCH_ARTIFACT' + - name: WindowsContainerImage + value: 'onebranch.azurecr.io/windows/ltsc2022/vse2022:latest' + - name: LinuxContainerImage + value: mcr.microsoft.com/onebranch/azurelinux/build:3.0 + - name: ReleaseTagVar + value: ${{ parameters.ReleaseTagVar }} + - group: PoolNames + - name: templateFile + value: ${{ iif ( parameters.OfficialBuild, 'v2/OneBranch.Official.CrossPlat.yml@onebranchTemplates', 'v2/OneBranch.NonOfficial.CrossPlat.yml@onebranchTemplates' ) }} + - name: releaseEnvironment + value: ${{ iif ( parameters.OfficialBuild, 'Production', 'Test' ) }} + # Fix for BinSkim ICU package error in Linux containers + - name: DOTNET_SYSTEM_GLOBALIZATION_INVARIANT + value: true + +resources: + repositories: + - repository: onebranchTemplates + type: git + name: OneBranch.Pipelines/GovernedTemplates + ref: refs/heads/main + - repository: PSInternalTools + type: git + name: PowerShellCore/Internal-PowerShellTeam-Tools + ref: refs/heads/master + + pipelines: + - pipeline: CoOrdinatedBuildPipeline + source: 'PowerShell-Coordinated Binaries-Official' + + - pipeline: PSPackagesOfficial + source: 'PowerShell-Packages-Official' + trigger: + branches: + include: + - master + - releases/* + +extends: + template: ${{ variables.templateFile }} + parameters: + release: + category: NonAzure + featureFlags: + WindowsHostVersion: + Version: 2022 + Network: KS3 + incrementalSDLBinaryAnalysis: true + cloudvault: + enabled: false + globalSdl: + disableLegacyManifest: true + # disabled Armory as we dont have any ARM templates to scan. It fails on some sample ARM templates. + armory: + enabled: false + tsa: + enabled: true + credscan: + enabled: true + scanFolder: $(Build.SourcesDirectory) + suppressionsFile: $(Build.SourcesDirectory)\.config\suppress.json + binskim: + break: false # always break the build on binskim issues in addition to TSA upload + exactToolVersion: 4.4.2 + policheck: + break: true # always break the build on policheck issues. You can disable it by setting to 'false' + # suppression: + # suppressionFile: $(Build.SourcesDirectory)\.gdn\global.gdnsuppress + tsaOptionsFile: .config\tsaoptions.json + + stages: + - stage: setReleaseTagAndChangelog + displayName: 'Set Release Tag and Upload Changelog' + jobs: + - template: /.pipelines/templates/release-SetTagAndChangelog.yml@self + + - stage: validateSdk + displayName: 'Validate SDK' + dependsOn: [] + jobs: + - template: /.pipelines/templates/release-validate-sdk.yml@self + parameters: + jobName: "windowsSDK" + displayName: "Windows SDK Validation" + imageName: PSMMS2019-Secure + poolName: $(windowsPool) + + - template: /.pipelines/templates/release-validate-sdk.yml@self + parameters: + jobName: "MacOSSDK" + displayName: "MacOS SDK Validation" + imageName: macOS-latest + poolName: Azure Pipelines + + - template: /.pipelines/templates/release-validate-sdk.yml@self + parameters: + jobName: "LinuxSDK" + displayName: "Linux SDK Validation" + imageName: PSMMSUbuntu22.04-Secure + poolName: $(ubuntuPool) + + - stage: gbltool + displayName: 'Validate Global tools' + dependsOn: [] + jobs: + - template: /.pipelines/templates/release-validate-globaltools.yml@self + parameters: + jobName: "WindowsGlobalTools" + displayName: "Windows Global Tools Validation" + jobtype: windows + + - template: /.pipelines/templates/release-validate-globaltools.yml@self + parameters: + jobName: "LinuxGlobalTools" + displayName: "Linux Global Tools Validation" + jobtype: linux + globalToolExeName: 'pwsh' + globalToolPackageName: 'PowerShell.Linux.x64' + + - stage: fxdpackages + displayName: 'Validate FXD Packages' + dependsOn: [] + jobs: + - template: /.pipelines/templates/release-validate-fxdpackages.yml@self + parameters: + jobName: 'winfxd' + displayName: 'Validate Win Fxd Packages' + jobtype: 'windows' + artifactName: 'drop_windows_package_package_win_fxdependent' + packageNamePattern: '**/*win-fxdependent.zip' + + - template: /.pipelines/templates/release-validate-fxdpackages.yml@self + parameters: + jobName: 'winfxdDesktop' + displayName: 'Validate WinDesktop Fxd Packages' + jobtype: 'windows' + artifactName: 'drop_windows_package_package_win_fxdependentWinDesktop' + packageNamePattern: '**/*win-fxdependentwinDesktop.zip' + + - template: /.pipelines/templates/release-validate-fxdpackages.yml@self + parameters: + jobName: 'linuxfxd' + displayName: 'Validate Linux Fxd Packages' + jobtype: 'linux' + artifactName: 'drop_linux_package_fxdependent' + packageNamePattern: '**/*linux-x64-fxdependent.tar.gz' + + - template: /.pipelines/templates/release-validate-fxdpackages.yml@self + parameters: + jobName: 'linuxArm64fxd' + displayName: 'Validate Linux ARM64 Fxd Packages' + jobtype: 'linux' + artifactName: 'drop_linux_package_fxdependent' + # this is really an architecture independent package + packageNamePattern: '**/*linux-x64-fxdependent.tar.gz' + arm64: 'yes' + enableCredScan: false + + - stage: ManualValidation + dependsOn: [] + displayName: Manual Validation + jobs: + - template: /.pipelines/templates/approvalJob.yml@self + parameters: + displayName: Validate Windows Packages + jobName: ValidateWinPkg + instructions: | + Validate zip package on windows + + - template: /.pipelines/templates/approvalJob.yml@self + parameters: + displayName: Validate OSX Packages + jobName: ValidateOsxPkg + instructions: | + Validate tar.gz package on osx-arm64 + + - stage: ReleaseAutomation + dependsOn: [] + displayName: 'Release Automation' + jobs: + - template: /.pipelines/templates/approvalJob.yml@self + parameters: + displayName: Start Release Automation + jobName: StartRA + instructions: | + Kick off Release automation build at: https://dev.azure.com/powershell-rel/Release-Automation/_build?definitionId=10&_a=summary + + - template: /.pipelines/templates/approvalJob.yml@self + parameters: + displayName: Triage results + jobName: TriageRA + dependsOnJob: StartRA + instructions: | + Triage ReleaseAutomation results + + - template: /.pipelines/templates/approvalJob.yml@self + parameters: + displayName: Signoff Tests + dependsOnJob: TriageRA + jobName: SignoffTests + instructions: | + Signoff ReleaseAutomation results + + - stage: UpdateChangeLog + displayName: Update the changelog + dependsOn: + - ManualValidation + - ReleaseAutomation + - fxdpackages + - gbltool + - validateSdk + + jobs: + - template: /.pipelines/templates/approvalJob.yml@self + parameters: + displayName: Make sure the changelog is updated + jobName: MergeChangeLog + instructions: | + Update and merge the changelog for the release. + This step is required for creating GitHub draft release. + + - stage: PublishGitHubReleaseAndNuget + displayName: Publish GitHub and Nuget Release + dependsOn: + - setReleaseTagAndChangelog + - UpdateChangeLog + variables: + ob_release_environment: ${{ variables.releaseEnvironment }} + jobs: + - template: /.pipelines/templates/release-githubNuget.yml@self + parameters: + skipPublish: ${{ parameters.SkipPublish }} + + - stage: PushGitTagAndMakeDraftPublic + displayName: Push Git Tag and Make Draft Public + dependsOn: PublishGitHubReleaseAndNuget + jobs: + - template: /.pipelines/templates/approvalJob.yml@self + parameters: + displayName: Push Git Tag + jobName: PushGitTag + instructions: | + Push the git tag to upstream + + - template: /.pipelines/templates/approvalJob.yml@self + parameters: + displayName: Make Draft Public + dependsOnJob: PushGitTag + jobName: DraftPublic + instructions: | + Make the GitHub Release Draft Public + + - stage: BlobPublic + displayName: Make Blob Public + dependsOn: + - UpdateChangeLog + - PushGitTagAndMakeDraftPublic + jobs: + - template: /.pipelines/templates/release-MakeBlobPublic.yml@self + parameters: + SkipPSInfraInstallers: ${{ parameters.SkipPSInfraInstallers }} + + - stage: PublishPMC + displayName: Publish PMC + dependsOn: PushGitTagAndMakeDraftPublic + jobs: + - template: /.pipelines/templates/approvalJob.yml@self + parameters: + displayName: Publish to PMC + jobName: ReleaseToPMC + instructions: | + Run PowerShell-Release-Official-Azure.yml pipeline to publish to PMC + + - stage: UpdateDotnetDocker + dependsOn: PushGitTagAndMakeDraftPublic + displayName: Update DotNet SDK Docker images + jobs: + - template: /.pipelines/templates/approvalJob.yml@self + parameters: + displayName: Update .NET SDK docker images + jobName: DotnetDocker + instructions: | + Create PR for updating dotnet-docker images to use latest PowerShell version. + 1. Fork and clone https://github.com/dotnet/dotnet-docker.git + 2. git checkout upstream/nightly -b updatePS + 3. dotnet run --project .\eng\update-dependencies\ specific --product-version powershell= --compute-shas + 4. create PR targeting nightly branch + + - stage: UpdateWinGet + dependsOn: PushGitTagAndMakeDraftPublic + displayName: Add manifest entry to winget + jobs: + - template: /.pipelines/templates/approvalJob.yml@self + parameters: + displayName: Add manifest entry to winget + jobName: UpdateWinGet + instructions: | + This is typically done by the community 1-2 days after the release. + + - stage: PublishMsix + dependsOn: + - setReleaseTagAndChangelog + - PushGitTagAndMakeDraftPublic + displayName: Publish MSIX to store + variables: + ob_release_environment: ${{ variables.releaseEnvironment }} + jobs: + - template: /.pipelines/templates/release-MSIX-Publish.yml@self + parameters: + skipMSIXPublish: ${{ parameters.skipMSIXPublish }} + + - stage: PublishVPack + dependsOn: PushGitTagAndMakeDraftPublic + displayName: Release vPack + jobs: + - template: /.pipelines/templates/approvalJob.yml@self + parameters: + displayName: Start 2 vPack Release pipelines + jobName: PublishVPack + instructions: | + 1. Kick off PowerShell-vPack-Official pipeline + 2. Kick off PowerShell-MSIXBundle-VPack pipeline + + # Need to verify if the Az PS / CLI team still uses this. Skippinng for this release. + # - stage: ReleaseDeps + # dependsOn: GitHubTasks + # displayName: Update pwsh.deps.json links + # jobs: + # - template: templates/release-UpdateDepsJson.yml + + - stage: UploadBuildInfoJson + dependsOn: PushGitTagAndMakeDraftPublic + displayName: Upload BuildInfo.json + jobs: + - template: /.pipelines/templates/release-upload-buildinfo.yml@self + + - stage: ReleaseSymbols + dependsOn: PushGitTagAndMakeDraftPublic + displayName: Release Symbols + jobs: + - template: /.pipelines/templates/release-symbols.yml@self + + - stage: ChangesToMaster + displayName: Ensure changes are in GH master + dependsOn: + - PublishPMC + jobs: + - template: /.pipelines/templates/approvalJob.yml@self + parameters: + displayName: Make sure changes are in master + jobName: MergeToMaster + instructions: | + Make sure that changes README.md and metadata.json are merged into master on GitHub. + + - stage: ReleaseToMU + displayName: Release to MU + dependsOn: PushGitTagAndMakeDraftPublic # This only needs the blob to be available + jobs: + - template: /.pipelines/templates/approvalJob.yml@self + parameters: + displayName: Release to MU + instructions: | + Notify the PM team to start the process of releasing to MU. + + - stage: ReleaseClose + displayName: Finish Release + dependsOn: + - ReleaseToMU + - ReleaseSymbols + jobs: + - template: /.pipelines/templates/approvalJob.yml@self + parameters: + displayName: Retain Build + jobName: RetainBuild + instructions: | + Retain the build + + - template: /.pipelines/templates/approvalJob.yml@self + parameters: + displayName: Delete release branch + jobName: DeleteBranch + instructions: | + Delete release diff --git a/.pipelines/PowerShell-vPack-Official.yml b/.pipelines/PowerShell-vPack-Official.yml new file mode 100644 index 00000000000..096dfb574a4 --- /dev/null +++ b/.pipelines/PowerShell-vPack-Official.yml @@ -0,0 +1,341 @@ +trigger: none + +parameters: # parameters are shown up in ADO UI in a build queue time +- name: OfficialBuild + type: boolean + default: true +- name: 'createVPack' + displayName: 'Create and Submit VPack' + type: boolean + default: true +- name: vPackName + type: string + displayName: 'VPack Name:' + default: 'PowerShell.BuildTool' + values: + - PowerShell.BuildTool + - PowerShell + - PowerShellDoNotUse +- name: 'ReleaseTagVar' + type: string + displayName: 'Release Tag Var:' + default: 'fromBranch' +- name: 'debug' + displayName: 'Enable debug output' + type: boolean + default: false +- name: netiso + displayName: "Network Isolation Policy" + type: string + values: + - KS4 + - R1 + - Netlock + default: "R1" + +name: vPack_$(Build.SourceBranchName)_Prod.${{ parameters.OfficialBuild }}_Create.${{ parameters.createVPack }}_Name.${{ parameters.vPackName}}_$(date:yyyyMMdd).$(rev:rr) + +variables: + - name: CDP_DEFINITION_BUILD_COUNT + value: $[counter('', 0)] + - name: system.debug + value: ${{ parameters.debug }} + - name: BuildSolution + value: $(Build.SourcesDirectory)\dirs.proj + - name: BuildConfiguration + value: Release + - name: WindowsContainerImage + value: 'onebranch.azurecr.io/windows/ltsc2019/vse2022:latest' + - name: Codeql.Enabled + value: false # pipeline is not building artifacts; it repackages existing artifacts into a vpack + - name: DOTNET_CLI_TELEMETRY_OPTOUT + value: 1 + - name: POWERSHELL_TELEMETRY_OPTOUT + value: 1 + - name: nugetMultiFeedWarnLevel + value: none + - name: ReleaseTagVar + value: ${{ parameters.ReleaseTagVar }} + - group: Azure Blob variable group + - group: certificate_logical_to_actual # used within signing task + - name: templateFile + value: ${{ iif ( parameters.OfficialBuild, 'v2/Microsoft.Official.yml@onebranchTemplates', 'v2/Microsoft.NonOfficial.yml@onebranchTemplates' ) }} + - group: DotNetPrivateBuildAccess + - group: certificate_logical_to_actual + - name: netiso + value: ${{ parameters.netiso }} +# We shouldn't be using PATs anymore +# - group: mscodehub-feed-read-general + +resources: + repositories: + - repository: onebranchTemplates + type: git + name: OneBranch.Pipelines/GovernedTemplates + ref: refs/heads/main + +extends: + template: ${{ variables.templateFile }} + parameters: + platform: + name: 'windows_undocked' # windows undocked + + featureFlags: + WindowsHostVersion: + Version: 2022 + Network: ${{ variables.netiso }} + + cloudvault: + enabled: false + + globalSdl: + useCustomPolicy: true # for signing code + disableLegacyManifest: true + # disabled Armory as we dont have any ARM templates to scan. It fails on some sample ARM templates. + armory: + enabled: false + sbom: + enabled: true + compiled: + enabled: false + credscan: + enabled: true + scanFolder: $(Build.SourcesDirectory) + suppressionsFile: $(Build.SourcesDirectory)\.config\suppress.json + binskim: + enabled: false + exactToolVersion: 4.4.2 + # APIScan requires a non-Ready-To-Run build + apiscan: + enabled: false + tsaOptionsFile: .config/tsaoptions.json + stages: + - stage: BuildStage + jobs: + - job: BuildJob + pool: + type: windows + + strategy: + matrix: + x86: + architecture: x86 + + x64: + architecture: x64 + + arm64: + architecture: arm64 + + variables: + ArtifactPlatform: 'windows' + ob_artifactBaseName: drop_build_$(architecture) + ob_outputDirectory: '$(BUILD.SOURCESDIRECTORY)\out' + ob_createvpack_enabled: ${{ parameters.createVPack }} + ob_createvpack_owneralias: tplunk + ob_createvpack_versionAs: parts + ob_createvpack_propsFile: true + ob_createvpack_verbose: true + ob_createvpack_packagename: '${{ parameters.vPackName }}.$(architecture)' + ob_createvpack_description: PowerShell $(architecture) $(version) + # I think the variables reload after we transition back to the host so this works. 🤷‍♂️ + ob_createvpack_majorVer: $(pwshMajorVersion) + ob_createvpack_minorVer: $(pwshMinorVersion) + ob_createvpack_patchVer: $(pwshPatchVersion) + ${{ if ne(variables['pwshPrereleaseVersion'], '') }}: + ob_createvpack_prereleaseVer: $(pwshPrereleaseVersion) + ${{ else }}: + ob_createvpack_prereleaseVer: $(Build.SourceVersion) + + steps: + - checkout: self + displayName: Checkout source code - during restore + clean: true + path: s + env: + ob_restore_phase: true + + - template: .pipelines/templates/SetVersionVariables.yml@self + parameters: + ReleaseTagVar: $(ReleaseTagVar) + CreateJson: yes + + - pwsh: | + $version = '$(Version)' + Write-Verbose -Verbose "Version: $version" + if(!$version) { + throw "Version is not set." + } + + $mainVersionParts = $version -split '-' + + Write-Verbose -Verbose "mainVersionParts: $($mainVersionParts[0]) ; $($mainVersionParts[1])" + $versionParts = $mainVersionParts[0] -split '[.]'; + $major = $versionParts[0] + $minor = $versionParts[1] + $patch = $versionParts[2] + + $previewPart = $mainVersionParts[1] + Write-Verbose -Verbose "previewPart: $previewPart" + + Write-Host "major: $major; minor: $minor; patch: $patch;" + + $vstsCommandString = "vso[task.setvariable variable=pwshMajorVersion]$major" + Write-Host ("sending " + $vstsCommandString) + Write-Host "##$vstsCommandString" + + $vstsCommandString = "vso[task.setvariable variable=pwshMinorVersion]$minor" + Write-Host ("sending " + $vstsCommandString) + Write-Host "##$vstsCommandString" + + $vstsCommandString = "vso[task.setvariable variable=pwshPatchVersion]$patch" + Write-Host ("sending " + $vstsCommandString) + Write-Host "##$vstsCommandString" + if($previewPart) { + $vstsCommandString = "vso[task.setvariable variable=pwshPrereleaseVersion]$previewPart" + } else { + Write-Verbose -Verbose "No prerelease part found in version string." + } + displayName: Set ob_createvpack_*Ver + env: + ob_restore_phase: true + + # Validate pwsh*Version variables + - pwsh: | + $variables = @("pwshMajorVersion", "pwshMinorVersion", "pwshPatchVersion") + foreach ($var in $variables) { + if (-not (get-item "Env:\$var" -ErrorAction SilentlyContinue).value) { + throw "Required variable '`$env:$var' is not set." + } + } + displayName: Validate pwsh*Version variables + env: + ob_restore_phase: true + + - pwsh: | + if($env:RELEASETAGVAR -match '-') { + throw "Don't release a preview build without coordinating with Windows Engineering Build Tools Team" + } + displayName: Stop any preview release + env: + ob_restore_phase: true + + - task: UseDotNet@2 + displayName: 'Use .NET Core sdk' + inputs: + packageType: sdk + version: 3.1.x + installationPath: $(Agent.ToolsDirectory)/dotnet + + ### BUILD ### + + - template: /.pipelines/templates/insert-nuget-config-azfeed.yml@self + parameters: + repoRoot: $(repoRoot) + + - task: CodeQL3000Init@0 # Add CodeQL Init task right before your 'Build' step. + env: + ob_restore_phase: true # Set ob_restore_phase to run this step before '🔒 Setup Signing' step. + inputs: + Enabled: true + AnalyzeInPipeline: false # Do not upload results + Language: csharp + + - task: UseDotNet@2 + displayName: 'Install .NET based on global.json' + inputs: + useGlobalJson: true + workingDirectory: $(repoRoot) + env: + ob_restore_phase: true + + - pwsh: | + # Need to set PowerShellRoot variable for obp-file-signing template + $vstsCommandString = "vso[task.setvariable variable=PowerShellRoot]$(repoRoot)" + Write-Host ("sending " + $vstsCommandString) + Write-Host "##$vstsCommandString" + + $Architecture = '$(Architecture)' + $runtime = switch ($Architecture) + { + "x64" { "win7-x64" } + "x86" { "win7-x86" } + "arm64" { "win-arm64" } + } + + $params = @{} + if ($env:BuildConfiguration -eq 'minSize') { + $params['ForMinimalSize'] = $true + } + + $vstsCommandString = "vso[task.setvariable variable=Runtime]$runtime" + Write-Host ("sending " + $vstsCommandString) + Write-Host "##$vstsCommandString" + + Write-Verbose -Message "Building PowerShell with Runtime: $runtime for '$env:BuildConfiguration' configuration" + Import-Module -Name $(repoRoot)/build.psm1 -Force + $buildWithSymbolsPath = New-Item -ItemType Directory -Path "$(Pipeline.Workspace)/Symbols_$Architecture" -Force + + Start-PSBootstrap -Scenario Package + $null = New-Item -ItemType Directory -Path $buildWithSymbolsPath -Force -Verbose + + $ReleaseTagParam = @{} + + if ($env:RELEASETAGVAR) { + $ReleaseTagParam['ReleaseTag'] = $env:RELEASETAGVAR + } + + Start-PSBuild -Runtime $runtime -Configuration Release -Output $buildWithSymbolsPath -Clean -PSModuleRestore @params @ReleaseTagParam + + $refFolderPath = Join-Path $buildWithSymbolsPath 'ref' + Write-Verbose -Verbose "refFolderPath: $refFolderPath" + $outputPath = Join-Path '$(ob_outputDirectory)' 'psoptions' + $null = New-Item -ItemType Directory -Path $outputPath -Force + $psOptPath = "$outputPath/psoptions.json" + Save-PSOptions -PSOptionsPath $psOptPath + + Write-Verbose -Verbose "Completed building PowerShell for '$env:BuildConfiguration' configuration" + displayName: Build Windows Universal - $(Architecture) -$(BuildConfiguration) Symbols folder + env: + __DOTNET_RUNTIME_FEED_KEY: $(RUNTIME_SOURCEFEED_KEY) + ob_restore_phase: true # Set ob_restore_phase to run this step before '🔒 Setup Signing' step. + + - task: CodeQL3000Finalize@0 # Add CodeQL Finalize task right after your 'Build' step. + env: + ob_restore_phase: true # Set ob_restore_phase to run this step before '🔒 Setup Signing' step. + + - task: ms.vss-governance-buildtask.governance-build-task-component-detection.ComponentGovernanceComponentDetection@0 + displayName: 'Component Detection' + inputs: + sourceScanPath: '$(repoRoot)\src' + ob_restore_phase: true + + - template: /.pipelines/templates/obp-file-signing.yml@self + parameters: + binPath: '$(Pipeline.Workspace)/Symbols_$(Architecture)' + SigningProfile: $(windows_build_tools_cert_id) + OfficialBuild: false + vPackScenario: true + + ### END OF BUILD ### + + - pwsh: | + Get-ChildItem env:/ob_createvpack_*Ver + Get-ChildItem -Path "$(Pipeline.Workspace)\Symbols_$(Architecture)\*" -Recurse + Get-Content "$(Pipeline.Workspace)\PowerShell\preview.json" -ErrorAction SilentlyContinue | Write-Host + displayName: Debug Output Directory and Version + condition: succeededOrFailed() + + - pwsh: | + Get-ChildItem -Path env: | Out-String -width 9999 -Stream | write-Verbose -Verbose + displayName: Capture Environment + condition: succeededOrFailed() + + - pwsh: | + $vpackFiles = Get-ChildItem -Path "$(Pipeline.Workspace)\Symbols_$(Architecture)\*" -Recurse + if($vpackFiles.Count -eq 0) { + throw "No files found in $(Pipeline.Workspace)\Symbols_$(Architecture)" + } + $vpackFiles + displayName: Debug Output Directory and Version + condition: succeededOrFailed() diff --git a/.pipelines/apiscan-gen-notice.yml b/.pipelines/apiscan-gen-notice.yml new file mode 100644 index 00000000000..9cc83e7136a --- /dev/null +++ b/.pipelines/apiscan-gen-notice.yml @@ -0,0 +1,115 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. +name: apiscan-genNotice-$(BUILD.SOURCEBRANCHNAME)-$(Build.BuildId) +trigger: none + +parameters: + - name: FORCE_CODEQL + displayName: Debugging - Enable CodeQL and set cadence to 1 hour + type: boolean + default: false + - name: SkipVerifyPackages + type: boolean + default: false + +variables: + # PAT permissions NOTE: Declare a SymbolServerPAT variable in this group with a 'microsoft' organizanization scoped PAT with 'Symbols' Read permission. + # A PAT in the wrong org will give a single Error 203. No PAT will give a single Error 401, and individual pdbs may be missing even if permissions are correct. + - group: symbols + - name: ob_outputDirectory + value: '$(Build.ArtifactStagingDirectory)/ONEBRANCH_ARTIFACT' + - name: CDP_DEFINITION_BUILD_COUNT + value: $[counter('', 0)] + # Defines the variables AzureFileCopySubscription, StorageAccount, StorageAccountKey, StorageResourceGroup, StorageSubscriptionName + - group: 'Azure Blob variable group' + # Defines the variables CgPat, CgOrganization, and CgProject + - group: 'ComponentGovernance' + - group: 'PoolNames' + - name: LinuxContainerImage + value: mcr.microsoft.com/onebranch/azurelinux/build:3.0 + - name: WindowsContainerImage + value: onebranch.azurecr.io/windows/ltsc2022/vse2022:latest + - ${{ if eq(parameters['FORCE_CODEQL'],'true') }}: + # Cadence is hours before CodeQL will allow a re-upload of the database + - name: CodeQL.Cadence + value: 0 + - name: CODEQL_ENABLED + ${{ if or(eq(variables['Build.SourceBranch'], 'refs/heads/master'), eq(parameters['FORCE_CODEQL'],'true')) }}: + value: true + ${{ else }}: + value: false + - name: Codeql.TSAEnabled + value: $(CODEQL_ENABLED) + # AnalyzeInPipeline: false = upload results + # AnalyzeInPipeline: true = do not upload results + - name: Codeql.AnalyzeInPipeline + ${{ if or(eq(variables['Build.SourceBranch'], 'refs/heads/master'), eq(parameters['FORCE_CODEQL'],'true')) }}: + value: false + ${{ else }}: + value: true + +resources: + repositories: + - repository: templates + type: git + name: OneBranch.Pipelines/GovernedTemplates + ref: refs/heads/main + +extends: + template: v2/OneBranch.NonOfficial.CrossPlat.yml@templates + parameters: + featureFlags: + WindowsHostVersion: + Version: 2022 + globalSdl: + codeql: + compiled: + enabled: $(CODEQL_ENABLED) + tsaEnabled: $(CODEQL_ENABLED) # This enables TSA bug filing only for CodeQL 3000 + armory: + enabled: false + sbom: + enabled: false + cg: + enabled: true + ignoreDirectories: '.devcontainer,demos,docker,docs,src,test,tools/packaging' + tsa: + enabled: true # onebranch publish all SDL results to TSA. If TSA is disabled all SDL tools will forced into 'break' build mode. + credscan: + enabled: true + scanFolder: $(Build.SourcesDirectory) + suppressionsFile: $(Build.SourcesDirectory)\.config\suppress.json + binskim: + break: true # always break the build on binskim issues in addition to TSA upload + policheck: + break: true # always break the build on policheck issues. You can disable it by setting to 'false' + # APIScan requires a non-Ready-To-Run build + apiscan: + enabled: true + softwareName: "PowerShell" # Default is repo name + versionNumber: "7.6" # Default is build number + isLargeApp: false # Default: false. + symbolsFolder: $(SymbolsServerUrl);$(ob_outputDirectory) +#softwareFolder - relative path to a folder to be scanned. Default value is root of artifacts folder + tsaOptionsFile: .config\tsaoptions.json + psscriptanalyzer: + enabled: true + policyName: Microsoft + break: false + + stages: + - stage: APIScan + displayName: 'ApiScan' + dependsOn: [] + jobs: + - template: /.pipelines/templates/compliance/apiscan.yml@self + parameters: + parentJobs: [] + - stage: notice + displayName: Generate Notice File + dependsOn: [] + jobs: + - template: /.pipelines/templates/compliance/generateNotice.yml@self + parameters: + parentJobs: [] + SkipVerifyPackages: ${{ parameters.SkipVerifyPackages }} diff --git a/.pipelines/store/PDP/PDP-Media/en-US/Error.png b/.pipelines/store/PDP/PDP-Media/en-US/Error.png new file mode 100644 index 00000000000..48e96378055 Binary files /dev/null and b/.pipelines/store/PDP/PDP-Media/en-US/Error.png differ diff --git a/.pipelines/store/PDP/PDP-Media/en-US/Experimental_Features.png b/.pipelines/store/PDP/PDP-Media/en-US/Experimental_Features.png new file mode 100644 index 00000000000..90420254a8e Binary files /dev/null and b/.pipelines/store/PDP/PDP-Media/en-US/Experimental_Features.png differ diff --git a/.pipelines/store/PDP/PDP-Media/en-US/Feedback_Provider.png b/.pipelines/store/PDP/PDP-Media/en-US/Feedback_Provider.png new file mode 100644 index 00000000000..f4084360d5c Binary files /dev/null and b/.pipelines/store/PDP/PDP-Media/en-US/Feedback_Provider.png differ diff --git a/.pipelines/store/PDP/PDP-Media/en-US/Predictor_Inline.png b/.pipelines/store/PDP/PDP-Media/en-US/Predictor_Inline.png new file mode 100644 index 00000000000..3b8d6228485 Binary files /dev/null and b/.pipelines/store/PDP/PDP-Media/en-US/Predictor_Inline.png differ diff --git a/.pipelines/store/PDP/PDP-Media/en-US/Predictor_ListView.png b/.pipelines/store/PDP/PDP-Media/en-US/Predictor_ListView.png new file mode 100644 index 00000000000..1fb9a6247c5 Binary files /dev/null and b/.pipelines/store/PDP/PDP-Media/en-US/Predictor_ListView.png differ diff --git a/.pipelines/store/PDP/PDP-Media/en-US/Prompt.png b/.pipelines/store/PDP/PDP-Media/en-US/Prompt.png new file mode 100644 index 00000000000..a40d6fddfdc Binary files /dev/null and b/.pipelines/store/PDP/PDP-Media/en-US/Prompt.png differ diff --git a/.pipelines/store/PDP/PDP-Media/en-US/Stable_Release.png b/.pipelines/store/PDP/PDP-Media/en-US/Stable_Release.png new file mode 100644 index 00000000000..2761a46a64f Binary files /dev/null and b/.pipelines/store/PDP/PDP-Media/en-US/Stable_Release.png differ diff --git a/.pipelines/store/PDP/PDP-Media/en-US/pwshLogo.png b/.pipelines/store/PDP/PDP-Media/en-US/pwshLogo.png new file mode 100644 index 00000000000..c531f719c85 Binary files /dev/null and b/.pipelines/store/PDP/PDP-Media/en-US/pwshLogo.png differ diff --git a/.pipelines/store/PDP/PDP/en-US/PDP.xml b/.pipelines/store/PDP/PDP/en-US/PDP.xml new file mode 100644 index 00000000000..15d0bdf5270 --- /dev/null +++ b/.pipelines/store/PDP/PDP/en-US/PDP.xml @@ -0,0 +1,176 @@ + + + + + + + + + + + + + Shell + + PowerShell + + Terminal + + Command Line + + Automation + + Task Automation + + Scripting + + + PowerShell is a task-based command-line shell and scripting language built on .NET. PowerShell helps system administrators and power-users rapidly automate task that manage operating systems (Linux, macOS, and Windows) and processes. + +PowerShell commands let you manage computers from the command line. PowerShell providers let you access data stores, such as the registry and certificate store, as easily as you access the file system. PowerShell includes a rich expression parser and a fully developed scripting language. + +PowerShell is Open Source. See https://github.com/powershell/powershell + + + + + + + + + + + + + + + + + + + + + + Please see our GitHub releases page for additional details. + + + + + + Prompt + + + + Inline Prediction + + + + Prediction List View + + + + Error Feedback Provider + + + + Feedback Provider + + + + Experimental Features + + + + + + + + + + + + + + + + + + + Interactive Shell + + Scripting Language + + Remote Management + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Microsoft Corporation + + + + + https://github.com/PowerShell/PowerShell + + https://github.com/PowerShell/PowerShell/issues + + https://go.microsoft.com/fwlink/?LinkID=521839 + diff --git a/.pipelines/store/SBConfig.json b/.pipelines/store/SBConfig.json new file mode 100644 index 00000000000..a52d60b045f --- /dev/null +++ b/.pipelines/store/SBConfig.json @@ -0,0 +1,69 @@ +{ + "helpUri": "https:\\\\aka.ms\\StoreBroker_Config", + "schemaVersion": 2, + "packageParameters": { + "PDPRootPath": "", + "Release": "", + "PDPInclude": [ + "PDP.xml" + ], + "PDPExclude": [], + "LanguageExclude": [ + "default", + "qps-ploc", + "qps-ploca", + "qps-plocm" + ], + "MediaRootPath": "", + "MediaFallbackLanguage": "en-US", + "PackagePath": [], + "OutPath": "", + "OutName": "", + "DisableAutoPackageNameFormatting": false + }, + "appSubmission": { + "productId": "", + "targetPublishMode": "Immediate", + "targetPublishDate": null, + "visibility": "NotSet", + "pricing": { + "priceId": "NotAvailable", + "trialPeriod": "NoFreeTrial", + "marketSpecificPricings": {}, + "sales": [] + }, + "allowTargetFutureDeviceFamilies": { + "Xbox": false, + "Team": false, + "Holographic": false, + "Desktop": false, + "Mobile": false + }, + "allowMicrosoftDecideAppAvailabilityToFutureDeviceFamilies": false, + "enterpriseLicensing": "None", + "applicationCategory": "NotSet", + "hardwarePreferences": [], + "hasExternalInAppProducts": false, + "meetAccessibilityGuidelines": false, + "canInstallOnRemovableMedia": false, + "automaticBackupEnabled": false, + "isGameDvrEnabled": false, + "gamingOptions": [ + { + "genres": [], + "isLocalMultiplayer": false, + "isLocalCooperative": false, + "isOnlineMultiplayer": false, + "isOnlineCooperative": false, + "localMultiplayerMinPlayers": 0, + "localMultiplayerMaxPlayers": 0, + "localCooperativeMinPlayers": 0, + "localCooperativeMaxPlayers": 0, + "isBroadcastingPrivilegeGranted": false, + "isCrossPlayEnabled": false, + "kinectDataForExternal": "Disabled" + } + ], + "notesForCertification": "" + } +} diff --git a/.pipelines/templates/SetVersionVariables.yml b/.pipelines/templates/SetVersionVariables.yml new file mode 100644 index 00000000000..30ed1704022 --- /dev/null +++ b/.pipelines/templates/SetVersionVariables.yml @@ -0,0 +1,48 @@ +parameters: +- name: ReleaseTagVar + default: v6.2.0 +- name: ReleaseTagVarName + default: ReleaseTagVar +- name: CreateJson + default: 'no' +- name: ob_restore_phase + type: boolean + default: true + +steps: +- template: set-reporoot.yml@self + parameters: + ob_restore_phase: ${{ parameters.ob_restore_phase }} + +- powershell: | + $createJson = ("${{ parameters.CreateJson }}" -ne "no") + + $REPOROOT = $env:REPOROOT + + if (-not (Test-Path $REPOROOT/tools/releaseBuild/setReleaseTag.ps1)) { + if (Test-Path "$REPOROOT/PowerShell/tools/releaseBuild/setReleaseTag.ps1") { + $REPOROOT = "$REPOROOT/PowerShell" + } else { + throw "Could not find setReleaseTag.ps1 in $REPOROOT/tools/releaseBuild or $REPOROOT/PowerShell/tools/releaseBuild" + } + } + + $releaseTag = & "$REPOROOT/tools/releaseBuild/setReleaseTag.ps1" -ReleaseTag ${{ parameters.ReleaseTagVar }} -Variable "${{ parameters.ReleaseTagVarName }}" -CreateJson:$createJson + $version = $releaseTag.Substring(1) + $vstsCommandString = "vso[task.setvariable variable=Version]$version" + Write-Host ("sending " + $vstsCommandString) + Write-Host "##$vstsCommandString" + $azureVersion = $releaseTag.ToLowerInvariant() -replace '\.', '-' + $vstsCommandString = "vso[task.setvariable variable=AzureVersion]$azureVersion" + Write-Host ("sending " + $vstsCommandString) + Write-Host "##$vstsCommandString" + displayName: 'Set ${{ parameters.ReleaseTagVarName }} and other version Variables' + env: + ob_restore_phase: ${{ parameters.ob_restore_phase }} + +- powershell: | + Get-ChildItem -Path Env: | Out-String -Width 150 + displayName: Capture environment + condition: succeededOrFailed() + env: + ob_restore_phase: ${{ parameters.ob_restore_phase }} diff --git a/.pipelines/templates/approvalJob.yml b/.pipelines/templates/approvalJob.yml new file mode 100644 index 00000000000..ac3b8bc2ab2 --- /dev/null +++ b/.pipelines/templates/approvalJob.yml @@ -0,0 +1,36 @@ +parameters: + - name: displayName + type: string + - name: instructions + type: string + - name: jobName + type: string + default: approval + - name: timeoutInMinutes + type: number + # 2 days + default: 2880 + - name: onTimeout + type: string + default: 'reject' + values: + - resume + - reject + - name: dependsOnJob + type: string + default: '' + +jobs: + - job: ${{ parameters.jobName }} + dependsOn: ${{ parameters.dependsOnJob }} + displayName: ${{ parameters.displayName }} + pool: + type: agentless + timeoutInMinutes: 4320 # job times out in 3 days + steps: + - task: ManualValidation@0 + displayName: ${{ parameters.displayName }} + timeoutInMinutes: ${{ parameters.timeoutInMinutes }} + inputs: + instructions: ${{ parameters.instructions }} + onTimeout: ${{ parameters.onTimeout }} diff --git a/.pipelines/templates/channelSelection.yml b/.pipelines/templates/channelSelection.yml new file mode 100644 index 00000000000..9dd0f3fb216 --- /dev/null +++ b/.pipelines/templates/channelSelection.yml @@ -0,0 +1,49 @@ +steps: +- pwsh: | + # Determine LTS, Preview, or Stable + $metadata = Get-Content "$(Build.SourcesDirectory)/PowerShell/tools/metadata.json" -Raw | ConvertFrom-Json + $releaseTag = '$(OutputReleaseTag.releaseTag)' + + # Rebuild branches should be treated as preview builds + # NOTE: The following regex is duplicated from rebuild-branch-check.yml. + # This duplication is necessary because channelSelection.yml does not call rebuild-branch-check.yml, + # and is used in contexts where that check may not have run. + # If you update this regex, also update it in rebuild-branch-check.yml to keep them in sync. + $isRebuildBranch = '$(Build.SourceBranch)' -match 'refs/heads/rebuild/.*-rebuild\.' + + $LTS = $metadata.LTSRelease.Latest + $Stable = $metadata.StableRelease.Latest + $isPreview = $releaseTag -match '-' + + # If this is a rebuild branch, force preview mode and ignore LTS metadata + if ($isRebuildBranch) { + $IsLTS = $false + $IsStable = $false + $IsPreview = $true + Write-Verbose -Message "Rebuild branch detected, forcing Preview channel" -Verbose + } + else { + $IsLTS = [bool]$LTS + $IsStable = [bool]$Stable + $IsPreview = [bool]$isPreview + } + + $channelVars = @{ + IsLTS = $IsLTS + IsStable = $IsStable + IsPreview = $IsPreview + } + + $trueCount = ($channelVars.Values | Where-Object { $_ }) | Measure-Object | Select-Object -ExpandProperty Count + if ($trueCount -gt 1) { + Write-Error "Only one of IsLTS, IsStable, or IsPreview can be true. Current values: IsLTS=$IsLTS, IsStable=$IsStable, IsPreview=$IsPreview" + exit 1 + } + + foreach ($name in $channelVars.Keys) { + $value = if ($channelVars[$name]) { 'true' } else { 'false' } + Write-Verbose -Message "Setting $name variable: $value" -Verbose + Write-Host "##vso[task.setvariable variable=$name;isOutput=true]$value" + } + name: ChannelSelection + displayName: Select Preview, Stable, or LTS Channel diff --git a/.pipelines/templates/checkAzureContainer.yml b/.pipelines/templates/checkAzureContainer.yml new file mode 100644 index 00000000000..3e383d2c572 --- /dev/null +++ b/.pipelines/templates/checkAzureContainer.yml @@ -0,0 +1,86 @@ +jobs: +- job: DeleteBlob + variables: + - group: Azure Blob variable group + - group: AzureBlobServiceConnection + - name: ob_artifactBaseName + value: BuildInfoJson + - name: ob_outputDirectory + value: '$(Build.ArtifactStagingDirectory)/ONEBRANCH_ARTIFACT/BuildJson' + - name: ob_sdl_sbom_enabled + value: false + - name: ob_sdl_codeSignValidation_enabled + value: false + - name: ob_sdl_tsa_configFile + value: $(Build.SourcesDirectory)\PowerShell\.config\tsaoptions.json + - name: ob_sdl_credscan_suppressionsFile + value: $(Build.SourcesDirectory)\PowerShell\.config\suppress.json + - name: ob_sdl_codeql_compiled_enabled + value: false + + displayName: Delete blob is exists + pool: + type: windows + steps: + - checkout: self + clean: true + env: + ob_restore_phase: true # This ensures checkout is done at the beginning of the restore phase + + - template: /.pipelines/templates/SetVersionVariables.yml@self + parameters: + ReleaseTagVar: $(ReleaseTagVar) + CreateJson: yes + + - template: /.pipelines/templates/cloneToOfficialPath.yml@self + + - template: /.pipelines/templates/insert-nuget-config-azfeed.yml@self + parameters: + repoRoot: $(PowerShellRoot) + + - pwsh: | + if (-not (Test-Path -Path $(Build.SourcesDirectory)\PowerShell\.config\tsaoptions.json)) { + Get-ChildItem -Path $(Build.SourcesDirectory) -Recurse + throw 'tsaoptions.json not found' + } + displayName: 'Check tsaoptions.json' + + - pwsh: | + if (-not (Test-Path -Path $(Build.SourcesDirectory)\PowerShell\.config\suppress.json)) { + Get-ChildItem -Path $(Build.SourcesDirectory) -Recurse + throw 'suppress.json not found' + } + displayName: 'Check suppress.json' + + - task: AzurePowerShell@5 + displayName: Check if blob exists and delete if specified + inputs: + azureSubscription: az-blob-cicd-infra + scriptType: inlineScript + azurePowerShellVersion: LatestVersion + pwsh: true + inline: | + $containersToDelete = @('$(AzureVersion)', '$(AzureVersion)-private', '$(AzureVersion)-nuget', '$(AzureVersion)-gc') + + $containersToDelete | ForEach-Object { + $containerName = $_ + try { + $container = Get-AzStorageContainer -Container $containerName -Context (New-AzStorageContext -StorageAccountName '$(StorageAccount)') -ErrorAction Stop + if ($container -ne $null -and '$(ForceAzureBlobDelete)' -eq 'false') { + throw "Azure blob container $containerName already exists. To overwrite, use ForceAzureBlobDelete parameter" + } + elseif ($container -ne $null -and '$(ForceAzureBlobDelete)' -eq 'true') { + Write-Verbose -Verbose "Removing container $containerName due to ForceAzureBlobDelete parameter" + Remove-AzStorageContainer -Name $containerName -Context (New-AzStorageContext -StorageAccountName '$(StorageAccount)') -Force + } + } + catch { + if ($_.FullyQualifiedErrorId -eq 'ResourceNotFoundException,Microsoft.WindowsAzure.Commands.Storage.Blob.Cmdlet.GetAzureStorageContainerCommand') { + Write-Verbose -Verbose "Container $containerName does not exists." + } + else { + throw $_ + } + } + } + - template: /.pipelines/templates/step/finalize.yml@self diff --git a/.pipelines/templates/cloneToOfficialPath.yml b/.pipelines/templates/cloneToOfficialPath.yml new file mode 100644 index 00000000000..b060c713683 --- /dev/null +++ b/.pipelines/templates/cloneToOfficialPath.yml @@ -0,0 +1,31 @@ +parameters: +- name: nativePathRoot + default: '' +- name: ob_restore_phase + type: boolean + default: true + +steps: +- powershell: | + $dirSeparatorChar = [system.io.path]::DirectorySeparatorChar + $nativePath = "${{parameters.nativePathRoot }}${dirSeparatorChar}PowerShell" + Write-Host "##vso[task.setvariable variable=PowerShellRoot]$nativePath" + if ((Test-Path "$nativePath")) { + Remove-Item -Path "$nativePath" -Force -Recurse -Verbose -ErrorAction ignore + } + else { + Write-Verbose -Verbose -Message "No cleanup required." + } + # REPOROOT must be set by the pipeline - this is where the repository was checked out + $sourceDir = $env:REPOROOT + if (-not $sourceDir) { throw "REPOROOT environment variable is not set. This step depends on REPOROOT being configured in the pipeline." } + + $buildModulePath = Join-Path $sourceDir "build.psm1" + if (-not (Test-Path $buildModulePath)) { throw "build.psm1 not found at: $buildModulePath. REPOROOT must point to the PowerShell repository root." } + + Write-Verbose -Verbose -Message "Cloning from: $sourceDir to $nativePath" + git clone --quiet $sourceDir $nativePath + displayName: Clone PowerShell Repo to /PowerShell + errorActionPreference: silentlycontinue + env: + ob_restore_phase: ${{ parameters.ob_restore_phase }} diff --git a/.pipelines/templates/compliance/apiscan.yml b/.pipelines/templates/compliance/apiscan.yml new file mode 100644 index 00000000000..5809af8e28c --- /dev/null +++ b/.pipelines/templates/compliance/apiscan.yml @@ -0,0 +1,172 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +jobs: + - job: APIScan + variables: + - name: runCodesignValidationInjection + value : false + - name: NugetSecurityAnalysisWarningLevel + value: none + - name: ReleaseTagVar + value: fromBranch + # Defines the variables APIScanClient, APIScanTenant and APIScanSecret + - group: PS-PS-APIScan + - name: branchCounterKey + value: $[format('{0:yyyyMMdd}-{1}', pipeline.startTime,variables['Build.SourceBranch'])] + - name: branchCounter + value: $[counter(variables['branchCounterKey'], 1)] + - group: DotNetPrivateBuildAccess + - group: ReleasePipelineSecrets + - group: mscodehub-feed-read-general + - group: mscodehub-feed-read-akv + - name: ob_outputDirectory + value: '$(Build.ArtifactStagingDirectory)/ONEBRANCH_ARTIFACT' + - name: repoRoot + value: '$(Build.SourcesDirectory)\PowerShell' + - name: ob_sdl_tsa_configFile + value: $(Build.SourcesDirectory)\PowerShell\.config\tsaoptions.json + - name: ob_sdl_credscan_suppressionsFile + value: $(Build.SourcesDirectory)\PowerShell\.config\suppress.json + - name: Codeql.SourceRoot + value: $(repoRoot) + + pool: + type: windows + + # APIScan can take a long time + timeoutInMinutes: 180 + + steps: + - checkout: self + clean: true + fetchTags: true + fetchDepth: 1000 + displayName: Checkout PowerShell + retryCountOnTaskFailure: 1 + env: + ob_restore_phase: true # This ensures checkout is done at the beginning of the restore phase + + - template: ../SetVersionVariables.yml + parameters: + ReleaseTagVar: $(ReleaseTagVar) + CreateJson: no + + - template: ../insert-nuget-config-azfeed.yml + parameters: + repoRoot: '$(repoRoot)' + + - task: UseDotNet@2 + displayName: 'Use .NET Core sdk' + inputs: + useGlobalJson: true + packageType: 'sdk' + workingDirectory: $(Build.SourcesDirectory)" + + - pwsh: | + Import-Module .\build.psm1 -force + Find-DotNet + dotnet tool install dotnet-symbol --tool-path $(Agent.ToolsDirectory)\tools\dotnet-symbol + $symbolToolPath = Get-ChildItem -Path $(Agent.ToolsDirectory)\tools\dotnet-symbol\dotnet-symbol.exe | Select-Object -First 1 -ExpandProperty FullName + Write-Host "##vso[task.setvariable variable=symbolToolPath]$symbolToolPath" + displayName: Install dotnet-symbol + workingDirectory: '$(repoRoot)' + retryCountOnTaskFailure: 2 + + - task: CodeQL3000Init@0 # Add CodeQL Init task right before your 'Build' step. + displayName: 🔏 CodeQL 3000 Init + condition: eq(variables['CODEQL_ENABLED'], 'true') + inputs: + Language: csharp + + - pwsh: | + Import-Module .\build.psm1 -force + Find-DotNet + Start-PSBuild -Configuration StaticAnalysis -PSModuleRestore -Clean -Runtime fxdependent-win-desktop + + $OutputFolder = Split-Path (Get-PSOutput) + + Write-Verbose -Verbose -Message "Deleting ref folder from output folder" + if (Test-Path $OutputFolder/ref) { + Remove-Item -Recurse -Force $OutputFolder/ref + } + + $Destination = '$(ob_outputDirectory)' + if (-not (Test-Path $Destination)) { + Write-Verbose -Verbose -Message "Creating destination folder '$Destination'" + $null = mkdir $Destination + } + + Copy-Item -Path "$OutputFolder\*" -Destination $Destination -Recurse -Verbose + workingDirectory: '$(repoRoot)' + displayName: 'Build PowerShell Source' + + - pwsh: | + # Only keep windows runtimes + Write-Verbose -Verbose -Message "Deleting non-win-x64 runtimes ..." + Get-ChildItem -Path '$(ob_outputDirectory)\runtimes\*' | Where-Object {$_.FullName -notmatch '.*\\runtimes\\win'} | Foreach-Object { + Write-Verbose -Verbose -Message "Deleting $($_.FullName)" + Remove-Item -Path $_.FullName -Recurse -Force + } + + # Remove win-x86/arm/arm64 runtimes due to issues with those runtimes + Write-Verbose -Verbose -Message "Temporarily deleting win-x86/arm/arm64 runtimes ..." + Get-ChildItem -Path '$(ob_outputDirectory)\runtimes\*' | Where-Object {$_.FullName -match '.*\\runtimes\\win-(x86|arm)'} | Foreach-Object { + Write-Verbose -Verbose -Message "Deleting $($_.FullName)" + Remove-Item -Path $_.FullName -Recurse -Force + } + + Write-Host + Write-Verbose -Verbose -Message "Show content in 'runtimes' folder:" + Get-ChildItem -Path '$(ob_outputDirectory)\runtimes' + Write-Host + workingDirectory: '$(repoRoot)' + displayName: 'Remove unused runtimes' + + - task: CodeQL3000Finalize@0 # Add CodeQL Finalize task right after your 'Build' step. + displayName: 🔏 CodeQL 3000 Finalize + condition: eq(variables['CODEQL_ENABLED'], 'true') + + - pwsh: | + Get-ChildItem -Path env: | Out-String -width 9999 -Stream | write-Verbose -Verbose + workingDirectory: '$(repoRoot)' + displayName: Capture Environment + condition: succeededOrFailed() + + # Explicitly download symbols for the drop since the SDL image doesn't have http://SymWeb access and APIScan cannot handle https yet. + - pwsh: | + Import-Module .\build.psm1 -force + Find-DotNet + $pat = '$(SymbolServerPAT)' + if ($pat -like '*PAT*' -or $pat -eq '') + { + throw 'No PAT defined' + } + $url = 'https://microsoft.artifacts.visualstudio.com/defaultcollection/_apis/symbol/symsrv' + $(symbolToolPath) --authenticated-server-path $(SymbolServerPAT) $url --symbols -d "$env:ob_outputDirectory\*" --recurse-subdirectories + displayName: 'Download Symbols for binaries' + retryCountOnTaskFailure: 2 + workingDirectory: '$(repoRoot)' + + - pwsh: | + Get-ChildItem '$(ob_outputDirectory)' -File -Recurse | + Foreach-Object { + [pscustomobject]@{ + Path = $_.FullName + Version = $_.VersionInfo.FileVersion + Md5Hash = (Get-FileHash -Algorithm MD5 -Path $_.FullName).Hash + Sha512Hash = (Get-FileHash -Algorithm SHA512 -Path $_.FullName).Hash + } + } | Export-Csv -Path '$(Build.SourcesDirectory)/ReleaseFileHash.csv' + workingDirectory: '$(repoRoot)' + displayName: 'Create release file hash artifact' + + - pwsh: | + Copy-Item -Path '$(Build.SourcesDirectory)/ReleaseFileHash.csv' -Destination '$(ob_outputDirectory)' -Verbose + displayName: 'Publish Build File Hash artifact' + + - pwsh: | + Get-ChildItem -Path env: | Out-String -width 9999 -Stream | write-Verbose -Verbose + displayName: Capture Environment + condition: succeededOrFailed() + workingDirectory: '$(repoRoot)' diff --git a/.pipelines/templates/compliance/generateNotice.yml b/.pipelines/templates/compliance/generateNotice.yml new file mode 100644 index 00000000000..7de316e8b49 --- /dev/null +++ b/.pipelines/templates/compliance/generateNotice.yml @@ -0,0 +1,121 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +parameters: + - name: parentJobs + type: jobList + - name: SkipVerifyPackages + type: boolean + +jobs: +- job: generateNotice + variables: + - name: runCodesignValidationInjection + value : false + - name: NugetSecurityAnalysisWarningLevel + value: none + - name: ob_outputDirectory + value: '$(Build.ArtifactStagingDirectory)/ONEBRANCH_ARTIFACT/notice' + - name: ob_sdl_apiscan_enabled + value: false + - name: repoRoot + value: '$(Build.SourcesDirectory)\PowerShell' + - name: ob_sdl_tsa_configFile + value: $(Build.SourcesDirectory)\PowerShell\.config\tsaoptions.json + - name: ob_sdl_credscan_suppressionsFile + value: $(Build.SourcesDirectory)\PowerShell\.config\suppress.json + + displayName: Generate Notice + dependsOn: + ${{ parameters.parentJobs }} + pool: + type: windows + + timeoutInMinutes: 15 + + steps: + - checkout: self + clean: true + + - pwsh: | + [string]$Branch=$env:BUILD_SOURCEBRANCH + $branchOnly = $Branch -replace '^refs/heads/'; + $branchOnly = $branchOnly -replace '[_\-]' + + if ($branchOnly -eq 'master') { + $container = 'tpn' + } else { + $branchOnly = $branchOnly -replace '[\./]', '-' + $container = "tpn-$branchOnly" + } + + $vstsCommandString = "vso[task.setvariable variable=tpnContainer]$container" + Write-Verbose -Message $vstsCommandString -Verbose + Write-Host -Object "##$vstsCommandString" + displayName: Set ContainerName + + - task: ms.vss-governance-buildtask.governance-build-task-component-detection.ComponentGovernanceComponentDetection@0 + displayName: 'Component Detection' + inputs: + sourceScanPath: '$(repoRoot)\tools' + + - pwsh: | + $(repoRoot)/tools/clearlyDefined/ClearlyDefined.ps1 -TestAndHarvest + displayName: Verify that packages have license data + condition: eq(${{ parameters.SkipVerifyPackages }}, false) + + - task: msospo.ospo-extension.8d7f9abb-6896-461d-9e25-4f74ed65ddb2.notice@0 + displayName: 'NOTICE File Generator' + inputs: + outputfile: '$(ob_outputDirectory)\ThirdPartyNotices.txt' + # output format can be html or text + outputformat: text + # this isn't working + # additionaldata: $(Build.SourcesDirectory)\assets\additionalAttributions.txt + + - pwsh: | + Get-Content -Raw -Path $(repoRoot)\assets\additionalAttributions.txt | Out-File '$(ob_outputDirectory)\ThirdPartyNotices.txt' -Encoding utf8NoBOM -Force -Append + Get-Content -Raw -Path $(repoRoot)\assets\additionalAttributions.txt + displayName: Append Additional Attributions + continueOnError: true + + - pwsh: | + Get-Content -Raw -Path '$(ob_outputDirectory)\ThirdPartyNotices.txt' + displayName: Capture Notice + continueOnError: true + + - task: AzurePowerShell@5 + displayName: Upload Notice + inputs: + azureSubscription: az-blob-cicd-infra + scriptType: inlineScript + azurePowerShellVersion: LatestVersion + workingDirectory: '$(repoRoot)' + pwsh: true + inline: | + try { + $downloadsDirectory = '$(Build.ArtifactStagingDirectory)/downloads' + $uploadedDirectory = '$(Build.ArtifactStagingDirectory)/uploaded' + $storageAccountName = "pscoretestdata" + $containerName = '$(tpnContainer)' + $blobName = 'ThirdPartyNotices.txt' + $noticePath = "$(ob_outputDirectory)\$blobName" + + Write-Verbose -Verbose "creating context ($storageAccountName) ..." + $context = New-AzStorageContext -StorageAccountName $storageAccountName -UseConnectedAccount + + Write-Verbose -Verbose "checking if container ($containerName) exists ..." + $containerExists = Get-AzStorageContainer -Name $containerName -Context $context -ErrorAction SilentlyContinue + if (-not $containerExists) { + Write-Verbose -Verbose "Creating container ..." + $null = New-AzStorageContainer -Name $containerName -Context $context + Write-Verbose -Verbose "Blob container $containerName created successfully." + } + + Write-Verbose -Verbose "Setting blob ($blobName) content ($noticePath) ..." + $null = Set-AzStorageBlobContent -File $noticePath -Container $containerName -Blob $blobName -Context $context -confirm:$false -force + Write-Verbose -Verbose "Done" + } catch { + Get-Error + throw + } diff --git a/.pipelines/templates/insert-nuget-config-azfeed.yml b/.pipelines/templates/insert-nuget-config-azfeed.yml new file mode 100644 index 00000000000..20057440c00 --- /dev/null +++ b/.pipelines/templates/insert-nuget-config-azfeed.yml @@ -0,0 +1,53 @@ +parameters: +- name: "repoRoot" + default: $(REPOROOT) +- name: "ob_restore_phase" + type: boolean + default: true + +steps: +- task: NuGetAuthenticate@1 + displayName: Install Azure Artifacts Credential Provider + inputs: + forceReinstallCredentialProvider: true + +- pwsh: | + try { + $configPath = "${env:NugetConfigDir}/nuget.config" + Import-Module ${{ parameters.repoRoot }}/build.psm1 -Force + + Write-Verbose -Verbose "Running: Switch-PSNugetConfig -Source Private -UserName '$(AzDevopsFeedUserNameKVPAT)' -ClearTextPAT '$(powershellPackageReadPat)'" + Switch-PSNugetConfig -Source Private -UserName '$(AzDevopsFeedUserNameKVPAT)' -ClearTextPAT '$(powershellPackageReadPat)' + + if(-not (Test-Path $configPath)) + { + throw "nuget.config is not created" + } + } + catch { + Get-Error + throw + } + displayName: 'Switch to production Azure DevOps feed for all nuget.configs' + condition: and(succeededOrFailed(), ne(variables['UseAzDevOpsFeed'], '')) + env: + NugetConfigDir: ${{ parameters.repoRoot }}/src/Modules + ob_restore_phase: ${{ parameters.ob_restore_phase }} + +- pwsh: | + Get-ChildItem ${{ parameters.repoRoot }}/nuget.config -Recurse | Foreach-Object { + Write-Verbose -Verbose "--- START $($_.fullname) ---" + get-content $_.fullname | Out-String -width 9999 -Stream | write-Verbose -Verbose + Write-Verbose -Verbose "--- END $($_.fullname) ---" + } + displayName: 'Capture all nuget.config files' + condition: and(succeededOrFailed(), ne(variables['UseAzDevOpsFeed'], '')) + env: + ob_restore_phase: ${{ parameters.ob_restore_phase }} + +- pwsh: | + Get-ChildItem -Path env:VSS* | Out-String -width 9999 -Stream | write-Verbose -Verbose + displayName: Capture VSS* Environment + condition: and(succeededOrFailed(), ne(variables['UseAzDevOpsFeed'], '')) + env: + ob_restore_phase: ${{ parameters.ob_restore_phase }} diff --git a/.pipelines/templates/install-dotnet.yml b/.pipelines/templates/install-dotnet.yml new file mode 100644 index 00000000000..464e13d1047 --- /dev/null +++ b/.pipelines/templates/install-dotnet.yml @@ -0,0 +1,24 @@ +parameters: +- name: ob_restore_phase + type: boolean + default: true + +steps: + - pwsh: | + if (-not (Test-Path '$(RepoRoot)')) { + $psRoot = '$(Build.SourcesDirectory)/PowerShell' + Set-Location $psRoot -Verbose + } + + $version = Get-Content ./global.json | ConvertFrom-Json | Select-Object -ExpandProperty sdk | Select-Object -ExpandProperty version + + Write-Verbose -Verbose "Installing .NET SDK with version $version" + + Import-Module ./build.psm1 -Force + Install-Dotnet -Version $version -Verbose + + displayName: 'Install dotnet SDK' + workingDirectory: $(RepoRoot) + env: + ob_restore_phase: ${{ parameters.ob_restore_phase }} + diff --git a/.pipelines/templates/linux-package-build.yml b/.pipelines/templates/linux-package-build.yml new file mode 100644 index 00000000000..bcf332b3778 --- /dev/null +++ b/.pipelines/templates/linux-package-build.yml @@ -0,0 +1,202 @@ +parameters: + unsignedDrop: 'drop_linux_build_linux_x64' + signedeDrop: 'drop_linux_sign_linux_x64' + packageType: deb + jobName: 'deb' + signingProfile: 'CP-450779-pgpdetached' + +jobs: +- job: ${{ parameters.jobName }} + displayName: Package linux ${{ parameters.packageType }} + condition: succeeded() + pool: + type: linux + + variables: + - name: runCodesignValidationInjection + value: false + - name: nugetMultiFeedWarnLevel + value: none + - name: NugetSecurityAnalysisWarningLevel + value: none + - name: skipNugetSecurityAnalysis + value: true + - group: DotNetPrivateBuildAccess + - name: ob_outputDirectory + value: '$(Build.ArtifactStagingDirectory)/ONEBRANCH_ARTIFACT' + - name: ob_sdl_binskim_enabled + value: true + - name: PackageType + value: ${{ parameters.packageType }} + - name: signedDrop + value: ${{ parameters.signedDrop }} + - name: unsignedDrop + value: ${{ parameters.unsignedDrop }} + - name: ob_sdl_tsa_configFile + value: $(Build.SourcesDirectory)/PowerShell/.config/tsaoptions.json + - name: ob_sdl_credscan_suppressionsFile + value: $(Build.SourcesDirectory)/PowerShell/.config/suppress.json + - name: SigningProfile + value: ${{ parameters.signingProfile }} + + steps: + - checkout: self + clean: true + env: + ob_restore_phase: true # This ensures checkout is done at the beginning of the restore phase + + - pwsh: | + Get-ChildItem -Path env: | Out-String -width 9999 -Stream | write-Verbose -Verbose + displayName: Capture environment + env: + ob_restore_phase: true # This ensures this done in restore phase to workaround signing issue + + - template: SetVersionVariables.yml@self + parameters: + ReleaseTagVar: $(ReleaseTagVar) + CreateJson: no + + - template: shouldSign.yml + + - template: cloneToOfficialPath.yml + parameters: + nativePathRoot: '$(Agent.TempDirectory)' + + - template: rebuild-branch-check.yml@self + + - download: CoOrdinatedBuildPipeline + artifact: ${{ parameters.unsignedDrop }} + displayName: 'Download unsigned artifacts' + env: + ob_restore_phase: true # This ensures this done in restore phase to workaround signing issue + + - download: CoOrdinatedBuildPipeline + artifact: ${{ parameters.signedDrop }} + displayName: 'Download signed artifacts' + env: + ob_restore_phase: true # This ensures this done in restore phase to workaround signing issue + + - pwsh: | + Write-Verbose -Verbose "Unsigned artifacts" + Get-ChildItem "$(Pipeline.Workspace)/CoOrdinatedBuildPipeline/${{ parameters.unsignedDrop }}" -Recurse + + Write-Verbose -Verbose "Signed artifacts" + Get-ChildItem "$(Pipeline.Workspace)/CoOrdinatedBuildPipeline/${{ parameters.signedDrop }}" -Recurse + displayName: 'Capture Downloaded Artifacts' + # Diagnostics is not critical it passes every time it runs + continueOnError: true + env: + ob_restore_phase: true # This ensures this done in restore phase to workaround signing issue + + - template: /.pipelines/templates/install-dotnet.yml@self + + - pwsh: | + $packageType = '$(PackageType)' + Write-Verbose -Verbose "packageType = $packageType" + + $signedDrop = '$(signedDrop)' + Write-Verbose -Verbose "signedDrop = $signedDrop" + + $unsignedDrop = '$(unsignedDrop)' + Write-Verbose -Verbose "unsignedDrop = $unsignedDrop" + + Write-Verbose -Message "Init..." -Verbose + + $repoRoot = "$env:REPOROOT" + Import-Module "$repoRoot/build.psm1" + Import-Module "$repoRoot/tools/packaging" + + Start-PSBootstrap -Scenario Both + + $psOptionsPath = "$(Pipeline.Workspace)/CoOrdinatedBuildPipeline/${unsignedDrop}/psoptions/psoptions.json" + + if (-not (Test-Path $psOptionsPath)) { + throw "psOptionsPath file not found at $psOptionsPath" + } + + Restore-PSOptions $psOptionsPath + Write-Verbose -Message "Restoring PSOptions from $psoptionsFilePath" -Verbose + Get-PSOptions | Write-Verbose -Verbose + + $signedFolder, $pkgFilter = switch ($packageType) { + 'tar-arm' { 'Signed-linux-arm', 'powershell*.tar.gz' } + 'tar-arm64' { 'Signed-linux-arm64', 'powershell*.tar.gz' } + 'tar-alpine' { 'Signed-linux-musl-x64', 'powershell*.tar.gz' } + 'fxdependent' { 'Signed-fxdependent', 'powershell*.tar.gz' } + 'tar' { 'Signed-linux-x64', 'powershell*.tar.gz' } + 'tar-alpine-fxdependent' { 'Signed-fxdependent-noopt-linux-musl-x64', 'powershell*.tar.gz' } + 'deb' { 'Signed-linux-x64', 'powershell*.deb' } + 'rpm-fxdependent' { 'Signed-fxdependent-linux-x64', 'powershell*.rpm' } + 'rpm-fxdependent-arm64' { 'Signed-fxdependent-linux-arm64', 'powershell*.rpm' } + 'rpm' { 'Signed-linux-x64', 'powershell*.rpm' } + 'min-size' { 'Signed-linux-x64', 'powershell*.tar.gz' } + } + + $signedFilesPath = "$(Pipeline.Workspace)/CoOrdinatedBuildPipeline/${signedDrop}/${signedFolder}" + Write-Verbose -Verbose "signedFilesPath: $signedFilesPath" + + Write-Verbose -Message "checking pwsh exists in $signedFilesPath" -Verbose + if (-not (Test-Path "$signedFilesPath/pwsh")) { + throw "pwsh not found in $signedFilesPath" + } + + $metadata = Get-Content "$repoRoot/tools/metadata.json" -Raw | ConvertFrom-Json + + Write-Verbose -Verbose "metadata:" + $metadata | Out-String | Write-Verbose -Verbose + + # Use the rebuild branch check from the template + $isRebuildBranch = '$(RebuildBranchCheck.IsRebuildBranch)' -eq 'true' + + # Don't build LTS packages for rebuild branches + $LTS = $metadata.LTSRelease.Package -and -not $isRebuildBranch + + if ($isRebuildBranch) { + Write-Verbose -Message "Rebuild branch detected, skipping LTS package build" -Verbose + } + + Write-Verbose -Verbose "LTS: $LTS" + + if ($LTS) { + Write-Verbose -Message "LTS Release: $LTS" + } + + if (-not (Test-Path $(ob_outputDirectory))) { + New-Item -ItemType Directory -Path $(ob_outputDirectory) -Force + } + + $packageType = '$(PackageType)' + Write-Verbose -Verbose "packageType = $packageType" + + Start-PSPackage -Type $packageType -ReleaseTag $(ReleaseTagVar) -PackageBinPath $signedFilesPath + + $vstsCommandString = "vso[task.setvariable variable=PackageFilter]$pkgFilter" + Write-Host ("sending " + $vstsCommandString) + Write-Host "##$vstsCommandString" + displayName: 'Package ${{ parameters.packageType}}' + env: + __DOTNET_RUNTIME_FEED_KEY: $(RUNTIME_SOURCEFEED_KEY) + ob_restore_phase: true # This ensures this done in restore phase to workaround signing issue + + - task: onebranch.pipeline.signing@1 + displayName: Sign deb and rpm packages + inputs: + command: 'sign' + signing_profile: '$(SigningProfile)' + files_to_sign: '**/*.rpm;**/*.deb' + search_root: '$(Pipeline.Workspace)' + + - pwsh: | + $pkgFilter = '$(PackageFilter)' + Write-Verbose -Verbose "pkgFilter: $pkgFilter" + + $pkgPath = Get-ChildItem -Path $(Pipeline.Workspace) -Filter $pkgFilter -Recurse -File | Select-Object -ExpandProperty FullName + Write-Verbose -Verbose "pkgPath: $pkgPath" + Copy-Item -Path $pkgPath -Destination '$(ob_outputDirectory)' -Force -Verbose + displayName: 'Copy artifacts to output directory' + env: + __DOTNET_RUNTIME_FEED_KEY: $(RUNTIME_SOURCEFEED_KEY) + + - pwsh: | + Get-ChildItem -Path $(ob_outputDirectory) -Recurse + displayName: 'List artifacts' diff --git a/.pipelines/templates/linux.yml b/.pipelines/templates/linux.yml new file mode 100644 index 00000000000..aef0b53381f --- /dev/null +++ b/.pipelines/templates/linux.yml @@ -0,0 +1,201 @@ +parameters: + Runtime: 'linux-x64' + BuildConfiguration: 'release' + JobName: 'build_linux' + +jobs: +- job: build_${{ parameters.JobName }} + displayName: Build_Linux_${{ parameters.Runtime }}_${{ parameters.BuildConfiguration }} + condition: succeeded() + pool: + type: linux + variables: + - name: runCodesignValidationInjection + value: false + - name: NugetSecurityAnalysisWarningLevel + value: none + - name: DOTNET_NOLOGO + value: 1 + - group: DotNetPrivateBuildAccess + - name: ob_outputDirectory + value: '$(Build.ArtifactStagingDirectory)/ONEBRANCH_ARTIFACT' + - name: ob_sdl_codeSignValidation_enabled + value: false + - name: ob_sdl_binskim_enabled + value: true + - name: ob_sdl_tsa_configFile + value: $(Build.SourcesDirectory)\PowerShell\.config\tsaoptions.json + - name: ob_sdl_credscan_suppressionsFile + value: $(Build.SourcesDirectory)\PowerShell\.config\suppress.json + - name: BUILDCONFIGURATION + value: ${{ parameters.BuildConfiguration }} + - name: Runtime + value: ${{ parameters.Runtime }} + - name: ob_sdl_sbom_packageName + value: 'Microsoft.Powershell.Linux.${{ parameters.Runtime }}' + # We add this manually, so we need it disabled the OneBranch auto-injected one. + - name: ob_sdl_codeql_compiled_enabled + value: false + + steps: + - checkout: self + clean: true + env: + ob_restore_phase: true # This ensures checkout is done at the beginning of the restore phase + + - template: /.pipelines/templates/SetVersionVariables.yml@self + parameters: + ReleaseTagVar: $(ReleaseTagVar) + + - template: /.pipelines/templates/cloneToOfficialPath.yml@self + + - template: /.pipelines/templates/insert-nuget-config-azfeed.yml@self + parameters: + repoRoot: $(PowerShellRoot) + + - task: CodeQL3000Init@0 # Add CodeQL Init task right before your 'Build' step. + condition: eq(variables['CODEQL_ENABLED'], 'true') + env: + ob_restore_phase: true # Set ob_restore_phase to run this step before '🔒 Setup Signing' step. + inputs: + Enabled: true + # AnalyzeInPipeline: false = upload results + # AnalyzeInPipeline: true = do not upload results + AnalyzeInPipeline: false + Language: csharp + + - template: /.pipelines/templates/install-dotnet.yml@self + + - pwsh: | + $runtime = $env:RUNTIME + + $params = @{} + if ($env:BUILDCONFIGURATION -eq 'minSize') { + Write-Verbose -Message "Building for minimal size" + $params['ForMinimalSize'] = $true + } + + Write-Verbose -Message "Building PowerShell with Runtime: $runtime" + Import-Module -Name $(PowerShellRoot)/build.psm1 -Force + $buildWithSymbolsPath = New-Item -ItemType Directory -Path $(Pipeline.Workspace)/Symbols_$(Runtime) -Force + + $null = New-Item -ItemType Directory -Path $buildWithSymbolsPath -Force -Verbose + + $ReleaseTagParam = @{} + + if ($env:RELEASETAGVAR) { + $ReleaseTagParam['ReleaseTag'] = $env:RELEASETAGVAR + } + + Start-PSBuild -Runtime $runtime -Configuration Release -Output $buildWithSymbolsPath @params -Clean -PSModuleRestore @ReleaseTagParam + + $outputPath = Join-Path '$(ob_outputDirectory)' 'psoptions' + $null = New-Item -ItemType Directory -Path $outputPath -Force + $psOptPath = "$outputPath/psoptions.json" + Save-PSOptions -PSOptionsPath $psOptPath + + Write-Verbose -Verbose "Verifying pdbs exist in build folder" + $pdbs = Get-ChildItem -Path $buildWithSymbolsPath -Recurse -Filter *.pdb + if ($pdbs.Count -eq 0) { + Write-Error -Message "No pdbs found in build folder" + } + else { + Write-Verbose -Verbose "Found $($pdbs.Count) pdbs in build folder" + $pdbs | ForEach-Object { + Write-Verbose -Verbose "Pdb: $($_.FullName)" + } + } + + Write-Verbose -Verbose "Completed building PowerShell for '$env:BUILDCONFIGURATION' configuration" + displayName: 'Build Linux - $(Runtime)' + env: + __DOTNET_RUNTIME_FEED_KEY: $(RUNTIME_SOURCEFEED_KEY) + ob_restore_phase: true # Set ob_restore_phase to run this step before '🔒 Setup Signing' step. + + - task: CodeQL3000Finalize@0 # Add CodeQL Finalize task right after your 'Build' step. + condition: eq(variables['CODEQL_ENABLED'], 'true') + env: + ob_restore_phase: true # Set ob_restore_phase to run this step before '🔒 Setup Signing' step. + + - pwsh: | + $platform = 'linux' + $vstsCommandString = "vso[task.setvariable variable=ArtifactPlatform]$platform" + Write-Host ("sending " + $vstsCommandString) + Write-Host "##$vstsCommandString" + displayName: Set artifact platform + + - pwsh: | + $pathForUpload = New-Item -ItemType Directory -Path '$(ob_outputDirectory)/Unsigned-$(Runtime)' -Force + Write-Verbose -Verbose -Message "pathForUpload: $pathForUpload" + Copy-Item -Path '$(Pipeline.Workspace)/Symbols_$(Runtime)/*' -Destination $pathForUpload -Recurse -Force -Verbose + displayName: Copy unsigned files for upload + + - template: /.pipelines/templates/step/finalize.yml@self + +- job: sign_${{ parameters.JobName }} + displayName: Sign_Linux_${{ parameters.Runtime }}_${{ parameters.BuildConfiguration }} + condition: succeeded() + dependsOn: build_${{ parameters.JobName }} + pool: + type: windows + variables: + - name: runCodesignValidationInjection + value: false + - name: NugetSecurityAnalysisWarningLevel + value: none + - name: DOTNET_NOLOGO + value: 1 + - group: DotNetPrivateBuildAccess + - group: certificate_logical_to_actual + - name: ob_outputDirectory + value: '$(Build.ArtifactStagingDirectory)/ONEBRANCH_ARTIFACT' + - name: ob_sdl_codeSignValidation_enabled + value: false + - name: ob_sdl_binskim_enabled + value: false + - name: ob_sdl_tsa_configFile + value: $(Build.SourcesDirectory)\PowerShell\.config\tsaoptions.json + - name: ob_sdl_credscan_suppressionsFile + value: $(Build.SourcesDirectory)\PowerShell\.config\suppress.json + - name: BuildConfiguration + value: ${{ parameters.BuildConfiguration }} + - name: Runtime + value: ${{ parameters.Runtime }} + - name: ob_sdl_codeql_compiled_enabled + value: false + + steps: + - checkout: self + clean: true + env: + ob_restore_phase: true # This ensures checkout is done at the beginning of the restore phase + + - template: /.pipelines/templates/SetVersionVariables.yml@self + parameters: + ReleaseTagVar: $(ReleaseTagVar) + + - template: /.pipelines/templates/cloneToOfficialPath.yml@self + + - task: DownloadPipelineArtifact@2 + inputs: + artifact: drop_linux_build_${{ parameters.JobName }} + path: $(Pipeline.Workspace)/drop_linux_build + displayName: Download build + + - pwsh: | + Get-ChildItem -Path $(Pipeline.Workspace)/drop_linux_build -Recurse + displayName: Capture downloaded files + + - pwsh: | + $pwshPath = Get-ChildItem -Path $(Pipeline.Workspace)/drop_linux_build -File -Recurse | Where-Object { $_.Name -eq 'pwsh' } + $rootPath = Split-Path -Path $pwshPath.FullName -Parent + Write-Verbose -Verbose "Setting vso[task.setvariable variable=DropRootPath]$rootPath" + Write-Host "##vso[task.setvariable variable=DropRootPath]$rootPath" + displayName: Set drop root path + + - template: /.pipelines/templates/obp-file-signing.yml@self + parameters: + binPath: $(DropRootPath) + OfficialBuild: $(ps_official_build) + + - template: /.pipelines/templates/step/finalize.yml@self diff --git a/.pipelines/templates/mac-package-build.yml b/.pipelines/templates/mac-package-build.yml new file mode 100644 index 00000000000..4e7040d4acc --- /dev/null +++ b/.pipelines/templates/mac-package-build.yml @@ -0,0 +1,242 @@ +parameters: + parentJob: '' + buildArchitecture: x64 + +jobs: +- job: package_macOS_${{ parameters.buildArchitecture }} + displayName: Package macOS ${{ parameters.buildArchitecture }} + condition: succeeded() + pool: + type: linux + isCustom: true + name: Azure Pipelines + vmImage: 'macOS-latest' + + variables: + - name: HOMEBREW_NO_ANALYTICS + value: 1 + - name: runCodesignValidationInjection + value: false + - name: nugetMultiFeedWarnLevel + value: none + - name: NugetSecurityAnalysisWarningLevel + value: none + - name: skipNugetSecurityAnalysis + value: true + - group: DotNetPrivateBuildAccess + - name: ob_outputDirectory + value: '$(Build.ArtifactStagingDirectory)/ONEBRANCH_ARTIFACT' + - name: ob_sdl_binskim_enabled + value: true + - name: ob_sdl_credscan_suppressionsfileforartifacts + value: $(Build.SourcesDirectory)/PowerShell/.config/suppress.json + - name: BuildArch + value: ${{ parameters.buildArchitecture }} + + steps: + - checkout: self + clean: true + + - pwsh: | + Get-ChildItem -Path env: | Out-String -width 9999 -Stream | write-Verbose -Verbose + displayName: Capture environment + + - pwsh: | + # create folder + sudo mkdir "$(Agent.TempDirectory)/PowerShell" + + # make the current user the owner + sudo chown $env:USER "$(Agent.TempDirectory)/PowerShell" + displayName: 'Create $(Agent.TempDirectory)/PowerShell' + + - template: SetVersionVariables.yml@self + parameters: + ReleaseTagVar: $(ReleaseTagVar) + CreateJson: no + + - template: shouldSign.yml + + - template: cloneToOfficialPath.yml + parameters: + nativePathRoot: '$(Agent.TempDirectory)' + + - template: rebuild-branch-check.yml@self + + - download: CoOrdinatedBuildPipeline + artifact: macosBinResults-${{ parameters.buildArchitecture }} + + - download: CoOrdinatedBuildPipeline + artifact: drop_macos_sign_${{ parameters.buildArchitecture }} + + - pwsh: | + Write-Verbose -Verbose "unsigned artifacts" + Get-ChildItem "$(Pipeline.Workspace)/CoOrdinatedBuildPipeline/macosBinResults-${{ parameters.buildArchitecture }}" -Recurse + + Write-Verbose -Verbose "unsigned artifacts" + Get-ChildItem "$(Pipeline.Workspace)/CoOrdinatedBuildPipeline/drop_macos_sign_${{ parameters.buildArchitecture }}" -Recurse + displayName: 'Capture Downloaded Artifacts' + # Diagnostics is not critical it passes every time it runs + continueOnError: true + + - pwsh: | + # Add -SkipReleaseChecks as a mitigation to unblock release. + # macos-10.15 does not allow creating a folder under root. Hence, moving the folder. + + $buildArch = '${{ parameters.buildArchitecture }}' + + Write-Verbose -Message "Init..." -Verbose + $repoRoot = $env:REPOROOT + Set-Location $repoRoot + Import-Module "$repoRoot/build.psm1" + Import-Module "$repoRoot/tools/packaging" + + $unsignedFilesPath = "$(Pipeline.Workspace)/CoOrdinatedBuildPipeline/macosBinResults-$buildArch" + $signedFilesPath = "$(Pipeline.Workspace)/CoOrdinatedBuildPipeline/drop_macos_sign_$buildArch/Signed-$buildArch" + + Write-Verbose -Message "checking pwsh exists in $signedFilesPath" -Verbose + if (-not (Test-Path $signedFilesPath/pwsh)) { + throw "pwsh not found in $signedFilesPath" + } + + $psoptionsPath = Get-ChildItem -Path $unsignedFilesPath -Filter 'psoptions.json' -Recurse -File | Select-Object -ExpandProperty FullName + Write-Verbose -Message "Restoring PSOptions from $psoptionsPath" -Verbose + + Restore-PSOptions -PSOptionsPath "$psoptionsPath" + Get-PSOptions | Write-Verbose -Verbose + + $metadata = Get-Content "$repoRoot/tools/metadata.json" -Raw | ConvertFrom-Json + + Write-Verbose -Verbose "metadata:" + $metadata | Out-String | Write-Verbose -Verbose + + # Use the rebuild branch check from the template + $isRebuildBranch = '$(RebuildBranchCheck.IsRebuildBranch)' -eq 'true' + + # Don't build LTS packages for rebuild branches + $LTS = $metadata.LTSRelease.Package -and -not $isRebuildBranch + + if ($isRebuildBranch) { + Write-Verbose -Message "Rebuild branch detected, skipping LTS package build" -Verbose + } + + Write-Verbose -Verbose "LTS: $LTS" + + if ($LTS) { + Write-Verbose -Message "LTS Release: $LTS" + } + + Start-PSBootstrap -Scenario Package + + $macosRuntime = "osx-$buildArch" + + Start-PSPackage -Type osxpkg -SkipReleaseChecks -MacOSRuntime $macosRuntime -ReleaseTag $(ReleaseTagVar) -PackageBinPath $signedFilesPath -LTS:$LTS + $pkgNameFilter = "powershell-*$macosRuntime.pkg" + Write-Verbose -Verbose "Looking for pkg packages with filter: $pkgNameFilter in '$(Pipeline.Workspace)' to upload..." + $pkgPath = Get-ChildItem -Path $(Pipeline.Workspace) -Filter $pkgNameFilter -Recurse -File + + foreach($p in $pkgPath) { + $file = $p.FullName + Write-Verbose -verbose "Uploading $file to macos-pkgs" + Write-Host "##vso[artifact.upload containerfolder=macos-pkgs;artifactname=macos-pkgs]$file" + } + + Start-PSPackage -Type tar -SkipReleaseChecks -MacOSRuntime $macosRuntime -ReleaseTag $(ReleaseTagVar) -PackageBinPath $signedFilesPath -LTS:$LTS + $tarPkgNameFilter = "powershell-*$macosRuntime.tar.gz" + Write-Verbose -Verbose "Looking for tar packages with filter: $tarPkgNameFilter in '$(Pipeline.Workspace)' to upload..." + $tarPkgPath = Get-ChildItem -Path $(Pipeline.Workspace) -Filter $tarPkgNameFilter -Recurse -File + + foreach($t in $tarPkgPath) { + $file = $t.FullName + Write-Verbose -verbose "Uploading $file to macos-pkgs" + Write-Host "##vso[artifact.upload containerfolder=macos-pkgs;artifactname=macos-pkgs]$file" + } + + displayName: 'Package ${{ parameters.buildArchitecture}}' + env: + __DOTNET_RUNTIME_FEED_KEY: $(RUNTIME_SOURCEFEED_KEY) + +- job: sign_package_macOS_${{ parameters.buildArchitecture }} + displayName: Sign Package macOS ${{ parameters.buildArchitecture }} + dependsOn: package_macOS_${{ parameters.buildArchitecture }} + condition: succeeded() + pool: + type: windows + + variables: + - name: ob_outputDirectory + value: '$(Build.ArtifactStagingDirectory)/ONEBRANCH_ARTIFACT' + - name: ob_sdl_binskim_enabled + value: true + - name: ob_sdl_credscan_suppressionsfileforartifacts + value: $(Build.SourcesDirectory)/PowerShell/.config/suppress.json + - name: BuildArch + value: ${{ parameters.buildArchitecture }} + - group: mscodehub-macos-package-signing + + steps: + - download: current + artifact: macos-pkgs + + - pwsh: | + $buildArch = '${{ parameters.buildArchitecture }}' + $macosRuntime = "osx-$buildArch" + $pkgNameFilter = "powershell-*$macosRuntime.pkg" + $pkgPath = Get-ChildItem -Path $(Pipeline.Workspace) -Filter $pkgNameFilter -Recurse -File + + if ($pkgPath.Count -eq 0) { + throw "No package found for $macosRuntime" + } + + foreach($p in $pkgPath) { + $file = $p.FullName + $fileName = $p.BaseName + Write-Verbose -verbose "Compressing $file" + $zipFile = "$(Pipeline.Workspace)\${fileName}.zip" + Write-Verbose -Verbose "Zip file: $zipFile" + Compress-Archive -Path $file -Destination $zipFile + } + + Write-Verbose -Verbose "Compressed files:" + Get-ChildItem -Path $(Pipeline.Workspace) -Filter "*.zip" -File | Write-Verbose -Verbose + displayName: Compress package files for signing + + - task: onebranch.pipeline.signing@1 + displayName: 'OneBranch CodeSigning Package' + inputs: + command: 'sign' + files_to_sign: '**/*-osx-*.zip' + search_root: '$(Pipeline.Workspace)' + inline_operation: | + [ + { + "KeyCode": "$(KeyCode)", + "OperationCode": "MacAppDeveloperSign", + "ToolName": "sign", + "ToolVersion": "1.0", + "Parameters": { + "Hardening": "Enable", + "OpusInfo": "http://microsoft.com" + } + } + ] + + - pwsh: | + $signedPkg = Get-ChildItem -Path $(Pipeline.Workspace) -Filter "*osx*.zip" -File + + $signedPkg | ForEach-Object { + Write-Verbose -Verbose "Signed package zip: $_" + + if (-not (Test-Path $_)) { + throw "Package not found: $_" + } + + if (-not (Test-Path $(ob_outputDirectory))) { + $null = New-Item -Path $(ob_outputDirectory) -ItemType Directory + } + + Expand-Archive -Path $_ -DestinationPath $(ob_outputDirectory) -Verbose + } + + Write-Verbose -Verbose "Expanded pkg file:" + Get-ChildItem -Path $(ob_outputDirectory) | Write-Verbose -Verbose + displayName: Expand signed file diff --git a/.pipelines/templates/mac.yml b/.pipelines/templates/mac.yml new file mode 100644 index 00000000000..ff4787b9bcf --- /dev/null +++ b/.pipelines/templates/mac.yml @@ -0,0 +1,149 @@ +parameters: + buildArchitecture: 'x64' +jobs: +- job: build_macOS_${{ parameters.buildArchitecture }} + displayName: Build macOS ${{ parameters.buildArchitecture }} + condition: succeeded() + pool: + type: linux + isCustom: true + name: Azure Pipelines + vmImage: 'macOS-latest' + + variables: + - name: HOMEBREW_NO_ANALYTICS + value: 1 + - name: runCodesignValidationInjection + value: false + - name: NugetSecurityAnalysisWarningLevel + value: none + - group: DotNetPrivateBuildAccess + - name: ob_outputDirectory + value: '$(Build.ArtifactStagingDirectory)/ONEBRANCH_ARTIFACT' + - name: PowerShellRoot + value: $(Build.SourcesDirectory) + + steps: + - checkout: self + clean: true + env: + ob_restore_phase: true # This ensures checkout is done at the beginning of the restore phase + + - template: /.pipelines/templates/SetVersionVariables.yml@self + parameters: + ReleaseTagVar: $(ReleaseTagVar) + - pwsh: | + # create folder + sudo mkdir "$(Agent.TempDirectory)/PowerShell" + # make the current user the owner + sudo chown $env:USER "$(Agent.TempDirectory)/PowerShell" + displayName: 'Create $(Agent.TempDirectory)/PowerShell' + + ## We cross compile for arm64, so the arch is always x64 + - template: /.pipelines/templates/install-dotnet.yml@self + + - pwsh: | + Import-Module $(PowerShellRoot)/build.psm1 -Force + Start-PSBootstrap -Scenario Package + displayName: 'Bootstrap VM' + env: + __DOTNET_RUNTIME_FEED_KEY: $(RUNTIME_SOURCEFEED_KEY) + + - template: /.pipelines/templates/insert-nuget-config-azfeed.yml@self + parameters: + repoRoot: $(PowerShellRoot) + - pwsh: | + $env:AzDevOpsFeedPAT2 = '$(powershellPackageReadPat)' + # Add -SkipReleaseChecks as a mitigation to unblock release. + # macos-10.15 does not allow creating a folder under root. Hence, moving the folder. + + Import-Module ./build.psm1 -Force + + $ReleaseTagParam = @{} + + if ($env:RELEASETAGVAR) { + $ReleaseTagParam['ReleaseTag'] = $env:RELEASETAGVAR + } + + Start-PSBuild -Runtime 'osx-${{ parameters.buildArchitecture }}' -Configuration Release -PSModuleRestore -Clean -Output $(OB_OUTPUTDIRECTORY) @ReleaseTagParam + $artifactName = "macosBinResults-${{ parameters.buildArchitecture }}" + + $psOptPath = "$(OB_OUTPUTDIRECTORY)/psoptions.json" + Save-PSOptions -PSOptionsPath $psOptPath + + # Since we are using custom pool for macOS, we need to use artifact.upload to publish the artifacts + Write-Host "##vso[artifact.upload containerfolder=$artifactName;artifactname=$artifactName]$(OB_OUTPUTDIRECTORY)" + + $env:AzDevOpsFeedPAT2 = $null + displayName: 'Build' + env: + __DOTNET_RUNTIME_FEED_KEY: $(RUNTIME_SOURCEFEED_KEY) + + - template: /.pipelines/templates/step/finalize.yml@self + +- job: sign_${{ parameters.buildArchitecture }} + displayName: Sign_macOS_${{ parameters.buildArchitecture }} + condition: succeeded() + dependsOn: build_macOS_${{ parameters.buildArchitecture }} + pool: + type: windows + variables: + - name: NugetSecurityAnalysisWarningLevel + value: none + - group: DotNetPrivateBuildAccess + - group: certificate_logical_to_actual + - name: ob_outputDirectory + value: '$(Build.ArtifactStagingDirectory)/ONEBRANCH_ARTIFACT' + - name: ob_sdl_codeSignValidation_enabled + value: true + - name: ob_sdl_tsa_configFile + value: $(Build.SourcesDirectory)\PowerShell\.config\tsaoptions.json + - name: ob_sdl_credscan_suppressionsFile + value: $(Build.SourcesDirectory)\PowerShell\.config\suppress.json + - name: BuildArchitecture + value: ${{ parameters.buildArchitecture }} + - name: ob_sdl_codeql_compiled_enabled + value: false + - name: ob_sdl_sbom_packageName + value: 'Microsoft.Powershell.MacOS.${{parameters.buildArchitecture}}' + + steps: + - checkout: self + clean: true + env: + ob_restore_phase: true # This ensures checkout is done at the beginning of the restore phase + + - template: /.pipelines/templates/SetVersionVariables.yml@self + parameters: + ReleaseTagVar: $(ReleaseTagVar) + + - template: /.pipelines/templates/cloneToOfficialPath.yml@self + + - task: DownloadPipelineArtifact@2 + inputs: + artifact: 'macosBinResults-$(BuildArchitecture)' + path: '$(Pipeline.Workspace)\Symbols' + displayName: Download build + + - pwsh: | + Get-ChildItem "$(Pipeline.Workspace)\*" -Recurse + displayName: 'Capture Downloaded Artifacts' + # Diagnostics is not critical it passes every time it runs + continueOnError: true + + - pwsh: | + $runtime = '$(BuildArchitecture)' + Write-Host "sending.. vso[task.setvariable variable=Runtime]$runtime" + Write-Host "##vso[task.setvariable variable=Runtime]$runtime" + + $rootPath = "$(Pipeline.Workspace)\Symbols" + Write-Verbose -Verbose "Setting vso[task.setvariable variable=DropRootPath]$rootPath" + Write-Host "##vso[task.setvariable variable=DropRootPath]$rootPath" + displayName: Expand symbols zip + + - template: /.pipelines/templates/obp-file-signing.yml@self + parameters: + binPath: $(DropRootPath) + OfficialBuild: $(ps_official_build) + + - template: /.pipelines/templates/step/finalize.yml@self diff --git a/.pipelines/templates/nupkg.yml b/.pipelines/templates/nupkg.yml new file mode 100644 index 00000000000..3a2aa4f3172 --- /dev/null +++ b/.pipelines/templates/nupkg.yml @@ -0,0 +1,300 @@ +jobs: +- job: build_nupkg + displayName: Package NuPkgs + condition: succeeded() + pool: + type: windows + + variables: + - name: runCodesignValidationInjection + value: false + - name: nugetMultiFeedWarnLevel + value: none + - name: NugetSecurityAnalysisWarningLevel + value: none + - name: skipNugetSecurityAnalysis + value: true + - name: ob_outputDirectory + value: '$(Build.ArtifactStagingDirectory)\ONEBRANCH_ARTIFACT' + - name: ob_sdl_binskim_enabled + value: true + - name: ob_sdl_tsa_configFile + value: $(Build.SourcesDirectory)\PowerShell\.config\tsaoptions.json + - name: ob_sdl_credscan_suppressionsFile + value: $(Build.SourcesDirectory)\PowerShell\.config\suppress.json + - group: mscodehub-feed-read-general + - group: mscodehub-feed-read-akv + - group: DotNetPrivateBuildAccess + + steps: + - checkout: self + clean: true + env: + ob_restore_phase: true # This ensures checkout is done at the beginning of the restore phase + + - pwsh: | + Get-ChildItem -Path env: | Out-String -width 9999 -Stream | write-Verbose -Verbose + displayName: Capture environment + env: + ob_restore_phase: true # This ensures this done in restore phase to workaround signing issue + + - template: SetVersionVariables.yml@self + parameters: + ReleaseTagVar: $(ReleaseTagVar) + CreateJson: no + + - template: shouldSign.yml + + - template: cloneToOfficialPath.yml + parameters: + nativePathRoot: '$(Agent.TempDirectory)' + + - download: CoOrdinatedBuildPipeline + artifact: drop_windows_build_windows_fxdependent_release + displayName: 'Download drop_windows_build_windows_fxdependent_release' + env: + ob_restore_phase: true # This ensures this done in restore phase to workaround signing issue + + - download: CoOrdinatedBuildPipeline + artifact: drop_windows_build_windows_fxdependentWinDesktop_release + displayName: 'Download drop_windows_build_windows_fxdependentWinDesktop_release' + env: + ob_restore_phase: true # This ensures this done in restore phase to workaround signing issue + + - download: CoOrdinatedBuildPipeline + artifact: drop_linux_sign_linux_fxd + displayName: 'Download drop_linux_sign_linux_fxd' + env: + ob_restore_phase: true # This ensures this done in restore phase to workaround signing issue + + - download: CoOrdinatedBuildPipeline + artifact: drop_linux_sign_linux_fxd_x64_alpine + displayName: 'Download drop_linux_sign_linux_fxd_x64_alpine' + env: + ob_restore_phase: true # This ensures this done in restore phase to workaround signing issue + + - pwsh: | + Write-Verbose -Verbose "drop_windows_build_windows_fxdependent_release" + Get-ChildItem -Path $(Pipeline.Workspace)\CoOrdinatedBuildPipeline\drop_windows_build_windows_fxdependent_release -Recurse | Out-String | Write-Verbose -Verbose + + Write-Verbose -Verbose "drop_windows_build_windows_fxdependentWinDesktop_release" + Get-ChildItem -Path $(Pipeline.Workspace)\CoOrdinatedBuildPipeline\drop_windows_build_windows_fxdependentWinDesktop_release -Recurse | Out-String | Write-Verbose -Verbose + + Write-Verbose -Verbose "drop_linux_sign_linux_fxd" + Get-ChildItem -Path $(Pipeline.Workspace)\CoOrdinatedBuildPipeline\drop_linux_sign_linux_fxd -Recurse | Out-String | Write-Verbose -Verbose + + Write-Verbose -Verbose "drop_linux_sign_linux_fxd_x64_alpine" + Get-ChildItem -Path $(Pipeline.Workspace)\CoOrdinatedBuildPipeline\drop_linux_sign_linux_fxd_x64_alpine -Recurse | Out-String | Write-Verbose -Verbose + displayName: 'Capture download artifacts' + env: + ob_restore_phase: true # This ensures this done in restore phase to workaround signing issue + + - template: /.pipelines/templates/insert-nuget-config-azfeed.yml@self + parameters: + repoRoot: $(PowerShellRoot) + + - template: /.pipelines/templates/install-dotnet.yml@self + + - pwsh: | + Set-Location -Path '$(PowerShellRoot)' + Import-Module "$(PowerShellRoot)/build.psm1" -Force + + $sharedModules = @('Microsoft.PowerShell.Commands.Management', + 'Microsoft.PowerShell.Commands.Utility', + 'Microsoft.PowerShell.ConsoleHost', + 'Microsoft.PowerShell.Security', + 'System.Management.Automation' + ) + + $winOnlyModules = @('Microsoft.Management.Infrastructure.CimCmdlets', + 'Microsoft.PowerShell.Commands.Diagnostics', + 'Microsoft.PowerShell.CoreCLR.Eventing', + 'Microsoft.WSMan.Management', + 'Microsoft.WSMan.Runtime' + ) + + $refAssemblyFolder = Join-Path '$(System.ArtifactsDirectory)' 'RefAssembly' + $null = New-Item -Path $refAssemblyFolder -Force -Verbose -Type Directory + + Start-PSBuild -Clean -Runtime linux-x64 -Configuration Release -ReleaseTag $(ReleaseTagVar) + + $sharedModules | Foreach-Object { + $refFile = Get-ChildItem -Path "$(PowerShellRoot)\src\$_\obj\Release\net10.0\refint\$_.dll" + Write-Verbose -Verbose "RefAssembly: $refFile" + Copy-Item -Path $refFile -Destination "$refAssemblyFolder\$_.dll" -Verbose + $refDoc = "$(PowerShellRoot)\src\$_\bin\Release\net10.0\$_.xml" + if (-not (Test-Path $refDoc)) { + Write-Warning "$refDoc not found" + Get-ChildItem -Path "$(PowerShellRoot)\src\$_\bin\Release\net10.0\" | Out-String | Write-Verbose -Verbose + } + else { + Copy-Item -Path $refDoc -Destination "$refAssemblyFolder\$_.xml" -Verbose + } + } + + Start-PSBuild -Clean -Runtime win7-x64 -Configuration Release -ReleaseTag $(ReleaseTagVar) + + $winOnlyModules | Foreach-Object { + $refFile = Get-ChildItem -Path "$(PowerShellRoot)\src\$_\obj\Release\net10.0\refint\*.dll" + Write-Verbose -Verbose 'RefAssembly: $refFile' + Copy-Item -Path $refFile -Destination "$refAssemblyFolder\$_.dll" -Verbose + $refDoc = "$(PowerShellRoot)\src\$_\bin\Release\net10.0\$_.xml" + if (-not (Test-Path $refDoc)) { + Write-Warning "$refDoc not found" + Get-ChildItem -Path "$(PowerShellRoot)\src\$_\bin\Release\net10.0" | Out-String | Write-Verbose -Verbose + } + else { + Copy-Item -Path $refDoc -Destination "$refAssemblyFolder\$_.xml" -Verbose + } + } + + Get-ChildItem $refAssemblyFolder -Recurse | Out-String | Write-Verbose -Verbose + + # Set RefAssemblyPath path variable + $vstsCommandString = "vso[task.setvariable variable=RefAssemblyPath]${refAssemblyFolder}" + Write-Host "sending " + $vstsCommandString + Write-Host "##$vstsCommandString" + displayName: Build reference assemblies + env: + __DOTNET_RUNTIME_FEED_KEY: $(RUNTIME_SOURCEFEED_KEY) + + - task: onebranch.pipeline.signing@1 + displayName: Sign ref assemblies + inputs: + command: 'sign' + signing_profile: external_distribution + files_to_sign: '**\*.dll' + search_root: '$(System.ArtifactsDirectory)\RefAssembly' + + - pwsh: | + $files = @( + "Microsoft.Management.Infrastructure.CimCmdlets.dll" + "Microsoft.PowerShell.Commands.Diagnostics.dll" + "Microsoft.PowerShell.Commands.Management.dll" + "Microsoft.PowerShell.Commands.Utility.dll" + "Microsoft.PowerShell.ConsoleHost.dll" + "Microsoft.PowerShell.CoreCLR.Eventing.dll" + "Microsoft.PowerShell.Security.dll" + "Microsoft.PowerShell.SDK.dll" + "Microsoft.WSMan.Management.dll" + "Microsoft.WSMan.Runtime.dll" + "System.Management.Automation.dll" + ) + + Import-Module -Name '$(PowerShellRoot)\build.psm1' + Import-Module -Name '$(PowerShellRoot)\tools\packaging' + Find-DotNet + + Write-Verbose -Verbose "Version == $(Version)" + + $winFxdPath = "$(Pipeline.Workspace)\CoOrdinatedBuildPipeline\drop_windows_build_windows_fxdependent_release\Signed-fxdependent" + Write-Verbose -Verbose "winFxdPath == $winFxdPath" + + $linuxFxdPath = "$(Pipeline.Workspace)\CoOrdinatedBuildPipeline\drop_linux_sign_linux_fxd\Signed-fxdependent" + Write-Verbose -Verbose "linuxFxdPath == $linuxFxdPath" + + $nupkgOutputPath = Join-Path -Path '$(Pipeline.Workspace)' -ChildPath 'nupkg' + New-Item -Path $nupkgOutputPath -ItemType Directory -Force + + $files | Foreach-Object { + $FileBaseName = [System.IO.Path]::GetFileNameWithoutExtension($_) + $FilePackagePath = Join-Path -Path $nupkgOutputPath -ChildPath $FileBaseName + Write-Verbose -Verbose "FileName to package: $_" + Write-Verbose -Verbose "FilePackage path: $FilePackagePath" + New-ILNugetPackageSource -File $_ -PackagePath $FilePackagePath -PackageVersion '$(Version)' -WinFxdBinPath $winFxdPath -LinuxFxdBinPath $linuxFxdPath -RefAssemblyPath $(RefAssemblyPath) + New-ILNugetPackageFromSource -FileName $_ -PackageVersion '$(Version)' -PackagePath $FilePackagePath + } + displayName: 'Create NuGet Package for single file' + + - task: onebranch.pipeline.signing@1 + displayName: Sign nupkg files + inputs: + command: 'sign' + cp_code: 'CP-401405' + files_to_sign: '**\*.nupkg' + search_root: '$(Pipeline.Workspace)\nupkg' + + ### Create global tools + + - pwsh: | + $winFxdPath = "$(Pipeline.Workspace)\CoOrdinatedBuildPipeline\drop_windows_build_windows_fxdependent_release\Signed-fxdependent" + $winDesktopFxdPath = "$(Pipeline.Workspace)\CoOrdinatedBuildPipeline\drop_windows_build_windows_fxdependentWinDesktop_release\Signed-fxdependent-win-desktop" + $linuxFxdPath = "$(Pipeline.Workspace)\CoOrdinatedBuildPipeline\drop_linux_sign_linux_fxd\Signed-fxdependent" + $alpineFxdPath = "$(Pipeline.Workspace)\CoOrdinatedBuildPipeline\drop_linux_sign_linux_fxd_x64_alpine\Signed-fxdependent-noopt-linux-musl-x64" + + Import-Module -Name '$(PowerShellRoot)\build.psm1' + Import-Module -Name '$(PowerShellRoot)\tools\packaging' + + Start-PrepForGlobalToolNupkg -LinuxBinPath $linuxFxdPath -WindowsBinPath $winFxdPath -WindowsDesktopBinPath $winDesktopFxdPath -AlpineBinPath $alpineFxdPath + displayName: 'Prepare for global tool packages' + + - pwsh: | + Import-Module -Name '$(PowerShellRoot)\build.psm1' + Import-Module -Name '$(PowerShellRoot)\tools\packaging' + Find-DotNet + + $gblToolOutputPath = Join-Path -Path '$(Pipeline.Workspace)' -ChildPath 'globaltools' + New-Item -Path $gblToolOutputPath -ItemType Directory -Force + + $winFxdPath = "$(Pipeline.Workspace)\CoOrdinatedBuildPipeline\drop_windows_build_windows_fxdependent_release\Signed-fxdependent" + $winDesktopFxdPath = "$(Pipeline.Workspace)\CoOrdinatedBuildPipeline\drop_windows_build_windows_fxdependentWinDesktop_release\Signed-fxdependent-win-desktop" + $linuxFxdPath = "$(Pipeline.Workspace)\CoOrdinatedBuildPipeline\drop_linux_sign_linux_fxd\Signed-fxdependent" + $alpineFxdPath = "$(Pipeline.Workspace)\CoOrdinatedBuildPipeline\drop_linux_sign_linux_fxd_x64_alpine\Signed-fxdependent-noopt-linux-musl-x64" + + # Build global tools which do not have the shims exe generated in build. + $packageTypes = @('Unified', 'PowerShell.Linux.Alpine', 'PowerShell.Linux.x64', 'PowerShell.Linux.arm32', 'PowerShell.Linux.arm64') + + $packageTypes | Foreach-Object { + $PackageType = $_ + Write-Verbose -Verbose "PackageType: $PackageType" + + New-GlobalToolNupkgSource -PackageType $PackageType -PackageVersion '$(Version)' -LinuxBinPath $linuxFxdPath -WindowsBinPath $winFxdPath -WindowsDesktopBinPath $winDesktopFxdPath -AlpineBinPath $alpineFxdPath -SkipCGManifest + + Write-Verbose -Verbose "GlobalToolNuspecSourcePath = $global:GlobalToolNuSpecSourcePath" + Write-Verbose -Verbose "GlobalToolPkgName = $global:GlobalToolPkgName" + + Write-Verbose -Verbose "Starting global tool package creation for $PackageType" + New-GlobalToolNupkgFromSource -PackageNuSpecPath "$global:GlobalToolNuSpecSourcePath" -PackageName "$global:GlobalToolPkgName" -DestinationPath $gblToolOutputPath + Write-Verbose -Verbose "Global tool package created for $PackageType" + $global:GlobalToolNuSpecSourcePath = $null + $global:GlobalToolPkgName = $null + } + displayName: 'Create global tools' + + - pwsh: | + $gblToolOutputPath = Join-Path -Path '$(Pipeline.Workspace)' -ChildPath 'globaltools' + Get-ChildItem -Path $gblToolOutputPath + displayName: Capture global tools + + - task: onebranch.pipeline.signing@1 + displayName: Sign nupkg files + inputs: + command: 'sign' + cp_code: 'CP-401405' + files_to_sign: '**\*.nupkg' + search_root: '$(Pipeline.Workspace)\globaltools' + + - pwsh: | + if (-not (Test-Path '$(ob_outputDirectory)')) { + New-Item -ItemType Directory -Path '$(ob_outputDirectory)' -Force + } + + Write-Verbose -Verbose "Copying nupkgs to output directory" + $nupkgOutputPath = Join-Path -Path '$(Pipeline.Workspace)' -ChildPath 'nupkg' + Get-ChildItem -Path $nupkgOutputPath -Filter *.nupkg -Recurse | Copy-Item -Destination '$(ob_outputDirectory)' -Force -Verbose + + # Copy Windows.x86 global tool from build to output directory + $winX64GlobalTool = "$(Pipeline.Workspace)\CoOrdinatedBuildPipeline\drop_windows_build_windows_fxdependent_release\globaltool\powershell*.nupkg" + Write-Verbose -Verbose "Finding Windows.x64 global tool at $winX64GlobalTool" + $globalToolPath = Get-Item $winX64GlobalTool + Copy-Item -Path $globalToolPath -Destination '$(ob_outputDirectory)' -Force -Verbose + + Write-Verbose -Verbose "Copying global tools to output directory" + $gblToolOutputPath = Join-Path -Path '$(Pipeline.Workspace)' -ChildPath 'globaltools' + Get-ChildItem -Path $gblToolOutputPath -Filter *.nupkg -Recurse | Copy-Item -Destination '$(ob_outputDirectory)' -Force -Verbose + displayName: Copy artifacts to output directory + + - pwsh: | + $nupkgOutputPath = '$(ob_outputDirectory)' + Get-ChildItem -Path $nupkgOutputPath | Out-String | Write-Verbose -Verbose + displayName: List artifacts diff --git a/.pipelines/templates/obp-file-signing.yml b/.pipelines/templates/obp-file-signing.yml new file mode 100644 index 00000000000..cbe44ad0018 --- /dev/null +++ b/.pipelines/templates/obp-file-signing.yml @@ -0,0 +1,175 @@ +parameters: + binPath: '$(ob_outputDirectory)' + globalTool: 'false' + SigningProfile: 'external_distribution' + OfficialBuild: true + vPackScenario: false + +steps: +- pwsh: | + $fullSymbolsFolder = '${{ parameters.binPath }}' + Write-Verbose -Verbose "fullSymbolsFolder == $fullSymbolsFolder" + Get-ChildItem -Recurse $fullSymbolsFolder | Select-Object -ExpandProperty FullName | Write-Verbose -Verbose + $filesToSignDirectory = "$(Pipeline.Workspace)/toBeSigned" + if ((Test-Path -Path $filesToSignDirectory)) { + Remove-Item -Path $filesToSignDirectory -Recurse -Force + } + $null = New-Item -ItemType Directory -Path $filesToSignDirectory -Force + + $itemsToCopyWithRecurse = @( + "$($fullSymbolsFolder)/*.ps1" + "$($fullSymbolsFolder)/Microsoft.PowerShell*.dll" + ) + $itemsToCopy = @{ + "$($fullSymbolsFolder)/*.ps1" = "" + "$($fullSymbolsFolder)/Modules/Microsoft.PowerShell.Host/Microsoft.PowerShell.Host.psd1" = "Modules/Microsoft.PowerShell.Host" + "$($fullSymbolsFolder)/Modules/Microsoft.PowerShell.Management/Microsoft.PowerShell.Management.psd1" = "Modules/Microsoft.PowerShell.Management" + "$($fullSymbolsFolder)/Modules/Microsoft.PowerShell.Security/Microsoft.PowerShell.Security.psd1" = "Modules/Microsoft.PowerShell.Security" + "$($fullSymbolsFolder)/Modules/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1" = "Modules/Microsoft.PowerShell.Utility" + "$($fullSymbolsFolder)/pwsh.dll" = "" + "$($fullSymbolsFolder)/System.Management.Automation.dll" = "" + } + ## Windows only modules + if('$(ArtifactPlatform)' -eq 'windows') { + $itemsToCopy += @{ + "$($fullSymbolsFolder)/pwsh.exe" = "" + "$($fullSymbolsFolder)/Microsoft.Management.Infrastructure.CimCmdlets.dll" = "" + "$($fullSymbolsFolder)/Microsoft.WSMan.*.dll" = "" + "$($fullSymbolsFolder)/Modules/CimCmdlets/CimCmdlets.psd1" = "Modules/CimCmdlets" + "$($fullSymbolsFolder)/Modules/Microsoft.PowerShell.Diagnostics/Diagnostics.format.ps1xml" = "Modules/Microsoft.PowerShell.Diagnostics" + "$($fullSymbolsFolder)/Modules/Microsoft.PowerShell.Diagnostics/Event.format.ps1xml" = "Modules/Microsoft.PowerShell.Diagnostics" + "$($fullSymbolsFolder)/Modules/Microsoft.PowerShell.Diagnostics/GetEvent.types.ps1xml" = "Modules/Microsoft.PowerShell.Diagnostics" + "$($fullSymbolsFolder)/Modules/Microsoft.PowerShell.Security/Security.types.ps1xml" = "Modules/Microsoft.PowerShell.Security" + "$($fullSymbolsFolder)/Modules/Microsoft.PowerShell.Diagnostics/Microsoft.PowerShell.Diagnostics.psd1" = "Modules/Microsoft.PowerShell.Diagnostics" + "$($fullSymbolsFolder)/Modules/Microsoft.WSMan.Management/Microsoft.WSMan.Management.psd1" = "Modules/Microsoft.WSMan.Management" + "$($fullSymbolsFolder)/Modules/Microsoft.WSMan.Management/WSMan.format.ps1xml" = "Modules/Microsoft.WSMan.Management" + "$($fullSymbolsFolder)/Modules/PSDiagnostics/PSDiagnostics.ps?1" = "Modules/PSDiagnostics" + } + } + + $itemsToExclude = @( + # This package is retrieved from https://www.github.com/powershell/MarkdownRender + "$($fullSymbolsFolder)/Microsoft.PowerShell.MarkdownRender.dll" + ) + + if('$(ArtifactPlatform)' -eq 'linux' -or '$(ArtifactPlatform)' -eq 'macos') { + $itemsToExclude += "$($fullSymbolsFolder)/pwsh" + } + + Write-Verbose -verbose "recursively copying $($itemsToCopyWithRecurse | out-string) to $filesToSignDirectory" + Copy-Item -Path $itemsToCopyWithRecurse -Destination $filesToSignDirectory -Recurse -verbose -exclude $itemsToExclude + Write-Verbose -verbose "recursive copy done." + + foreach($pattern in $itemsToCopy.Keys) { + $destinationFolder = Join-Path $filesToSignDirectory -ChildPath $itemsToCopy.$pattern + $null = New-Item -ItemType Directory -Path $destinationFolder -Force + Write-Verbose -verbose "copying $pattern to $destinationFolder" + + if (-not (Test-Path -Path $pattern)) { + Write-Verbose -verbose "No files found for pattern $pattern" + continue + } + + Copy-Item -Path $pattern -Destination $destinationFolder -Recurse -verbose + } + + Write-Verbose -verbose "copying done." + Write-Verbose -verbose "Files to be signed at: $filesToSignDirectory" + + Get-ChildItem -Recurse -File $filesToSignDirectory | Select-Object -Property FullName + displayName: 'Prepare files to be signed' + +- task: onebranch.pipeline.signing@1 + displayName: Sign 1st party files + inputs: + command: 'sign' + signing_profile: ${{ parameters.SigningProfile }} + files_to_sign: '**\*.psd1;**\*.psm1;**\*.ps1xml;**\*.ps1;**\*.dll;**\*.exe;**\pwsh' + search_root: $(Pipeline.Workspace)/toBeSigned + +- pwsh : | + Get-ChildItem -Path env: | Out-String -width 9999 -Stream | write-Verbose -Verbose + displayName: Capture environment + +- pwsh: | + Import-Module $(PowerShellRoot)/build.psm1 -Force + Import-Module $(PowerShellRoot)/tools/packaging -Force + + $BuildPath = (Get-Item '${{ parameters.binPath }}').FullName + Write-Verbose -Verbose -Message "BuildPath: $BuildPath" + + $officialBuild = [System.Convert]::ToBoolean('${{ parameters.OfficialBuild }}') + ## copy all files to be signed to build folder + Update-PSSignedBuildFolder -BuildPath $BuildPath -SignedFilesPath '$(Pipeline.Workspace)/toBeSigned' -OfficialBuild $officialBuild + + $dlls = Get-ChildItem $BuildPath/*.dll, $BuildPath/*.exe -Recurse + $signatures = $dlls | Get-AuthenticodeSignature + $officialIssuerPattern = '^CN=(Microsoft Code Signing PCA|Microsoft Root Certificate Authority|Microsoft Corporation).*' + $testCert = '^CN=(Microsoft|TestAzureEngBuildCodeSign).*' + $missingSignatures = $signatures | Where-Object { $_.status -eq 'notsigned' -or $_.SignerCertificate.Issuer -notmatch $testCert -or $_.SignerCertificate.Issuer -notmatch $officialIssuerPattern} | select-object -ExpandProperty Path + + Write-Verbose -verbose "to be signed:`r`n $($missingSignatures | Out-String)" + + $filesToSignDirectory = "$(Pipeline.Workspace)/thirdPartyToBeSigned" + if (Test-Path $filesToSignDirectory) { + Remove-Item -Path $filesToSignDirectory -Recurse -Force + } + $null = New-Item -ItemType Directory -Path $filesToSignDirectory -Force -Verbose + + $missingSignatures | ForEach-Object { + $pathWithoutLeaf = Split-Path $_ + $relativePath = $pathWithoutLeaf.replace($BuildPath,'') + Write-Verbose -Verbose -Message "relativePath: $relativePath" + $targetDirectory = Join-Path -Path $filesToSignDirectory -ChildPath $relativePath + Write-Verbose -Verbose -Message "targetDirectory: $targetDirectory" + if(!(Test-Path $targetDirectory)) + { + $null = New-Item -ItemType Directory -Path $targetDirectory -Force -Verbose + } + Copy-Item -Path $_ -Destination $targetDirectory + } + displayName: Create ThirdParty Signing Folder + +- task: onebranch.pipeline.signing@1 + displayName: Sign 3rd Party files + inputs: + command: 'sign' + signing_profile: $(msft_3rd_party_cert_id) + files_to_sign: '**\*.dll;**\*.exe' + search_root: $(Pipeline.Workspace)/thirdPartyToBeSigned + +- pwsh: | + Get-ChildItem '$(Pipeline.Workspace)/thirdPartyToBeSigned/*' + displayName: Capture ThirdParty Signed files + +- pwsh: | + $officialBuild = [System.Convert]::ToBoolean('${{ parameters.OfficialBuild }}') + $vPackScenario = [System.Convert]::ToBoolean('${{ parameters.vPackScenario }}') + Import-Module '$(PowerShellRoot)/build.psm1' -Force + Import-Module '$(PowerShellRoot)/tools/packaging' -Force + $isGlobalTool = '${{ parameters.globalTool }}' -eq 'true' + + if ($vPackScenario) { + Write-Verbose -Verbose -Message "vPackScenario is true, copying to $(ob_outputDirectory)" + $pathForUpload = New-Item -ItemType Directory -Path '$(ob_outputDirectory)' -Force + Write-Verbose -Verbose -Message "pathForUpload: $pathForUpload" + Copy-Item -Path '${{ parameters.binPath }}\*' -Destination $pathForUpload -Recurse -Force -Verbose + Write-Verbose -Verbose -Message "Files copied to $pathForUpload" + } + elseif (-not $isGlobalTool) { + $pathForUpload = New-Item -ItemType Directory -Path '$(ob_outputDirectory)/Signed-$(Runtime)' -Force + Write-Verbose -Verbose -Message "pathForUpload: $pathForUpload" + Copy-Item -Path '${{ parameters.binPath }}\*' -Destination $pathForUpload -Recurse -Force -Verbose + Write-Verbose -Verbose -Message "Files copied to $pathForUpload" + } + else { + $pathForUpload = '${{ parameters.binPath }}' + } + + Write-Verbose "Copying third party signed files to the build folder" + $thirdPartySignedFilesPath = (Get-Item '$(Pipeline.Workspace)/thirdPartyToBeSigned').FullName + Update-PSSignedBuildFolder -BuildPath $pathForUpload -SignedFilesPath $thirdPartySignedFilesPath -OfficialBuild $officialBuild + + displayName: 'Copy signed files for upload' + +- template: /.pipelines/templates/step/finalize.yml@self diff --git a/.pipelines/templates/package-create-msix.yml b/.pipelines/templates/package-create-msix.yml new file mode 100644 index 00000000000..255915ab02c --- /dev/null +++ b/.pipelines/templates/package-create-msix.yml @@ -0,0 +1,299 @@ +parameters: + - name: OfficialBuild + type: boolean + default: false + +jobs: +- job: CreateMSIXBundle + displayName: Create .msixbundle file + pool: + type: windows + + variables: + - group: msixTools + - group: 'Azure Blob variable group' + - group: 'Store Publish Variables' + - name: ob_sdl_credscan_suppressionsFile + value: $(Build.SourcesDirectory)\PowerShell\.config\suppress.json + - name: ob_sdl_tsa_configFile + value: $(Build.SourcesDirectory)\PowerShell\.config\tsaoptions.json + - name: ob_outputDirectory + value: '$(Build.ArtifactStagingDirectory)/ONEBRANCH_ARTIFACT' + + steps: + - checkout: self + clean: true + env: + ob_restore_phase: true # This ensures checkout is done at the beginning of the restore phase + + - template: release-SetReleaseTagandContainerName.yml@self + + - task: DownloadPipelineArtifact@2 + inputs: + buildType: 'current' + artifact: drop_windows_package_arm64 + itemPattern: | + **/*.msix + targetPath: '$(Build.ArtifactStagingDirectory)/downloads' + displayName: Download windows arm64 packages + + - task: DownloadPipelineArtifact@2 + inputs: + buildType: 'current' + artifact: drop_windows_package_x64 + itemPattern: | + **/*.msix + targetPath: '$(Build.ArtifactStagingDirectory)/downloads' + displayName: Download windows x64 packages + + - task: DownloadPipelineArtifact@2 + inputs: + buildType: 'current' + artifact: drop_windows_package_x86 + itemPattern: | + **/*.msix + targetPath: '$(Build.ArtifactStagingDirectory)/downloads' + displayName: Download windows x86 packages + + # Finds the makeappx tool on the machine with image: 'onebranch.azurecr.io/windows/ltsc2022/vse2022:latest' + - pwsh: | + $cmd = Get-Command makeappx.exe -ErrorAction Ignore + if ($cmd) { + Write-Verbose -Verbose 'makeappx available in PATH' + $exePath = $cmd.Source + } else { + $toolsDir = '$(Pipeline.Workspace)\releasePipeline\tools' + New-Item $toolsDir -Type Directory -Force > $null + $makeappx = Get-ChildItem -Recurse 'C:\Program Files (x86)\Windows Kits\10\makeappx.exe' | + Where-Object { $_.DirectoryName -match 'x64' } | + Select-Object -Last 1 + $exePath = $makeappx.FullName + Write-Verbose -Verbose 'makeappx was found:' + } + $vstsCommandString = "vso[task.setvariable variable=MakeAppxPath]$exePath" + Write-Host "sending " + $vstsCommandString + Write-Host "##$vstsCommandString" + displayName: Find makeappx tool + retryCountOnTaskFailure: 1 + + - pwsh: | + $sourceDir = '$(Pipeline.Workspace)\releasePipeline\msix' + $null = New-Item -Path $sourceDir -ItemType Directory -Force + + $msixFiles = Get-ChildItem -Path "$(Build.ArtifactStagingDirectory)/downloads/*.msix" -Recurse + foreach ($msixFile in $msixFiles) { + $null = Copy-Item -Path $msixFile.FullName -Destination $sourceDir -Force -Verbose + } + + $file = Get-ChildItem $sourceDir | Select-Object -First 1 + $prefix = ($file.BaseName -split "-win")[0] + $pkgName = "$prefix.msixbundle" + Write-Verbose -Verbose "Creating $pkgName" + + $makeappx = '$(MakeAppxPath)' + $outputDir = "$sourceDir\output" + New-Item $outputDir -Type Directory -Force > $null + & $makeappx bundle /d $sourceDir /p "$outputDir\$pkgName" + + Get-ChildItem -Path $sourceDir -Recurse + $vstsCommandString = "vso[task.setvariable variable=BundleDir]$outputDir" + Write-Host "sending " + $vstsCommandString + Write-Host "##$vstsCommandString" + displayName: Create MsixBundle + retryCountOnTaskFailure: 1 + + - task: onebranch.pipeline.signing@1 + displayName: Sign MsixBundle + condition: eq('${{ parameters.OfficialBuild }}', 'true') + inputs: + command: 'sign' + signing_profile: $(MSIXProfile) + files_to_sign: '**/*.msixbundle' + search_root: '$(BundleDir)' + + - pwsh: | + $signedBundle = Get-ChildItem -Path $(BundleDir) -Filter "*.msixbundle" -File + Write-Verbose -Verbose "Signed bundle: $signedBundle" + + if (-not (Test-Path $(ob_outputDirectory))) { + New-Item -ItemType Directory -Path $(ob_outputDirectory) -Force + } + + Copy-Item -Path $signedBundle.FullName -Destination "$(ob_outputDirectory)" -Verbose + + Write-Verbose -Verbose "Uploaded Bundle:" + Get-ChildItem -Path $(ob_outputDirectory) | Write-Verbose -Verbose + displayName: Upload msixbundle to Artifacts + + - pwsh: | + Write-Verbose -Verbose "Pipeline.Workspace: $(Pipeline.Workspace)" + Get-ChildItem -Path $(Pipeline.Workspace) -Recurse | Select-Object -ExpandProperty FullName + Write-Verbose -Verbose "System.DefaultWorkingDirectory: $(System.DefaultWorkingDirectory)" + Get-ChildItem -Path $(System.DefaultWorkingDirectory) -Recurse | Select-Object -ExpandProperty FullName + Test-Path -Path '$(System.DefaultWorkingDirectory)/PowerShell/.pipelines/store/PDP-Private.xml' | Write-Verbose -Verbose + displayName: Output Pipeline.Workspace and System.DefaultWorkingDirectory + + - template: channelSelection.yml@self + + - pwsh: | + $IsLTS = '$(ChannelSelection.IsLTS)' -eq 'true' + $IsStable = '$(ChannelSelection.IsStable)' -eq 'true' + $IsPreview = '$(ChannelSelection.IsPreview)' -eq 'true' + + Write-Verbose -Verbose "Channel Selection - LTS: $IsLTS, Stable: $IsStable, Preview: $IsPreview" + + # Define app configurations for each channel + $channelConfigs = @{ + 'LTS' = @{ + AppStoreName = 'PowerShell-LTS' + ProductId = '$(productId-LTS)' + AppId = '$(AppID-LTS)' + ServiceEndpoint = "StoreAppPublish-Stable" + } + 'Stable' = @{ + AppStoreName = 'PowerShell' + ProductId = '$(productId-Stable)' + AppId = '$(AppID-Stable)' + ServiceEndpoint = "StoreAppPublish-Stable" + } + 'Preview' = @{ + AppStoreName = 'PowerShell (Preview)' + ProductId = '$(productId-Preview)' + AppId = '$(AppID-Preview)' + ServiceEndpoint = "StoreAppPublish-Preview" + } + } + + $currentChannel = if ($IsLTS) { 'LTS' } + elseif ($IsStable) { 'Stable' } + elseif ($IsPreview) { 'Preview' } + else { + Write-Error "No valid channel detected" + exit 1 + } + + $config = $channelConfigs[$currentChannel] + Write-Verbose -Verbose "Selected channel: $currentChannel" + Write-Verbose -Verbose "App Store Name: $($config.AppStoreName)" + Write-Verbose -Verbose "Product ID: $($config.ProductId)" + + # Update PDP.xml file + $pdpPath = '$(System.DefaultWorkingDirectory)/PowerShell/.pipelines/store/PDP/PDP/en-US/PDP.xml' + if (Test-Path $pdpPath) { + Write-Verbose -Verbose "Updating PDP file: $pdpPath" + + [xml]$pdpXml = Get-Content $pdpPath -Raw + + # Create namespace manager for XML with default namespace + $nsManager = New-Object System.Xml.XmlNamespaceManager($pdpXml.NameTable) + $nsManager.AddNamespace("pd", "http://schemas.microsoft.com/appx/2012/ProductDescription") + + $appStoreNameElement = $pdpXml.SelectSingleNode("//pd:AppStoreName", $nsManager) + if ($appStoreNameElement) { + $appStoreNameElement.SetAttribute("_locID", $config.AppStoreName) + Write-Verbose -Verbose "Updated AppStoreName _locID to: $($config.AppStoreName)" + } else { + Write-Warning "AppStoreName element not found in PDP file" + } + + $pdpXml.Save($pdpPath) + Write-Verbose -Verbose "PDP file updated successfully" + Get-Content -Path $pdpPath | Write-Verbose -Verbose + } else { + Write-Error "PDP file not found: $pdpPath" + exit 1 + } + + # Update SBConfig.json file + $sbConfigPath = '$(System.DefaultWorkingDirectory)/PowerShell/.pipelines/store/SBConfig.json' + if (Test-Path $sbConfigPath) { + Write-Verbose -Verbose "Updating SBConfig file: $sbConfigPath" + + $sbConfigJson = Get-Content $sbConfigPath -Raw | ConvertFrom-Json + + $sbConfigJson.appSubmission.productId = $config.ProductId + Write-Verbose -Verbose "Updated productId to: $($config.ProductId)" + + $sbConfigJson | ConvertTo-Json -Depth 100 | Set-Content $sbConfigPath -Encoding UTF8 + Write-Verbose -Verbose "SBConfig file updated successfully" + Get-Content -Path $sbConfigPath | Write-Verbose -Verbose + } else { + Write-Error "SBConfig file not found: $sbConfigPath" + exit 1 + } + + Write-Host "##vso[task.setvariable variable=ServiceConnection]$($config.ServiceEndpoint)" + Write-Host "##vso[task.setvariable variable=SBConfigPath]$($sbConfigPath)" + + # These variables are used in the next tasks to determine which ServiceEndpoint to use + $ltsValue = $IsLTS.ToString().ToLower() + $stableValue = $IsStable.ToString().ToLower() + $previewValue = $IsPreview.ToString().ToLower() + + Write-Verbose -Verbose "About to set variables:" + Write-Verbose -Verbose " LTS=$ltsValue" + Write-Verbose -Verbose " STABLE=$stableValue" + Write-Verbose -Verbose " PREVIEW=$previewValue" + + Write-Host "##vso[task.setvariable variable=LTS]$ltsValue" + Write-Host "##vso[task.setvariable variable=STABLE]$stableValue" + Write-Host "##vso[task.setvariable variable=PREVIEW]$previewValue" + + Write-Verbose -Verbose "Variables set successfully" + name: UpdateConfigs + displayName: Update PDPs and SBConfig.json + + - pwsh: | + Write-Verbose -Verbose "Checking variables after UpdateConfigs:" + Write-Verbose -Verbose "LTS=$(LTS)" + Write-Verbose -Verbose "STABLE=$(STABLE)" + Write-Verbose -Verbose "PREVIEW=$(PREVIEW)" + displayName: Debug - Check Variables + + - task: MS-RDX-MRO.windows-store-publish.package-task.store-package@3 + displayName: 'Create StoreBroker Package (Preview)' + condition: eq(variables['PREVIEW'], 'true') + inputs: + serviceEndpoint: 'StoreAppPublish-Preview' + sbConfigPath: '$(SBConfigPath)' + sourceFolder: '$(BundleDir)' + contents: '*.msixBundle' + outSBName: 'PowerShellStorePackage' + pdpPath: '$(System.DefaultWorkingDirectory)/PowerShell/.pipelines/store/PDP/PDP' + pdpMediaPath: '$(System.DefaultWorkingDirectory)/PowerShell/.pipelines/store/PDP/PDP-Media' + + - task: MS-RDX-MRO.windows-store-publish.package-task.store-package@3 + displayName: 'Create StoreBroker Package (Stable/LTS)' + condition: or(eq(variables['STABLE'], 'true'), eq(variables['LTS'], 'true')) + inputs: + serviceEndpoint: 'StoreAppPublish-Stable' + sbConfigPath: '$(SBConfigPath)' + sourceFolder: '$(BundleDir)' + contents: '*.msixBundle' + outSBName: 'PowerShellStorePackage' + pdpPath: '$(System.DefaultWorkingDirectory)/PowerShell/.pipelines/store/PDP/PDP' + pdpMediaPath: '$(System.DefaultWorkingDirectory)/PowerShell/.pipelines/store/PDP/PDP-Media' + + - pwsh: | + Get-Item -Path "$(System.DefaultWorkingDirectory)/SBLog.txt" -ErrorAction SilentlyContinue | + Copy-Item -Destination "$(ob_outputDirectory)" -Verbose + displayName: Upload Store Failure Log + condition: failed() + + - pwsh: | + $submissionPackageDir = "$(System.DefaultWorkingDirectory)/SBOutDir" + $jsonFile = "$submissionPackageDir/PowerShellStorePackage.json" + $zipFile = "$submissionPackageDir/PowerShellStorePackage.zip" + + if ((Test-Path $jsonFile) -and (Test-Path $zipFile)) { + Write-Verbose -Verbose "Uploading StoreBroker Package files:" + Write-Verbose -Verbose "JSON File: $jsonFile" + Write-Verbose -Verbose "ZIP File: $zipFile" + + Copy-Item -Path $submissionPackageDir -Destination "$(ob_outputDirectory)" -Verbose -Recurse + } + + else { + Write-Error "Required files not found in $submissionPackageDir" + } + displayName: 'Upload StoreBroker Package' diff --git a/.pipelines/templates/packaging/windows/package.yml b/.pipelines/templates/packaging/windows/package.yml new file mode 100644 index 00000000000..03673d61c7b --- /dev/null +++ b/.pipelines/templates/packaging/windows/package.yml @@ -0,0 +1,226 @@ +parameters: + runtime: x64 + +jobs: +- job: build_win_${{ parameters.runtime }} + displayName: Build Windows Packages ${{ parameters.runtime }} + condition: succeeded() + pool: + type: windows + + variables: + - name: runCodesignValidationInjection + value: false + - name: ob_sdl_codeSignValidation_enabled + value: false # Skip signing validation in build-only stage + - name: ob_signing_setup_enabled + value: false # Disable signing setup - this is a build-only stage, signing happens in separate stage + - name: ob_artifactBaseName + value: drop_windows_package_${{ parameters.runtime }} + - name: nugetMultiFeedWarnLevel + value: none + - name: NugetSecurityAnalysisWarningLevel + value: none + - name: skipNugetSecurityAnalysis + value: true + - group: DotNetPrivateBuildAccess + - group: certificate_logical_to_actual + - name: ob_outputDirectory + value: '$(Build.ArtifactStagingDirectory)\ONEBRANCH_ARTIFACT' + - name: ob_sdl_binskim_enabled + value: false # Disable for build-only, enable in signing stage + - name: ob_sdl_tsa_configFile + value: $(Build.SourcesDirectory)\PowerShell\.config\tsaoptions.json + - name: ob_sdl_credscan_suppressionsFile + value: $(Build.SourcesDirectory)\PowerShell\.config\suppress.json + - name: Runtime + value: ${{ parameters.runtime }} + - group: msixTools + + steps: + - checkout: self + clean: true + + - pwsh: | + Get-ChildItem -Path env: | Out-String -width 9999 -Stream | write-Verbose -Verbose + displayName: Capture environment + + - template: /.pipelines/templates/SetVersionVariables.yml@self + parameters: + ReleaseTagVar: $(ReleaseTagVar) + CreateJson: no + ob_restore_phase: false + + - template: /.pipelines/templates/shouldSign.yml@self + parameters: + ob_restore_phase: false + + - template: /.pipelines/templates/cloneToOfficialPath.yml@self + parameters: + nativePathRoot: '$(Agent.TempDirectory)' + ob_restore_phase: false + + - template: /.pipelines/templates/rebuild-branch-check.yml@self + + - download: CoOrdinatedBuildPipeline + artifact: drop_windows_build_windows_${{ parameters.runtime }}_release + displayName: Download signed artifacts + condition: ${{ ne(parameters.runtime, 'minSize') }} + + - download: CoOrdinatedBuildPipeline + artifact: drop_windows_build_windows_x64_${{ parameters.runtime }} + displayName: Download minsize signed artifacts + condition: ${{ eq(parameters.runtime, 'minSize') }} + + - pwsh: | + Write-Verbose -Verbose "signed artifacts" + Get-ChildItem "$(Pipeline.Workspace)\CoOrdinatedBuildPipeline\drop_windows_build_windows_${{ parameters.runtime }}_release" -Recurse + displayName: 'Capture Downloaded Artifacts' + # Diagnostics is not critical it passes every time it runs + continueOnError: true + + - template: /.pipelines/templates/install-dotnet.yml@self + parameters: + ob_restore_phase: false + + - pwsh: | + $runtime = '$(Runtime)' + Write-Verbose -Verbose "runtime = '$(Runtime)'" + + $signedFolder = switch ($runtime) { + 'x64' { 'Signed-win7-x64' } + 'x86' { 'Signed-win7-x86' } + 'arm64' { 'Signed-win-arm64' } + 'fxdependent' { 'Signed-fxdependent' } + 'fxdependentWinDesktop' { 'Signed-fxdependent-win-desktop' } + 'minsize' { 'Signed-win7-x64' } + } + + Write-Verbose -Message "Init..." -Verbose + + $repoRoot = "$env:REPOROOT" + Import-Module "$repoRoot\build.psm1" + Import-Module "$repoRoot\tools\packaging" + + Start-PSBootstrap -Scenario Both + + Find-Dotnet + + $signedFilesPath, $psoptionsFilePath = if ($env:RUNTIME -eq 'minsize') { + "$(Pipeline.Workspace)\CoOrdinatedBuildPipeline\drop_windows_build_windows_x64_${runtime}\$signedFolder" + "$(Pipeline.Workspace)\CoOrdinatedBuildPipeline\drop_windows_build_windows_x64_${runtime}\psoptions\psoptions.json" + } + else { + "$(Pipeline.Workspace)\CoOrdinatedBuildPipeline\drop_windows_build_windows_${runtime}_release\$signedFolder" + "$(Pipeline.Workspace)\CoOrdinatedBuildPipeline\drop_windows_build_windows_${runtime}_release\psoptions\psoptions.json" + } + + Write-Verbose -Verbose "signedFilesPath: $signedFilesPath" + Write-Verbose -Verbose "psoptionsFilePath: $psoptionsFilePath" + + Write-Verbose -Message "checking pwsh exists in $signedFilesPath" -Verbose + if (-not (Test-Path $signedFilesPath\pwsh.exe)) { + throw "pwsh.exe not found in $signedFilesPath" + } + + Write-Verbose -Message "Restoring PSOptions from $psoptionsFilePath" -Verbose + + Restore-PSOptions -PSOptionsPath "$psoptionsFilePath" + Get-PSOptions | Write-Verbose -Verbose + + $metadata = Get-Content "$repoRoot/tools/metadata.json" -Raw | ConvertFrom-Json + + Write-Verbose -Verbose "metadata:" + $metadata | Out-String | Write-Verbose -Verbose + + # Use the rebuild branch check from the template + $isRebuildBranch = '$(RebuildBranchCheck.IsRebuildBranch)' -eq 'true' + + # Don't build LTS packages for rebuild branches + $LTS = $metadata.LTSRelease.Package -and -not $isRebuildBranch + + if ($isRebuildBranch) { + Write-Verbose -Message "Rebuild branch detected, skipping LTS package build" -Verbose + } + + Write-Verbose -Verbose "LTS: $LTS" + + if ($LTS) { + Write-Verbose -Message "LTS Release: $LTS" + } + + Start-PSBootstrap -Scenario Package + + $WindowsRuntime = switch ($runtime) { + 'x64' { 'win7-x64' } + 'x86' { 'win7-x86' } + 'arm64' { 'win-arm64' } + 'fxdependent' { 'win7-x64' } + 'fxdependentWinDesktop' { 'win7-x64' } + 'minsize' { 'win7-x64' } + } + + $packageTypes = switch ($runtime) { + 'x64' { @('msi', 'zip', 'msix') } + 'x86' { @('msi', 'zip', 'msix') } + 'arm64' { @('msi', 'zip', 'msix') } + 'fxdependent' { 'fxdependent' } + 'fxdependentWinDesktop' { 'fxdependent-win-desktop' } + 'minsize' { 'min-size' } + } + + if (-not (Test-Path $(ob_outputDirectory))) { + New-Item -ItemType Directory -Path $(ob_outputDirectory) -Force + } + + Set-Location $repoRoot + + Start-PSPackage -Type $packageTypes -SkipReleaseChecks -WindowsRuntime $WindowsRuntime -ReleaseTag $(ReleaseTagVar) -PackageBinPath $signedFilesPath -LTS:$LTS + + displayName: 'Build Packages (Unsigned)' + env: + __DOTNET_RUNTIME_FEED_KEY: $(RUNTIME_SOURCEFEED_KEY) + + # Copy unsigned packages to output directory + - pwsh: | + $runtime = '$(Runtime)' + Write-Verbose -Verbose "runtime = '$(Runtime)'" + + $packageTypes = switch ($runtime) { + 'x64' { @('msi', 'zip', 'msix') } + 'x86' { @('msi', 'zip', 'msix') } + 'arm64' { @('msi', 'zip', 'msix') } + 'fxdependent' { 'fxdependent' } + 'fxdependentWinDesktop' { 'fxdependent-win-desktop' } + 'minsize' { 'min-size' } + } + + if (-not (Test-Path $(ob_outputDirectory))) { + New-Item -ItemType Directory -Path $(ob_outputDirectory) -Force + } + + if ($packageTypes -contains 'msi') { + $msiPkgNameFilter = "powershell-*.msi" + $msiPkgPath = Get-ChildItem -Path $(Pipeline.Workspace) -Filter $msiPkgNameFilter -Recurse -File | Select-Object -ExpandProperty FullName + Write-Verbose -Verbose "unsigned msiPkgPath: $msiPkgPath" + Copy-Item -Path $msiPkgPath -Destination '$(ob_outputDirectory)' -Force -Verbose + } + + if ($packageTypes -contains 'zip' -or $packageTypes -contains 'fxdependent' -or $packageTypes -contains 'min-size' -or $packageTypes -contains 'fxdependent-win-desktop') { + $zipPkgNameFilter = "powershell-*.zip" + $zipPkgPath = Get-ChildItem -Path $(Pipeline.Workspace) -Filter $zipPkgNameFilter -Recurse -File | Select-Object -ExpandProperty FullName + Write-Verbose -Verbose "unsigned zipPkgPath: $zipPkgPath" + Copy-Item -Path $zipPkgPath -Destination '$(ob_outputDirectory)' -Force -Verbose + } + + if ($packageTypes -contains 'msix') { + $msixPkgNameFilter = "powershell-*.msix" + $msixPkgPath = Get-ChildItem -Path $(Pipeline.Workspace) -Filter $msixPkgNameFilter -Recurse -File | Select-Object -ExpandProperty FullName + Write-Verbose -Verbose "unsigned msixPkgPath: $msixPkgPath" + Copy-Item -Path $msixPkgPath -Destination '$(ob_outputDirectory)' -Force -Verbose + } + displayName: Copy unsigned packages to output directory + + - pwsh: | + Get-ChildItem -Path $(ob_outputDirectory) -Recurse + displayName: 'List unsigned artifacts' diff --git a/.pipelines/templates/packaging/windows/sign.yml b/.pipelines/templates/packaging/windows/sign.yml new file mode 100644 index 00000000000..4a095ba7694 --- /dev/null +++ b/.pipelines/templates/packaging/windows/sign.yml @@ -0,0 +1,216 @@ +parameters: + runtime: x64 + +jobs: +- job: sign_win_${{ parameters.runtime }} + displayName: Sign Windows Packages ${{ parameters.runtime }} + condition: succeeded() + pool: + type: windows + + variables: + - name: runCodesignValidationInjection + value: false + - name: ob_artifactBaseName + value: drop_windows_package_package_win_${{ parameters.runtime }} + - name: nugetMultiFeedWarnLevel + value: none + - name: NugetSecurityAnalysisWarningLevel + value: none + - name: skipNugetSecurityAnalysis + value: true + - group: DotNetPrivateBuildAccess + - group: certificate_logical_to_actual + - name: ob_outputDirectory + value: '$(Build.ArtifactStagingDirectory)\ONEBRANCH_ARTIFACT' + - name: ob_sdl_binskim_enabled + value: true + - name: ob_sdl_tsa_configFile + value: $(Build.SourcesDirectory)\PowerShell\.config\tsaoptions.json + - name: ob_sdl_credscan_suppressionsFile + value: $(Build.SourcesDirectory)\PowerShell\.config\suppress.json + - name: Runtime + value: ${{ parameters.runtime }} + - group: msixTools + + steps: + - checkout: self + clean: true + env: + ob_restore_phase: true + + - template: /.pipelines/templates/SetVersionVariables.yml@self + parameters: + ReleaseTagVar: $(ReleaseTagVar) + CreateJson: no + + - template: /.pipelines/templates/shouldSign.yml@self + + - template: /.pipelines/templates/cloneToOfficialPath.yml@self + parameters: + nativePathRoot: '$(Agent.TempDirectory)' + + # Download unsigned packages from the build stage + - download: current + artifact: drop_windows_package_${{ parameters.runtime }} + displayName: Download unsigned packages + env: + ob_restore_phase: true + + - pwsh: | + Write-Verbose -Verbose "Downloaded unsigned artifacts:" + Get-ChildItem "$(Pipeline.Workspace)\drop_windows_package_${{ parameters.runtime }}" -Recurse + displayName: 'Capture Downloaded Unsigned Artifacts' + continueOnError: true + env: + ob_restore_phase: true + + - template: /.pipelines/templates/install-dotnet.yml@self + + # Import build.psm1 and bootstrap packaging dependencies (WiX Toolset) + - pwsh: | + $repoRoot = "$env:REPOROOT" + Import-Module "$repoRoot\build.psm1" + Import-Module "$repoRoot\tools\packaging" + Write-Verbose -Verbose "Modules imported successfully" + + # Install WiX Toolset for EXE package creation + $isArm64 = '$(Runtime)' -eq 'arm64' + $env:RUNTIME = '$(Runtime)' + Start-PSBootstrap -Scenario Package + displayName: 'Import modules and install WiX Toolset' + env: + ob_restore_phase: true + + # Sign MSI packages + - task: onebranch.pipeline.signing@1 + displayName: Sign MSI packages + inputs: + command: 'sign' + signing_profile: external_distribution + files_to_sign: '**\*.msi' + search_root: '$(Pipeline.Workspace)' + + # Create EXE package from signed MSI (for x64, x86, arm64 only) + - pwsh: | + $runtime = '$(Runtime)' + Write-Verbose -Verbose "runtime = '$(Runtime)'" + + $repoRoot = "$env:REPOROOT" + Import-Module "$repoRoot\build.psm1" + Import-Module "$repoRoot\tools\packaging" + + $noExeRuntimes = @('fxdependent', 'fxdependentWinDesktop', 'minsize') + + if ($runtime -in $noExeRuntimes) { + Write-Verbose -Verbose "No EXE generated for $runtime" + return + } + + $version = '$(Version)' + + $msiLocation = Get-ChildItem -Path $(Pipeline.Workspace) -Recurse -Filter "powershell-*$runtime.msi" | Select-Object -ExpandProperty FullName + Write-Verbose -Verbose "msiLocation: $msiLocation" + + Set-Location $repoRoot + + $exePath = New-ExePackage -ProductVersion $version -ProductTargetArchitecture $runtime -MsiLocationPath $msiLocation + Write-Verbose -Verbose "setting vso[task.setvariable variable=exePath]$exePath" + Write-Host "##vso[task.setvariable variable=exePath]$exePath" + Write-Verbose -Verbose "exePath: $exePath" + + $enginePath = Join-Path -Path '$(System.ArtifactsDirectory)\unsignedEngine' -ChildPath engine.exe + Expand-ExePackageEngine -ExePath $exePath -EnginePath $enginePath -ProductTargetArchitecture $runtime + displayName: 'Make exe and expand package' + + # Sign EXE engine + - task: onebranch.pipeline.signing@1 + displayName: Sign exe engine + inputs: + command: 'sign' + signing_profile: $(msft_3rd_party_cert_id) + files_to_sign: '$(System.ArtifactsDirectory)\unsignedEngine\*.exe' + search_root: '$(Pipeline.Workspace)' + + # Compress signed EXE engine back into package + - pwsh: | + $runtime = '$(Runtime)' + Write-Verbose -Verbose "runtime = '$(Runtime)'" + $repoRoot = "$env:REPOROOT" + Import-Module "$repoRoot\build.psm1" + Import-Module "$repoRoot\tools\packaging" + + $noExeRuntimes = @('fxdependent', 'fxdependentWinDesktop', 'minsize') + + if ($runtime -in $noExeRuntimes) { + Write-Verbose -Verbose "No EXE generated for $runtime" + return + } + + $exePath = '$(exePath)' + $enginePath = Join-Path -Path '$(System.ArtifactsDirectory)\unsignedEngine' -ChildPath engine.exe + $enginePath | Get-AuthenticodeSignature | out-string | Write-Verbose -verbose + Compress-ExePackageEngine -ExePath $exePath -EnginePath $enginePath -ProductTargetArchitecture $runtime + displayName: Compress signed exe package + + # Sign final EXE packages + - task: onebranch.pipeline.signing@1 + displayName: Sign exe packages + inputs: + command: 'sign' + signing_profile: external_distribution + files_to_sign: '**\*.exe' + search_root: '$(Pipeline.Workspace)' + + # Copy all signed packages to output directory + - pwsh: | + $runtime = '$(Runtime)' + Write-Verbose -Verbose "runtime = '$(Runtime)'" + + $packageTypes = switch ($runtime) { + 'x64' { @('msi', 'zip', 'msix', 'exe') } + 'x86' { @('msi', 'zip', 'msix', 'exe') } + 'arm64' { @('msi', 'zip', 'msix', 'exe') } + 'fxdependent' { 'fxdependent' } + 'fxdependentWinDesktop' { 'fxdependent-win-desktop' } + 'minsize' { 'min-size' } + } + + if (-not (Test-Path $(ob_outputDirectory))) { + New-Item -ItemType Directory -Path $(ob_outputDirectory) -Force + } + + if ($packageTypes -contains 'msi') { + $msiPkgNameFilter = "powershell-*.msi" + $msiPkgPath = Get-ChildItem -Path $(Pipeline.Workspace) -Filter $msiPkgNameFilter -Recurse -File | Select-Object -ExpandProperty FullName + Write-Verbose -Verbose "signed msiPkgPath: $msiPkgPath" + Copy-Item -Path $msiPkgPath -Destination '$(ob_outputDirectory)' -Force -Verbose + } + + if ($packageTypes -contains 'exe') { + $exePkgNameFilter = "powershell-*.exe" + $exePkgPath = Get-ChildItem -Path $(Pipeline.Workspace) -Filter $exePkgNameFilter -Recurse -File | Select-Object -ExpandProperty FullName + Write-Verbose -Verbose "signed exePkgPath: $exePkgPath" + Copy-Item -Path $exePkgPath -Destination '$(ob_outputDirectory)' -Force -Verbose + } + + if ($packageTypes -contains 'zip' -or $packageTypes -contains 'fxdependent' -or $packageTypes -contains 'min-size' -or $packageTypes -contains 'fxdependent-win-desktop') { + $zipPkgNameFilter = "powershell-*.zip" + $zipPkgPath = Get-ChildItem -Path $(Pipeline.Workspace) -Filter $zipPkgNameFilter -Recurse -File | Select-Object -ExpandProperty FullName + Write-Verbose -Verbose "signed zipPkgPath: $zipPkgPath" + Copy-Item -Path $zipPkgPath -Destination '$(ob_outputDirectory)' -Force -Verbose + } + + if ($packageTypes -contains 'msix') { + $msixPkgNameFilter = "powershell-*.msix" + $msixPkgPath = Get-ChildItem -Path $(Pipeline.Workspace) -Filter $msixPkgNameFilter -Recurse -File | Select-Object -ExpandProperty FullName + Write-Verbose -Verbose "signed msixPkgPath: $msixPkgPath" + Copy-Item -Path $msixPkgPath -Destination '$(ob_outputDirectory)' -Force -Verbose + } + displayName: Copy signed packages to output directory + + - pwsh: | + Get-ChildItem -Path $(ob_outputDirectory) -Recurse + displayName: 'List signed artifacts' + env: + ob_restore_phase: true diff --git a/.pipelines/templates/rebuild-branch-check.yml b/.pipelines/templates/rebuild-branch-check.yml new file mode 100644 index 00000000000..a4b546a0dc6 --- /dev/null +++ b/.pipelines/templates/rebuild-branch-check.yml @@ -0,0 +1,17 @@ +# This template checks if the current branch is a rebuild branch +# and sets an output variable IsRebuildBranch that can be used by other templates +steps: +- pwsh: | + # Check if this is a rebuild branch (e.g., rebuild/v7.4.13-rebuild.5) + $isRebuildBranch = '$(Build.SourceBranch)' -match 'refs/heads/rebuild/.*-rebuild\.' + + $value = if ($isRebuildBranch) { 'true' } else { 'false' } + Write-Verbose -Message "IsRebuildBranch: $value" -Verbose + + if ($isRebuildBranch) { + Write-Verbose -Message "Rebuild branch detected: $(Build.SourceBranch)" -Verbose + } + + Write-Host "##vso[task.setvariable variable=IsRebuildBranch;isOutput=true]$value" + name: RebuildBranchCheck + displayName: Check if Rebuild Branch diff --git a/.pipelines/templates/release-MSIX-Publish.yml b/.pipelines/templates/release-MSIX-Publish.yml new file mode 100644 index 00000000000..2bf1e130103 --- /dev/null +++ b/.pipelines/templates/release-MSIX-Publish.yml @@ -0,0 +1,125 @@ +parameters: + - name: skipMSIXPublish + type: boolean + +jobs: +- job: Store_Publish_MSIX + displayName: Publish MSIX to the Microsoft Store + pool: + type: release + os: windows + templateContext: + inputs: + - input: pipelineArtifact + pipeline: PSPackagesOfficial + artifactName: drop_msixbundle_CreateMSIXBundle + variables: + - group: 'Store Publish Variables' + - name: LTS + value: $[ stageDependencies.setReleaseTagAndChangelog.setTagAndChangelog.outputs['ChannelSelection.IsLTS'] ] + - name: STABLE + value: $[ stageDependencies.setReleaseTagAndChangelog.setTagAndChangelog.outputs['ChannelSelection.IsStable'] ] + - name: PREVIEW + value: $[ stageDependencies.setReleaseTagAndChangelog.setTagAndChangelog.outputs['ChannelSelection.IsPreview'] ] + - template: ./variable/release-shared.yml@self + parameters: + RELEASETAG: $[ stageDependencies.setReleaseTagAndChangelog.setTagAndChangelog.outputs['OutputReleaseTag.releaseTag'] ] + steps: + - task: PowerShell@2 + inputs: + targetType: inline + script: | + Write-Verbose -Verbose "Release Tag: $(ReleaseTag)" + Get-ChildItem $(Pipeline.Workspace) -Recurse | Select-Object -ExpandProperty FullName + displayName: 'Capture ReleaseTag and Downloaded Packages' + + - task: PowerShell@2 + inputs: + targetType: inline + script: | + if ("$(ReleaseTag)" -eq '') { + Write-Error "ReleaseTag is not set. Cannot proceed with publishing to the Store." + exit 1 + } + $middleURL = '' + $tagString = "$(ReleaseTag)" + if ($tagString -match '-preview') { + $middleURL = "preview" + } + elseif ($tagString -match '(\d+\.\d+)') { + $middleURL = $matches[1] + } + + $endURL = $tagString -replace '^v','' -replace '\.','' + $message = "Changelog: https://github.com/PowerShell/PowerShell/blob/master/CHANGELOG/$middleURL.md#$endURL" + Write-Verbose -Verbose "Release Notes for the Store:" + Write-Verbose -Verbose "$message" + $jsonPath = "$(Pipeline.Workspace)\SBOutDir\PowerShellStorePackage.json" + $json = Get-Content $jsonPath -Raw | ConvertFrom-Json + + $json.listings.'en-us'.baseListing.releaseNotes = $message + + $json | ConvertTo-Json -Depth 100 | Set-Content $jsonPath -Encoding UTF8 + displayName: 'Update Release Notes in JSON' + + - task: PowerShell@2 + inputs: + targetType: inline + script: | + # Convert ADO variables to PowerShell boolean variables + $IsLTS = '$(LTS)' -eq 'true' + $IsStable = '$(STABLE)' -eq 'true' + $IsPreview = '$(PREVIEW)' -eq 'true' + + Write-Verbose -Verbose "Channel Selection - LTS: $(LTS), Stable: $(STABLE), Preview: $(PREVIEW)" + + $currentChannel = if ($IsLTS) { 'LTS' } + elseif ($IsStable) { 'Stable' } + elseif ($IsPreview) { 'Preview' } + else { + Write-Error "No valid channel detected" + exit 1 + } + + # Assign AppID for Store-Publish Task + $appID = $null + if ($IsLTS) { + $appID = '$(AppID-LTS)' + } + elseif ($IsStable) { + $appID = '$(AppID-Stable)' + } + else { + $appID = '$(AppID-Preview)' + } + + Write-Host "##vso[task.setvariable variable=AppID]$appID" + Write-Verbose -Verbose "Selected channel: $currentChannel" + Write-Verbose -Verbose "Conditional tasks will handle the publishing based on channel variables" + displayName: 'Validate Channel Selection' + + - task: MS-RDX-MRO.windows-store-publish.publish-task.store-publish@3 + displayName: 'Publish StoreBroker Package (Stable/LTS)' + condition: and(ne('${{ parameters.skipMSIXPublish }}', 'true'), or(eq(variables['STABLE'], 'true'), eq(variables['LTS'], 'true'))) + inputs: + serviceEndpoint: 'StoreAppPublish-Stable' + appId: '$(AppID)' + inputMethod: JsonAndZip + jsonPath: '$(Pipeline.Workspace)\SBOutDir\PowerShellStorePackage.json' + zipPath: '$(Pipeline.Workspace)\SBOutDir\PowerShellStorePackage.zip' + numberOfPackagesToKeep: 2 + jsonZipUpdateMetadata: true + targetPublishMode: 'Immediate' + + - task: MS-RDX-MRO.windows-store-publish.publish-task.store-publish@3 + displayName: 'Publish StoreBroker Package (Preview)' + condition: and(ne('${{ parameters.skipMSIXPublish }}', 'true'), eq(variables['PREVIEW'], 'true')) + inputs: + serviceEndpoint: 'StoreAppPublish-Preview' + appId: '$(AppID)' + inputMethod: JsonAndZip + jsonPath: '$(Pipeline.Workspace)\SBOutDir\PowerShellStorePackage.json' + zipPath: '$(Pipeline.Workspace)\SBOutDir\PowerShellStorePackage.zip' + numberOfPackagesToKeep: 2 + jsonZipUpdateMetadata: true + targetPublishMode: 'Immediate' diff --git a/.pipelines/templates/release-MakeBlobPublic.yml b/.pipelines/templates/release-MakeBlobPublic.yml new file mode 100644 index 00000000000..758298202a1 --- /dev/null +++ b/.pipelines/templates/release-MakeBlobPublic.yml @@ -0,0 +1,177 @@ +parameters: + - name: SkipPSInfraInstallers + displayName: Skip Copying Archives and Installers to PSInfrastructure Public Location + type: boolean + default: false + +jobs: +- template: /.pipelines/templates/approvalJob.yml@self + parameters: + displayName: Approve Copy release packages to PSInfra storage + jobName: CopyReleaseBlobApproval + instructions: | + Approval for Copy release packages to PSInfra storage + +- job: PSInfraReleaseBlobPublic + displayName: Copy release to PSInfra storage + dependsOn: CopyReleaseBlobApproval + condition: and(succeeded(), ne('${{ parameters.SkipPSInfraInstallers }}', true)) + pool: + name: PowerShell1ES + type: windows + isCustom: true + demands: + - ImageOverride -equals PSMMS2019-Secure + + + variables: + - group: 'PSInfraStorage' + - group: 'Azure Blob variable group' + - name: ob_outputDirectory + value: '$(Build.ArtifactStagingDirectory)/ONEBRANCH_ARTIFACT' + - name: ob_sdl_tsa_configFile + value: $(Build.SourcesDirectory)\PowerShell\.config\tsaoptions.json + - name: ob_sdl_credscan_suppressionsFile + value: $(Build.SourcesDirectory)\PowerShell\.config\suppress.json + - name: ob_sdl_codeql_compiled_enabled + value: false + + steps: + - checkout: self + clean: true + env: + ob_restore_phase: true # This ensures checkout is done at the beginning of the restore phase + + - template: /.pipelines/templates/SetVersionVariables.yml@self + parameters: + ReleaseTagVar: $(ReleaseTagVar) + CreateJson: no + + - pwsh: | + Get-ChildItem Env: + displayName: 'Capture Environment Variables' + + - task: AzurePowerShell@5 + displayName: Copy blobs to PSInfra storage + inputs: + azureSubscription: az-blob-cicd-infra + scriptType: inlineScript + azurePowerShellVersion: LatestVersion + pwsh: true + inline: | + $sourceStorageAccountName = '$(StorageAccount)' + $destinationStorageAccountName = '$(PSInfraStorageAccount)' + $destinationContainerName = '$web' + $destinationPrefix = 'install/$(ReleaseTagVar)' + + $sourceContext = New-AzStorageContext -StorageAccountName $sourceStorageAccountName + Write-Verbose -Verbose "Source context: $($sourceContext.BlobEndPoint)" + + $destinationContext = New-AzStorageContext -StorageAccountName $destinationStorageAccountName + Write-Verbose -Verbose "Destination context: $($destinationContext.BlobEndPoint)" + + foreach ($sourceContainerName in '$(AzureVersion)', '$(AzureVersion)-gc') { + $blobs = Get-AzStorageBlob -Context $sourceContext -Container $sourceContainerName + + Write-Verbose -Verbose "Blobs found in $sourceContainerName" + $blobs.Name | Write-Verbose -Verbose + + Write-Verbose -Verbose "Copying blobs from $sourceContainerName to $destinationContainerName/$destinationPrefix" + + foreach ($blob in $blobs) { + $sourceBlobName = $blob.Name + Write-Verbose -Verbose "sourceBlobName = $sourceBlobName" + + $destinationBlobName = "$destinationPrefix/$sourceBlobName" + Write-Verbose -Verbose "destinationBlobName = $destinationBlobName" + $existingBlob = Get-AzStorageBlob -Blob $destinationBlobName -Container $destinationContainerName -Context $destinationContext -ErrorAction Ignore + if ($existingBlob) { + Write-Verbose -Verbose "Blob $destinationBlobName already exists in '$destinationStorageAccountName/$destinationContainerName', removing before copy." + $existingBlob | Remove-AzStorageBlob -ErrorAction Stop -Verbose + } + + Copy-AzStorageBlob -SourceContext $sourceContext -DestinationContext $destinationContext -SrcContainer $sourceContainerName -SrcBlob $sourceBlobName -DestContainer $destinationContainerName -DestBlob $destinationBlobName -Force -Verbose -Confirm:$false + } + } + + +- template: /.pipelines/templates/approvalJob.yml@self + parameters: + displayName: Approve Copy Global tool packages to PSInfra storage + jobName: CopyBlobApproval + instructions: | + Approval for Copy global tool packages to PSInfra storage + +- job: PSInfraBlobPublic + displayName: Copy global tools to PSInfra storage + dependsOn: CopyBlobApproval + pool: + name: PowerShell1ES + type: windows + isCustom: true + demands: + - ImageOverride -equals PSMMS2019-Secure + + variables: + - group: 'PSInfraStorage' + - group: 'Azure Blob variable group' + - name: ob_outputDirectory + value: '$(Build.ArtifactStagingDirectory)/ONEBRANCH_ARTIFACT' + - name: ob_sdl_tsa_configFile + value: $(Build.SourcesDirectory)\PowerShell\.config\tsaoptions.json + - name: ob_sdl_credscan_suppressionsFile + value: $(Build.SourcesDirectory)\PowerShell\.config\suppress.json + + steps: + - checkout: self + clean: true + env: + ob_restore_phase: true # This ensures checkout is done at the beginning of the restore phase + + - template: /.pipelines/templates/SetVersionVariables.yml@self + parameters: + ReleaseTagVar: $(ReleaseTagVar) + CreateJson: no + + - pwsh: | + Get-ChildItem Env: | Out-String -width 9999 -Stream | write-Verbose -Verbose + displayName: 'Capture Environment Variables' + + - task: AzurePowerShell@5 + displayName: Copy blobs to PSInfra storage + inputs: + azureSubscription: az-blob-cicd-infra + scriptType: inlineScript + azurePowerShellVersion: LatestVersion + pwsh: true + inline: | + $sourceStorageAccountName = '$(StorageAccount)' + $sourceContainerName = '$(AzureVersion)-nuget' + $prefix = 'globaltool' + + $destinationStorageAccountName = '$(PSInfraStorageAccount)' + $destinationContainerName = '$web' + $destinationPrefix = 'tool/$(Version)' + + $sourceContext = New-AzStorageContext -StorageAccountName $sourceStorageAccountName + Write-Verbose -Verbose "Source context: $($sourceContext.BlobEndPoint)" + + $destinationContext = New-AzStorageContext -StorageAccountName $destinationStorageAccountName + Write-Verbose -Verbose "Destination context: $($destinationContext.BlobEndPoint)" + + $blobs = Get-AzStorageBlob -Context $sourceContext -Container $sourceContainerName -Prefix $prefix + + Write-Verbose -Verbose "Blobs found in $sourceContainerName" + $blobs.Name | Write-Verbose -Verbose + + Write-Verbose -Verbose "Copying blobs from $sourceContainerName to $destinationContainerName/$destinationPrefix" + + foreach ($blob in $blobs) { + $sourceBlobName = $blob.Name + Write-Verbose -Verbose "sourceBlobName = $sourceBlobName" + + $destinationBlobName = $sourceBlobName -replace "$prefix", $destinationPrefix + Write-Verbose -Verbose "destinationBlobName = $destinationBlobName" + + Copy-AzStorageBlob -SourceContext $sourceContext -DestinationContext $destinationContext -SrcContainer $sourceContainerName -SrcBlob $sourceBlobName -DestContainer $destinationContainerName -DestBlob $destinationBlobName -Force -Verbose -Confirm:$false + } diff --git a/.pipelines/templates/release-SetReleaseTagandContainerName.yml b/.pipelines/templates/release-SetReleaseTagandContainerName.yml new file mode 100644 index 00000000000..d40551353d2 --- /dev/null +++ b/.pipelines/templates/release-SetReleaseTagandContainerName.yml @@ -0,0 +1,36 @@ +parameters: +- name: restorePhase + default: false + +steps: +- pwsh: | + $variable = 'releaseTag' + $branch = $ENV:BUILD_SOURCEBRANCH + if($branch -notmatch '^.*((release/|rebuild/.*rebuild))') + { + throw "Branch name is not in release format: '$branch'" + } + + $releaseTag = $Branch -replace '^.*((release|rebuild)/)' + $vstsCommandString = "vso[task.setvariable variable=$Variable;isOutput=true]$releaseTag" + Write-Verbose -Message "setting $Variable to $releaseTag" -Verbose + Write-Host -Object "##$vstsCommandString" + name: OutputReleaseTag + displayName: Set Release Tag + env: + ob_restore_phase: ${{ parameters.restorePhase }} + +- pwsh: | + $azureVersion = '$(OutputReleaseTag.ReleaseTag)'.ToLowerInvariant() -replace '\.', '-' + $vstsCommandString = "vso[task.setvariable variable=AzureVersion;isOutput=true]$azureVersion" + Write-Host "sending " + $vstsCommandString + Write-Host "##$vstsCommandString" + + $version = '$(OutputReleaseTag.ReleaseTag)'.ToLowerInvariant().Substring(1) + $vstsCommandString = "vso[task.setvariable variable=Version;isOutput=true]$version" + Write-Host ("sending " + $vstsCommandString) + Write-Host "##$vstsCommandString" + name: OutputVersion + displayName: Set container name + env: + ob_restore_phase: ${{ parameters.restorePhase }} diff --git a/.pipelines/templates/release-SetTagAndChangelog.yml b/.pipelines/templates/release-SetTagAndChangelog.yml new file mode 100644 index 00000000000..b33e652b3c7 --- /dev/null +++ b/.pipelines/templates/release-SetTagAndChangelog.yml @@ -0,0 +1,51 @@ +jobs: +- job: setTagAndChangelog + displayName: Set Tag and Upload Changelog + condition: succeeded() + pool: + type: windows + variables: + - group: 'mscodehub-code-read-akv' + - name: ob_outputDirectory + value: '$(Build.ArtifactStagingDirectory)/ONEBRANCH_ARTIFACT' + - name: ob_sdl_credscan_suppressionsFile + value: $(Build.SourcesDirectory)\PowerShell\.config\suppress.json + - name: ob_sdl_tsa_configFile + value: $(Build.SourcesDirectory)\PowerShell\.config\tsaoptions.json + steps: + - template: release-SetReleaseTagandContainerName.yml@self + + - checkout: self + clean: true + env: + ob_restore_phase: true + + - pwsh: | + Write-Verbose -Verbose "Release Tag: $(OutputReleaseTag.releaseTag)" + $releaseVersion = '$(OutputReleaseTag.releaseTag)' -replace '^v','' + Write-Verbose -Verbose "Release Version: $releaseVersion" + $semanticVersion = [System.Management.Automation.SemanticVersion]$releaseVersion + + $isPreview = $semanticVersion.PreReleaseLabel -ne $null + + $fileName = if ($isPreview) { + "preview.md" + } + else { + $semanticVersion.Major.ToString() + "." + $semanticVersion.Minor.ToString() + ".md" + } + + $filePath = "$(Build.SourcesDirectory)/PowerShell/CHANGELOG/$fileName" + Write-Verbose -Verbose "Selected Log file: $filePath" + + if (-not (Test-Path -Path $filePath)) { + Write-Error "Changelog file not found: $filePath" + exit 1 + } + + Write-Verbose -Verbose "Creating output directory for CHANGELOG: $(ob_outputDirectory)/CHANGELOG" + New-Item -Path $(ob_outputDirectory)/CHANGELOG -ItemType Directory -Force + Copy-Item -Path $filePath -Destination $(ob_outputDirectory)/CHANGELOG + displayName: Upload Changelog + + - template: channelSelection.yml@self diff --git a/.pipelines/templates/release-githubNuget.yml b/.pipelines/templates/release-githubNuget.yml new file mode 100644 index 00000000000..5f67ce6a9e4 --- /dev/null +++ b/.pipelines/templates/release-githubNuget.yml @@ -0,0 +1,203 @@ +parameters: + - name: skipPublish + type: boolean + +jobs: +- job: GithubReleaseDraft + displayName: Create GitHub Release Draft + condition: succeeded() + pool: + type: release + os: windows + templateContext: + inputs: + - input: pipelineArtifact + artifactName: drop_setReleaseTagAndChangelog_SetTagAndChangelog + - input: pipelineArtifact + pipeline: PSPackagesOfficial + artifactName: drop_upload_upload_packages + variables: + - template: ./variable/release-shared.yml@self + parameters: + RELEASETAG: $[ stageDependencies.setReleaseTagAndChangelog.setTagAndChangelog.outputs['OutputReleaseTag.releaseTag'] ] + + steps: + - task: PowerShell@2 + inputs: + targetType: inline + script: | + Write-Verbose -Verbose "Release Tag: $(ReleaseTag)" + Get-ChildItem Env: | Out-String -Stream | Write-Verbose -Verbose + displayName: 'Capture Environment Variables' + + - task: PowerShell@2 + inputs: + targetType: inline + script: | + $Path = "$(Pipeline.Workspace)/GitHubPackages" + $OutputPath = Join-Path $Path 'hashes.sha256' + $packages = Get-ChildItem -Path $Path -Include * -Recurse -File + $checksums = $packages | + ForEach-Object { + Write-Verbose -Verbose "Generating checksum file for $($_.FullName)" + $packageName = $_.Name + $hash = (Get-FileHash -Path $_.FullName -Algorithm SHA256).Hash.ToLower() + # the '*' before the packagename signifies it is a binary + "$hash *$packageName" + } + $checksums | Out-File -FilePath $OutputPath -Force + $fileContent = Get-Content -Path $OutputPath -Raw | Out-String + Write-Verbose -Verbose -Message $fileContent + displayName: Add sha256 hashes + + - task: PowerShell@2 + inputs: + targetType: inline + script: | + Get-ChildItem $(Pipeline.Workspace) -recurse | Select-Object -ExpandProperty FullName + displayName: List all files in the workspace + + - task: PowerShell@2 + inputs: + targetType: inline + script: | + $releaseVersion = '$(ReleaseTag)' -replace '^v','' + Write-Verbose -Verbose "Available modules: " + Get-Module | Write-Verbose -Verbose + + $filePath = Get-ChildItem -Path "$(Pipeline.Workspace)/CHANGELOG" -Filter '*.md' | Select-Object -First 1 -ExpandProperty FullName + + if (-not (Test-Path $filePath)) { + throw "$filePath not found" + } + + $changelog = Get-Content -Path $filePath + + $headingPattern = "^## \[\d+\.\d+\.\d+" + $headingStartLines = $changelog | Select-String -Pattern $headingPattern | Select-Object -ExpandProperty LineNumber + $startLine = $headingStartLines[0] + $endLine = $headingStartLines[1] - 1 + + $clContent = $changelog | Select-Object -Skip ($startLine-1) -First ($endLine - $startLine) | Out-String + + $StringBuilder = [System.Text.StringBuilder]::new($clContent, $clContent.Length + 2kb) + $StringBuilder.AppendLine().AppendLine() > $null + $StringBuilder.AppendLine("### SHA256 Hashes of the release artifacts").AppendLine() > $null + Get-ChildItem -Path "$(Pipeline.Workspace)/GitHubPackages/" -File | ForEach-Object { + $PackageName = $_.Name + $SHA256 = (Get-FileHash -Path $_.FullName -Algorithm SHA256).Hash + $StringBuilder.AppendLine("- $PackageName").AppendLine(" - $SHA256") > $null + } + + $clContent = $StringBuilder.ToString() + + Write-Verbose -Verbose "Selected content: `n$clContent" + + $releaseNotesFilePath = "$(Pipeline.Workspace)/release-notes.md" + $clContent | Out-File -FilePath $releaseNotesFilePath -Encoding utf8 + + Write-Host "##vso[task.setvariable variable=ReleaseNotesFilePath;]$releaseNotesFilePath" + + #if name has prelease then make prerelease true as a variable + if ($releaseVersion -like '*-*') { + Write-Host "##vso[task.setvariable variable=IsPreRelease;]true" + } else { + Write-Host "##vso[task.setvariable variable=IsPreRelease;]false" + } + displayName: Set variables for GitHub release task + + - task: PowerShell@2 + inputs: + targetType: inline + script: | + Write-Host "ReleaseNotes content:" + Get-Content "$(Pipeline.Workspace)/release-notes.md" -Raw | Out-String -width 9999 | Write-Host + displayName: Verify Release Notes + + - task: PowerShell@2 + inputs: + targetType: inline + script: | + $middleURL = '' + $tagString = "$(ReleaseTag)" + Write-Verbose -Verbose "Use the following command to push the tag:" + if ($tagString -match '-preview') { + $middleURL = "preview" + } + elseif ($tagString -match '(\d+\.\d+)') { + $middleURL = $matches[1] + } + $endURL = $tagString -replace '^v|\.', '' + $message = "https://github.com/PowerShell/PowerShell/blob/master/CHANGELOG/$middleURL.md#$endURL" + Write-Verbose -Verbose "git tag -a $(ReleaseTag) $env:BUILD_SOURCEVERSION -m $message" + displayName: Git Push Tag Command + + - task: GitHubRelease@1 + inputs: + gitHubConnection: GitHubReleasePAT + repositoryName: PowerShell/PowerShell + target: master + assets: '$(Pipeline.Workspace)/GitHubPackages/*' + tagSource: 'userSpecifiedTag' + tag: '$(ReleaseTag)' + title: "$(ReleaseTag) Release of PowerShell" + isDraft: true + addChangeLog: false + action: 'create' + releaseNotesFilePath: '$(ReleaseNotesFilePath)' + isPrerelease: '$(IsPreRelease)' + +- job: NuGetPublish + displayName: Publish to NuGet + condition: succeeded() + pool: + type: release + os: windows + templateContext: + inputs: + - input: pipelineArtifact + pipeline: PSPackagesOfficial + artifactName: drop_upload_upload_packages + variables: + - template: ./variable/release-shared.yml@self + parameters: + VERSION: $[ stageDependencies.setReleaseTagAndChangelog.SetTagAndChangelog.outputs['OutputVersion.Version'] ] + + steps: + - task: PowerShell@2 + inputs: + targetType: inline + script: | + Write-Verbose -Verbose "Version: $(Version)" + Get-ChildItem Env: | Out-String -width 9999 -Stream | write-Verbose -Verbose + displayName: 'Capture Environment Variables' + + - task: PowerShell@2 + inputs: + targetType: inline + script: | + #Exclude all global tool packages. Their names start with 'PowerShell.' + $null = New-Item -ItemType Directory -Path "$(Pipeline.Workspace)/release" + Copy-Item "$(Pipeline.Workspace)/NuGetPackages/*.nupkg" -Destination "$(Pipeline.Workspace)/release" -Exclude "PowerShell.*.nupkg" -Force -Verbose + + $releaseVersion = '$(Version)' + $globalToolPath = "$(Pipeline.Workspace)/NuGetPackages/PowerShell.$releaseVersion.nupkg" + + if ($releaseVersion -notlike '*-*') { + # Copy the global tool package for stable releases + Copy-Item $globalToolPath -Destination "$(Pipeline.Workspace)/release" + } + + Write-Verbose -Verbose "The .nupkgs below will be pushed:" + Get-ChildItem "$(Pipeline.Workspace)/release" -recurse + displayName: Download and capture nupkgs + condition: and(ne('${{ parameters.skipPublish }}', 'true'), succeeded()) + + - task: NuGetCommand@2 + displayName: 'NuGet push' + condition: and(ne('${{ parameters.skipPublish }}', 'true'), succeeded()) + inputs: + command: push + packagesToPush: '$(Pipeline.Workspace)/release/*.nupkg' + nuGetFeedType: external + publishFeedCredentials: PowerShellNuGetOrgPush diff --git a/.pipelines/templates/release-prep-for-ev2.yml b/.pipelines/templates/release-prep-for-ev2.yml new file mode 100644 index 00000000000..cf7982cd5e1 --- /dev/null +++ b/.pipelines/templates/release-prep-for-ev2.yml @@ -0,0 +1,237 @@ +parameters: +- name: skipPublish + type: boolean + default: false + +stages: +- stage: PrepForEV2 + displayName: 'Copy and prep all files needed for EV2 stage' + jobs: + - job: CopyEV2FilesToArtifact + displayName: 'Copy EV2 Files to Artifact' + pool: + type: linux + variables: + - name: ob_outputDirectory + value: '$(Build.ArtifactStagingDirectory)/ONEBRANCH_ARTIFACT' + - name: repoRoot + value: '$(Build.SourcesDirectory)/PowerShell' + - name: ev2ServiceGroupRootFolder + value: '$(Build.SourcesDirectory)/PowerShell/.pipelines/EV2Specs/ServiceGroupRoot' + - name: ev2ParametersFolder + value: '$(Build.SourcesDirectory)/PowerShell/.pipelines/EV2Specs/ServiceGroupRoot/Parameters' + - group: 'mscodehub-code-read-akv' + - group: 'packages.microsoft.com' + - name: ob_sdl_credscan_suppressionsFile + value: $(Build.SourcesDirectory)\PowerShell\.config\suppress.json + steps: + - checkout: self ## the global setting on lfs didn't work + lfs: false + env: + ob_restore_phase: true + + - template: release-SetReleaseTagandContainerName.yml + parameters: + restorePhase: true + + - pwsh: | + $packageVersion = '$(OutputReleaseTag.ReleaseTag)'.ToLowerInvariant() -replace '^v','' + $vstsCommandString = "vso[task.setvariable variable=packageVersion]$packageVersion" + Write-Host "sending " + $vstsCommandString + Write-Host "##$vstsCommandString" + displayName: Set Package version + env: + ob_restore_phase: true + + - pwsh: | + $branch = 'mirror-target' + $gitArgs = "clone", + "--verbose", + "--branch", + "$branch", + "https://$(mscodehubCodeReadPat)@mscodehub.visualstudio.com/PowerShellCore/_git/Internal-PowerShellTeam-Tools", + '$(Pipeline.Workspace)/tools' + $gitArgs | Write-Verbose -Verbose + git $gitArgs + displayName: Clone Internal-PowerShellTeam-Tools from MSCodeHub + env: + ob_restore_phase: true + + - pwsh: | + Get-ChildItem Env: | Out-String -Stream | write-Verbose -Verbose + displayName: 'Capture Environment Variables' + env: + ob_restore_phase: true + + - pwsh: | + Get-ChildItem '$(Build.SourcesDirectory)' + displayName: 'Capture BuildDirectory' + env: + ob_restore_phase: true + + - pwsh: | + Get-ChildItem '$(Pipeline.Workspace)' -Recurse | Out-String -Stream | write-Verbose -Verbose + displayName: 'Capture Workspace' + env: + ob_restore_phase: true + + - pwsh: | + New-Item -Path '$(ev2ParametersFolder)' -ItemType Directory + displayName: 'Create Parameters folder under EV2Specs folder' + env: + ob_restore_phase: true + + - task: PipAuthenticate@1 + inputs: + artifactFeeds: 'PowerShellCore/PowerShellCore_PublicPackages' + displayName: 'Pip Authenticate' + env: + ob_restore_phase: true + + - pwsh: | + python3 -m pip install --upgrade pip + pip --version --verbose + + Write-Verbose -Verbose "Download pmc-cli to folder without installing it" + $pythonDlFolderPath = Join-Path '$(ev2ServiceGroupRootFolder)/Shell/Run' -ChildPath "python_dl" + pip download -d $pythonDlFolderPath pmc-cli --platform=manylinux_2_17_x86_64 --only-binary=:all: --verbose + displayName: 'Download pmc-cli package' + env: + ob_restore_phase: true + + - download: PSPackagesOfficial + artifact: 'drop_linux_package_deb' + displayName: 'Download artifact containing .deb_amd64.deb file from PSPackagesOfficial triggering pipeline' + env: + ob_restore_phase: true + + - download: PSPackagesOfficial + artifact: 'drop_linux_package_rpm' + displayName: 'Download artifact containing .rh.x64_86.rpm file from PSPackagesOfficial triggering pipeline' + env: + ob_restore_phase: true + + - download: PSPackagesOfficial + artifact: 'drop_linux_package_mariner_x64' + displayName: 'Download artifact containing .cm.x86_64.rpm file from PSPackagesOfficial triggering pipeline' + env: + ob_restore_phase: true + + - download: PSPackagesOfficial + artifact: 'drop_linux_package_mariner_arm64' + displayName: 'Download artifact containing .cm.aarch64.rpm file from PSPackagesOfficial triggering pipeline' + env: + ob_restore_phase: true + + - pwsh: | + Write-Verbose -Verbose "Copy ESRP signed .deb and .rpm packages" + $downloadedPipelineFolder = Join-Path '$(Pipeline.Workspace)' -ChildPath 'PSPackagesOfficial' + $srcFilesFolder = Join-Path -Path '$(Pipeline.Workspace)' -ChildPath 'SourceFiles' + New-Item -Path $srcFilesFolder -ItemType Directory + $packagesFolder = Join-Path -Path $srcFilesFolder -ChildPath 'packages' + New-Item -Path $packagesFolder -ItemType Directory + + $packageFiles = Get-ChildItem -Path $downloadedPipelineFolder -Recurse -Directory -Filter "drop_*" | Get-ChildItem -File -Include *.deb, *.rpm + foreach ($file in $packageFiles) + { + Write-Verbose -Verbose "copying file: $($file.FullName)" + Copy-Item -Path $($file.FullName) -Destination $packagesFolder -Verbose + } + + $packagesTarGzDestination = Join-Path -Path '$(ev2ParametersFolder)' -ChildPath 'packages.tar.gz' + tar -czvf $packagesTarGzDestination -C $packagesFolder . + displayName: 'Copy signed .deb and .rpm packages to .tar.gz to pass as a file var to shell extension' + env: + ob_restore_phase: true + + - pwsh: | + $pathToPMCMetadataFile = Join-Path -Path '$(ev2ParametersFolder)' -ChildPath 'pmcMetadata.json' + + $metadata = Get-Content -Path "$(repoRoot)/tools/metadata.json" -Raw | ConvertFrom-Json + $metadataHash = @{} + $skipPublishValue = '${{ parameters.skipPublish }}' + $metadataHash["ReleaseTag"] = '$(OutputReleaseTag.ReleaseTag)' + $metadataHash["LTS"] = $metadata.LTSRelease.Latest + $metadataHash["ForProduction"] = $true + $metadataHash["SkipPublish"] = [System.Convert]::ToBoolean($skipPublishValue) + + $metadataHash | ConvertTo-Json | Out-File $pathToPMCMetadataFile + + $mappingFilePath = Join-Path -Path '$(repoRoot)/tools/packages.microsoft.com' -ChildPath 'mapping.json' + $mappingFilePathExists = Test-Path $mappingFilePath + $mappingFileEV2Path = Join-Path -Path '$(ev2ParametersFolder)' -ChildPath "mapping.json" + Write-Verbose -Verbose "Copy mapping.json file at: $mappingFilePath which exists: $mappingFilePathExists to: $mappingFileEV2Path" + Copy-Item -Path $mappingFilePath -Destination $mappingFileEV2Path + displayName: 'Create pmcScriptMetadata.json and mapping.json file' + env: + ob_restore_phase: true + + - pwsh: | + $pathToJsonFile = Join-Path -Path '$(ev2ServiceGroupRootFolder)' -ChildPath 'RolloutSpec.json' + $content = Get-Content -Path $pathToJsonFile | ConvertFrom-Json + $content.RolloutMetadata.Notification.Email.To = '$(PmcEV2SupportEmail)' + Remove-Item -Path $pathToJsonFile + $content | ConvertTo-Json -Depth 4 | Out-File $pathToJsonFile + displayName: 'Replace values in RolloutSpecPath.json' + env: + ob_restore_phase: true + + - pwsh: | + $pathToJsonFile = Join-Path -Path '$(ev2ServiceGroupRootFolder)' -ChildPath 'UploadLinux.Rollout.json' + $content = Get-Content -Path $pathToJsonFile | ConvertFrom-Json + + $identityString = "/subscriptions/$(PmcSubscription)/resourcegroups/$(PmcResourceGroup)/providers/Microsoft.ManagedIdentity/userAssignedIdentities/$(PmcMIName)" + $content.shellExtensions.launch.identity.userAssignedIdentities[0] = $identityString + + Remove-Item -Path $pathToJsonFile + $content | ConvertTo-Json -Depth 6 | Out-File $pathToJsonFile + displayName: 'Replace values in UploadLinux.Rollout.json file' + env: + ob_restore_phase: true + + - pwsh: | + $pathToJsonFile = Join-Path -Path '$(ev2ServiceGroupRootFolder)' -ChildPath 'ServiceModel.json' + $content = Get-Content -Path $pathToJsonFile | ConvertFrom-Json + $content.ServiceResourceGroups[0].AzureResourceGroupName = '$(PmcResourceGroup)' + $content.ServiceResourceGroups[0].AzureSubscriptionId = '$(PmcSubscription)' + + Remove-Item -Path $pathToJsonFile + $content | ConvertTo-Json -Depth 9 | Out-File $pathToJsonFile + displayName: 'Replace values in ServiceModel.json' + env: + ob_restore_phase: true + + - pwsh: | + $settingFilePath = Join-Path '$(ev2ServiceGroupRootFolder)/Shell/Run' -ChildPath 'settings.toml' + New-Item -Path $settingFilePath -ItemType File + $pmcMIClientID = '$(PmcMIClientID)' + $pmcEndpoint = '$(PmcEndpointUrl)' + + Add-Content -Path $settingFilePath -Value "[default]" + Add-Content -Path $settingFilePath -Value "base_url = `"$pmcEndpoint`"" + Add-Content -Path $settingFilePath -Value "auth_type = `"msi`"" + Add-Content -Path $settingFilePath -Value "client_id = `"$pmcMIClientID`"" + displayName: 'Create settings.toml file with MI clientId populated' + env: + ob_restore_phase: true + + - task: onebranch.pipeline.signing@1 + inputs: + command: 'sign' + signing_profile: external_distribution + files_to_sign: '*.ps1' + search_root: '$(repoRoot)/.pipelines/EV2Specs/ServiceGroupRoot/Shell/Run' + displayName: Sign Run.ps1 + + - pwsh: | + # folder to tar must have: Run.ps1, settings.toml, python_dl + $srcPath = Join-Path '$(ev2ServiceGroupRootFolder)' -ChildPath 'Shell' + $pathToRunTarFile = Join-Path $srcPath -ChildPath "Run.tar" + tar -cvf $pathToRunTarFile -C $srcPath ./Run + displayName: 'Create archive for the shell extension' + + - task: CopyFiles@2 + inputs: + SourceFolder: '$(repoRoot)/.pipelines' + Contents: 'EV2Specs/**' + TargetFolder: $(ob_outputDirectory) diff --git a/.pipelines/templates/release-publish-pmc.yml b/.pipelines/templates/release-publish-pmc.yml new file mode 100644 index 00000000000..d5454845211 --- /dev/null +++ b/.pipelines/templates/release-publish-pmc.yml @@ -0,0 +1,37 @@ +stages: +- stage: 'Prod_Release' + displayName: 'Deploy packages to PMC with EV2' + dependsOn: + - PrepForEV2 + variables: + - name: ob_release_environment + value: "Production" + - name: repoRoot + value: $(Build.SourcesDirectory) + jobs: + - job: Prod_ReleaseJob + displayName: Publish to PMC + pool: + type: release + + steps: + - task: DownloadPipelineArtifact@2 + inputs: + targetPath: '$(Pipeline.Workspace)' + artifact: drop_PrepForEV2_CopyEv2FilesToArtifact + displayName: 'Download drop_PrepForEV2_CopyEv2FilesToArtifact artifact that has all files needed' + + - task: DownloadPipelineArtifact@2 + inputs: + buildType: 'current' + targetPath: '$(Pipeline.Workspace)' + displayName: 'Download to get EV2 Files' + + - task: vsrm-ev2.vss-services-ev2.adm-release-task.ExpressV2Internal@1 + displayName: 'Ev2: Push to PMC' + inputs: + UseServerMonitorTask: true + EndpointProviderType: ApprovalService + ApprovalServiceEnvironment: Production + ServiceRootPath: '$(Pipeline.Workspace)/drop_PrepForEV2_CopyEV2FilesToArtifact/EV2Specs/ServiceGroupRoot' + RolloutSpecPath: '$(Pipeline.Workspace)/drop_PrepForEV2_CopyEV2FilesToArtifact/EV2Specs/ServiceGroupRoot/RolloutSpec.json' diff --git a/.pipelines/templates/release-symbols.yml b/.pipelines/templates/release-symbols.yml new file mode 100644 index 00000000000..1023dcf5259 --- /dev/null +++ b/.pipelines/templates/release-symbols.yml @@ -0,0 +1,91 @@ +parameters: + - name: skipPublish + default: false + type: boolean + +jobs: +- job: PublishSymbols + displayName: Publish Symbols + condition: succeeded() + pool: + type: windows + variables: + - name: runCodesignValidationInjection + value: false + - name: NugetSecurityAnalysisWarningLevel + value: none + - name: DOTNET_NOLOGO + value: 1 + - name: ob_outputDirectory + value: '$(Build.ArtifactStagingDirectory)/ONEBRANCH_ARTIFACT' + - name: ob_sdl_codeSignValidation_enabled + value: false + - name: ob_sdl_binskim_enabled + value: false + - name: ob_sdl_tsa_configFile + value: $(Build.SourcesDirectory)\PowerShell\.config\tsaoptions.json + - name: ob_sdl_credscan_suppressionsFile + value: $(Build.SourcesDirectory)\PowerShell\.config\suppress.json + + steps: + - checkout: self + clean: true + env: + ob_restore_phase: true # This ensures checkout is done at the beginning of the restore phase + + - template: release-SetReleaseTagandContainerName.yml + + - pwsh: | + Get-ChildItem Env: | Out-String -width 9999 -Stream | write-Verbose -Verbose + displayName: 'Capture Environment Variables' + + - download: CoOrdinatedBuildPipeline + artifact: drop_windows_build_windows_x64_release + patterns: 'symbols.zip' + displayName: Download winx64 + + - download: CoOrdinatedBuildPipeline + artifact: drop_windows_build_windows_x86_release + patterns: 'symbols.zip' + displayName: Download winx86 + + - download: CoOrdinatedBuildPipeline + artifact: drop_windows_build_windows_arm64_release + patterns: 'symbols.zip' + displayName: Download winx64 + + - pwsh: | + Write-Verbose -Verbose "Enumerating $(Pipeline.Workspace)\CoOrdinatedBuildPipeline" + $downloadedArtifacts = Get-ChildItem -Path "$(Pipeline.Workspace)\CoOrdinatedBuildPipeline" -Recurse -Filter 'symbols.zip' + $downloadedArtifacts + $expandedRoot = New-Item -Path "$(Pipeline.Workspace)/expanded" -ItemType Directory -Verbose + $symbolsRoot = New-Item -Path "$(Pipeline.Workspace)/symbols" -ItemType Directory -Verbose + + $downloadedArtifacts | ForEach-Object { + $folderName = (Get-Item (Split-Path $_.FullName)).Name + Write-Verbose -Verbose "Expanding $($_.FullName) to $expandedRoot/$folderName/$($_.BaseName)" + $destFolder = New-Item -Path "$expandedRoot/$folderName/$($_.BaseName)/" -ItemType Directory -Verbose + Expand-Archive -Path $_.FullName -DestinationPath $destFolder -Force + + $symbolsToPublish = New-Item -Path "$symbolsRoot/$folderName/$($_.BaseName)" -ItemType Directory -Verbose + + Get-ChildItem -Path $destFolder -Recurse -Filter '*.pdb' | ForEach-Object { + Copy-Item -Path $_.FullName -Destination $symbolsToPublish -Verbose + } + } + + Write-Verbose -Verbose "Enumerating $symbolsRoot" + Get-ChildItem -Path $symbolsRoot -Recurse + $vstsCommandString = "vso[task.setvariable variable=SymbolsPath]$symbolsRoot" + Write-Verbose -Message "$vstsCommandString" -Verbose + Write-Host -Object "##$vstsCommandString" + displayName: Expand and capture symbols folders + + - task: PublishSymbols@2 + inputs: + symbolsFolder: '$(SymbolsPath)' + searchPattern: '**/*.pdb' + indexSources: false + publishSymbols: true + symbolServerType: teamServices + detailedLog: true diff --git a/.pipelines/templates/release-upload-buildinfo.yml b/.pipelines/templates/release-upload-buildinfo.yml new file mode 100644 index 00000000000..c18c96fc646 --- /dev/null +++ b/.pipelines/templates/release-upload-buildinfo.yml @@ -0,0 +1,141 @@ +parameters: + - name: skipPublish + default: false + type: boolean + +jobs: +- job: BuildInfoPublish + displayName: Publish BuildInfo + condition: succeeded() + pool: + name: PowerShell1ES + type: windows + isCustom: true + demands: + - ImageOverride -equals PSMMS2019-Secure + variables: + - name: runCodesignValidationInjection + value: false + - name: NugetSecurityAnalysisWarningLevel + value: none + - name: DOTNET_NOLOGO + value: 1 + - name: ob_outputDirectory + value: '$(Build.ArtifactStagingDirectory)/ONEBRANCH_ARTIFACT' + - group: 'Azure Blob variable group' + - name: ob_sdl_codeSignValidation_enabled + value: false + - name: ob_sdl_binskim_enabled + value: false + - name: ob_sdl_tsa_configFile + value: $(Build.SourcesDirectory)\PowerShell\.config\tsaoptions.json + - name: ob_sdl_credscan_suppressionsFile + value: $(Build.SourcesDirectory)\PowerShell\.config\suppress.json + + steps: + - checkout: self + clean: true + env: + ob_restore_phase: true # This ensures checkout is done at the beginning of the restore phase + + - template: release-SetReleaseTagandContainerName.yml + + - pwsh: | + Get-ChildItem Env: | Out-String -width 9999 -Stream | write-Verbose -Verbose + displayName: 'Capture Environment Variables' + + - download: PSPackagesOfficial + artifact: BuildInfoJson + displayName: Download build info artifact + + - pwsh: | + $toolsDirectory = '$(Build.SourcesDirectory)/tools' + Import-Module "$toolsDirectory/ci.psm1" + $jsonFile = Get-Item "$ENV:PIPELINE_WORKSPACE/PSPackagesOfficial/BuildInfoJson/*.json" + $fileName = Split-Path $jsonFile -Leaf + + $dateTime = [datetime]::UtcNow + $dateTime = [datetime]::new($dateTime.Ticks - ($dateTime.Ticks % [timespan]::TicksPerSecond), $dateTime.Kind) + + $metadata = Get-Content -LiteralPath "$toolsDirectory/metadata.json" -ErrorAction Stop | ConvertFrom-Json + $stableRelease = $metadata.StableRelease.Latest + $ltsRelease = $metadata.LTSRelease.Latest + + Write-Verbose -Verbose "Writing $jsonFile contents:" + $buildInfoJsonContent = Get-Content $jsonFile -Encoding UTF8NoBom -Raw + Write-Verbose -Verbose $buildInfoJsonContent + + $buildInfo = $buildInfoJsonContent | ConvertFrom-Json + $buildInfo.ReleaseDate = $dateTime + + $targetFile = "$ENV:PIPELINE_WORKSPACE/$fileName" + ConvertTo-Json -InputObject $buildInfo | Out-File $targetFile -Encoding ascii + + if ($stableRelease -or $fileName -eq "preview.json") { + Set-BuildVariable -Name CopyMainBuildInfo -Value YES + } else { + Set-BuildVariable -Name CopyMainBuildInfo -Value NO + } + + Set-BuildVariable -Name BuildInfoJsonFile -Value $targetFile + + ## Create 'lts.json' if it's the latest stable and also a LTS release. + + if ($fileName -eq "stable.json") { + if ($ltsRelease) { + $ltsFile = "$ENV:PIPELINE_WORKSPACE/lts.json" + Copy-Item -Path $targetFile -Destination $ltsFile -Force + Set-BuildVariable -Name LtsBuildInfoJsonFile -Value $ltsFile + Set-BuildVariable -Name CopyLTSBuildInfo -Value YES + } else { + Set-BuildVariable -Name CopyLTSBuildInfo -Value NO + } + + $releaseTag = $buildInfo.ReleaseTag + $version = $releaseTag -replace '^v' + $semVersion = [System.Management.Automation.SemanticVersion] $version + + $versionFile = "$ENV:PIPELINE_WORKSPACE/$($semVersion.Major)-$($semVersion.Minor).json" + Copy-Item -Path $targetFile -Destination $versionFile -Force + Set-BuildVariable -Name VersionBuildInfoJsonFile -Value $versionFile + Set-BuildVariable -Name CopyVersionBuildInfo -Value YES + } else { + Set-BuildVariable -Name CopyVersionBuildInfo -Value NO + } + displayName: Create json files + + - task: AzurePowerShell@5 + displayName: Upload buildjson to blob + inputs: + azureSubscription: az-blob-cicd-infra + scriptType: inlineScript + azurePowerShellVersion: LatestVersion + pwsh: true + inline: | + $containerName = '$web' + $storageAccount = '$(PSInfraStorageAccount)' + $prefix = "buildinfo" + + $storageContext = New-AzStorageContext -StorageAccountName $storageAccount -UseConnectedAccount + + if ($env:CopyMainBuildInfo -eq 'YES') { + $jsonFile = "$env:BuildInfoJsonFile" + $blobName = Get-Item $jsonFile | Split-Path -Leaf + Write-Verbose -Verbose "Uploading $jsonFile to $containerName/$prefix/$blobName" + Set-AzStorageBlobContent -File $jsonFile -Container $containerName -Blob "$prefix/$blobName" -Context $storageContext -Force + } + + if ($env:CopyLTSBuildInfo -eq 'YES') { + $jsonFile = "$env:LtsBuildInfoJsonFile" + $blobName = Get-Item $jsonFile | Split-Path -Leaf + Write-Verbose -Verbose "Uploading $jsonFile to $containerName/$prefix/$blobName" + Set-AzStorageBlobContent -File $jsonFile -Container $containerName -Blob "$prefix/$blobName" -Context $storageContext -Force + } + + if ($env:CopyVersionBuildInfo -eq 'YES') { + $jsonFile = "$env:VersionBuildInfoJsonFile" + $blobName = Get-Item $jsonFile | Split-Path -Leaf + Write-Verbose -Verbose "Uploading $jsonFile to $containerName/$prefix/$blobName" + Set-AzStorageBlobContent -File $jsonFile -Container $containerName -Blob "$prefix/$blobName" -Context $storageContext -Force + } + condition: and(succeeded(), eq(variables['CopyMainBuildInfo'], 'YES')) diff --git a/.pipelines/templates/release-validate-fxdpackages.yml b/.pipelines/templates/release-validate-fxdpackages.yml new file mode 100644 index 00000000000..3f4f9a3bb6c --- /dev/null +++ b/.pipelines/templates/release-validate-fxdpackages.yml @@ -0,0 +1,118 @@ +parameters: + - name: jobName + type: string + default: "" + - name: displayName + type: string + default: "" + - name: jobtype + type: string + default: "" + - name: artifactName + type: string + default: "" + - name: packageNamePattern + type: string + default: "" + - name: arm64 + type: string + default: "no" + - name: enableCredScan + type: boolean + default: true + +jobs: +- job: ${{ parameters.jobName }} + displayName: ${{ parameters.displayName }} + variables: + - group: DotNetPrivateBuildAccess + - name: artifactName + value: ${{ parameters.artifactName }} + - name: ob_outputDirectory + value: '$(Build.ArtifactStagingDirectory)/ONEBRANCH_ARTIFACT' + - name: ob_sdl_credscan_suppressionsFile + value: $(Build.SourcesDirectory)\PowerShell\.config\suppress.json + - name: ob_sdl_tsa_configFile + value: $(Build.SourcesDirectory)\PowerShell\.config\tsaoptions.json + - name: ob_sdl_credscan_enabled + value: ${{ parameters.enableCredScan }} + + pool: + type: ${{ parameters.jobtype }} + ${{ if eq(parameters.arm64, 'yes') }}: + hostArchitecture: arm64 + + steps: + - checkout: self + clean: true + + - template: release-SetReleaseTagandContainerName.yml@self + + - download: PSPackagesOfficial + artifact: "${{ parameters.artifactName }}" + displayName: Download fxd artifact + + - pwsh: | + Get-ChildItem -Path env: | Out-String -width 9999 -Stream | write-Verbose -Verbose + displayName: Capture environment + + - pwsh: | + $artifactName = '$(artifactName)' + Get-ChildItem "$(Pipeline.Workspace)/PSPackagesOfficial/$artifactName" -Recurse + displayName: 'Capture Downloaded Artifacts' + + - template: /.pipelines/templates/install-dotnet.yml@self + + - pwsh: | + $artifactName = '$(artifactName)' + $rootPath = "$(Pipeline.Workspace)/PSPackagesOfficial/$artifactName" + + $destPath = New-Item "$rootPath/fxd" -ItemType Directory + $packageNameFilter = '${{ parameters.packageNamePattern }}' + + if ($packageNameFilter.EndsWith('tar.gz')) { + $package = @(Get-ChildItem -Path "$rootPath/*.tar.gz") + Write-Verbose -Verbose "Package: $package" + if ($package.Count -ne 1) { + throw 'Only 1 package was expected.' + } + tar -xvf $package.FullName -C $destPath + } + else { + $package = @(Get-ChildItem -Path "$rootPath/*.zip") + Write-Verbose -Verbose "Package: $package" + if ($package.Count -ne 1) { + throw 'Only 1 package was expected.' + } + Expand-Archive -Path $package.FullName -Destination "$destPath" -Verbose + } + displayName: Expand fxd package + + - pwsh: | + $repoRoot = "$(Build.SourcesDirectory)/PowerShell" + $artifactName = '$(artifactName)' + $rootPath = "$(Pipeline.Workspace)/PSPackagesOfficial/$artifactName" + + $env:DOTNET_NOLOGO=1 + Import-Module "$repoRoot/build.psm1" -Force + Find-Dotnet -SetDotnetRoot + Write-Verbose -Verbose "DOTNET_ROOT: $env:DOTNET_ROOT" + Write-Verbose -Verbose "Check dotnet install" + dotnet --info + Write-Verbose -Verbose "Start test" + $packageNameFilter = '${{ parameters.packageNamePattern }}' + $pwshExeName = if ($packageNameFilter.EndsWith('tar.gz')) { 'pwsh' } else { 'pwsh.exe' } + $pwshPath = Join-Path "$rootPath/fxd" $pwshExeName + + if ($IsLinux) { + chmod u+x $pwshPath + } + + $pwshDllPath = Join-Path "$rootPath/fxd" 'pwsh.dll' + + $actualOutput = & dotnet $pwshDllPath -c 'Start-ThreadJob -ScriptBlock { "1" } | Wait-Job | Receive-Job' + Write-Verbose -Verbose "Actual output: $actualOutput" + if ($actualOutput -ne 1) { + throw "Actual output is not as expected" + } + displayName: Test package diff --git a/.pipelines/templates/release-validate-globaltools.yml b/.pipelines/templates/release-validate-globaltools.yml new file mode 100644 index 00000000000..8c2031d5cc9 --- /dev/null +++ b/.pipelines/templates/release-validate-globaltools.yml @@ -0,0 +1,127 @@ +parameters: + jobName: "" + displayName: "" + jobtype: "windows" + globalToolExeName: 'pwsh.exe' + globalToolPackageName: 'PowerShell.Windows.x64' + + +jobs: +- job: ${{ parameters.jobName }} + displayName: ${{ parameters.displayName }} + pool: + type: ${{ parameters.jobtype }} + variables: + - group: DotNetPrivateBuildAccess + - name: ob_outputDirectory + value: '$(Build.ArtifactStagingDirectory)/ONEBRANCH_ARTIFACT' + - name: ob_sdl_credscan_suppressionsFile + value: $(Build.SourcesDirectory)\PowerShell\.config\suppress.json + - name: ob_sdl_tsa_configFile + value: $(Build.SourcesDirectory)\PowerShell\.config\tsaoptions.json + + steps: + - checkout: self + clean: true + + - template: release-SetReleaseTagandContainerName.yml@self + + - download: PSPackagesOfficial + artifact: drop_nupkg_build_nupkg + displayName: Download nupkgs + + - pwsh: | + Get-ChildItem -Path env: | Out-String -width 9999 -Stream | write-Verbose -Verbose + displayName: Capture environment + + - pwsh: | + Get-ChildItem "$(Pipeline.Workspace)/PSPackagesOfficial/drop_nupkg_build_nupkg" -Recurse + displayName: 'Capture Downloaded Artifacts' + + - template: /.pipelines/templates/install-dotnet.yml@self + + - pwsh: | + $repoRoot = "$(Build.SourcesDirectory)/PowerShell" + + Import-Module "$repoRoot/build.psm1" -Force -Verbose + Start-PSBootstrap -Scenario Dotnet + + $toolPath = New-Item -ItemType Directory "$(System.DefaultWorkingDirectory)/toolPath" | Select-Object -ExpandProperty FullName + + Write-Verbose -Verbose "dotnet tool list -g" + dotnet tool list -g + + $packageName = '${{ parameters.globalToolPackageName }}' + Write-Verbose -Verbose "Installing $packageName" + + dotnet tool install --add-source "$ENV:PIPELINE_WORKSPACE/PSPackagesOfficial/drop_nupkg_build_nupkg" --tool-path $toolPath --version '$(OutputVersion.Version)' $packageName + + Get-ChildItem -Path $toolPath + + displayName: Install global tool + env: + __DOTNET_RUNTIME_FEED_KEY: $(RUNTIME_SOURCEFEED_KEY) + + - pwsh: | + $toolPath = "$(System.DefaultWorkingDirectory)/toolPath/${{ parameters.globalToolExeName }}" + + if (-not (Test-Path $toolPath)) + { + throw "Tool is not installed at $toolPath" + } + else + { + Write-Verbose -Verbose "Tool found at: $toolPath" + } + displayName: Validate tool is installed + + - pwsh: | + $repoRoot = "$(Build.SourcesDirectory)/PowerShell" + + Import-Module "$repoRoot/build.psm1" -Force -Verbose + Start-PSBootstrap -Scenario Dotnet + + $exeName = if ($IsWindows) { "pwsh.exe" } else { "pwsh" } + + $toolPath = "$(System.DefaultWorkingDirectory)/toolPath/${{ parameters.globalToolExeName }}" + + $source = (get-command -Type Application -Name dotnet | Select-Object -First 1 -ExpandProperty source) + $target = (Get-ChildItem $source).target + + # If we find a symbolic link for dotnet, then we need to split the filename off the target. + if ($target) { + Write-Verbose -Verbose "Splitting target: $target" + $target = Split-Path $target + } + + Write-Verbose -Verbose "target is set as $target" + + $env:DOTNET_ROOT = (resolve-path -Path (Join-Path (split-path $source) $target)).ProviderPath + + Write-Verbose -Verbose "DOTNET_ROOT: $env:DOTNET_ROOT" + Get-ChildItem $env:DOTNET_ROOT + + $versionFound = & $toolPath -c '$PSVersionTable.PSVersion.ToString()' + + if ( '$(OutputVersion.Version)' -ne $versionFound) + { + throw "Expected version of global tool not found. Installed version is $versionFound" + } + else + { + write-verbose -verbose "Found expected version: $versionFound" + } + + $dateYear = & $toolPath -c '(Get-Date).Year' + + if ( $dateYear -ne [DateTime]::Now.Year) + { + throw "Get-Date returned incorrect year: $dateYear" + } + else + { + write-verbose -verbose "Got expected year: $dateYear" + } + displayName: Basic validation + env: + __DOTNET_RUNTIME_FEED_KEY: $(RUNTIME_SOURCEFEED_KEY) diff --git a/.pipelines/templates/release-validate-packagenames.yml b/.pipelines/templates/release-validate-packagenames.yml new file mode 100644 index 00000000000..6344418cd8f --- /dev/null +++ b/.pipelines/templates/release-validate-packagenames.yml @@ -0,0 +1,184 @@ +jobs: +- job: validatePackageNames + displayName: Validate Package Names + pool: + type: windows + variables: + - name: ob_outputDirectory + value: '$(Build.ArtifactStagingDirectory)/ONEBRANCH_ARTIFACT' + - name: ob_sdl_credscan_suppressionsFile + value: $(Build.SourcesDirectory)\PowerShell\.config\suppress.json + - name: ob_sdl_tsa_configFile + value: $(Build.SourcesDirectory)\PowerShell\.config\tsaoptions.json + - group: 'Azure Blob variable group' + + steps: + - checkout: self + clean: true + + - template: release-SetReleaseTagandContainerName.yml + + - pwsh: | + Get-ChildItem ENV: | Out-String -width 9999 -Stream | write-Verbose -Verbose + displayName: Capture environment + + - pwsh: | + $name = "{0}_{1:x}" -f '$(OutputReleaseTag.releaseTag)', (Get-Date).Ticks + Write-Host $name + Write-Host "##vso[build.updatebuildnumber]$name" + displayName: Set Release Name + + - task: AzurePowerShell@5 + displayName: Upload packages to blob + inputs: + azureSubscription: az-blob-cicd-infra + scriptType: inlineScript + azurePowerShellVersion: LatestVersion + pwsh: true + inline: | + $storageAccount = Get-AzStorageAccount -ResourceGroupName '$(StorageResourceGroup)' -Name '$(StorageAccount)' + $ctx = $storageAccount.Context + $container = '$(OutputVersion.AzureVersion)' + + $destinationPath = '$(System.ArtifactsDirectory)' + $blobList = Get-AzStorageBlob -Container $container -Context $ctx + foreach ($blob in $blobList) { + $blobName = $blob.Name + $destinationFile = Join-Path -Path $destinationPath -ChildPath $blobName + Get-AzStorageBlobContent -Container $container -Blob $blobName -Destination $destinationFile -Context $ctx -Force + Write-Output "Downloaded $blobName to $destinationFile" + } + + - pwsh: | + Get-ChildItem $(System.ArtifactsDirectory)\* -recurse | Select-Object -ExpandProperty Name + displayName: Capture Artifact Listing + + - pwsh: | + $message = @() + Get-ChildItem $(System.ArtifactsDirectory)\* -recurse -filter *.rpm | ForEach-Object { + if($_.Name -notmatch 'powershell\-(preview-|lts-)?\d+\.\d+\.\d+(_[a-z]*\.\d+)?-1.(rh|cm).(x86_64|aarch64)\.rpm') + { + $messageInstance = "$($_.Name) is not a valid package name" + $message += $messageInstance + Write-Warning $messageInstance + } + } + if($message.count -gt 0){throw ($message | out-string)} + displayName: Validate RPM package names + + - pwsh: | + $message = @() + Get-ChildItem $(System.ArtifactsDirectory)\* -recurse -filter *.tar.gz | ForEach-Object { + if($_.Name -notmatch 'powershell-(lts-)?\d+\.\d+\.\d+\-([a-z]*.\d+\-)?(linux|osx|linux-musl)+\-(x64\-fxdependent|x64|arm32|arm64|x64\-musl-noopt\-fxdependent)\.(tar\.gz)') + { + $messageInstance = "$($_.Name) is not a valid package name" + $message += $messageInstance + Write-Warning $messageInstance + } + } + if($message.count -gt 0){throw ($message | out-string)} + displayName: Validate Tar.Gz Package Names + + - pwsh: | + $message = @() + Get-ChildItem $(System.ArtifactsDirectory)\* -recurse -filter *.pkg | ForEach-Object { + if($_.Name -notmatch 'powershell-(lts-)?\d+\.\d+\.\d+\-([a-z]*.\d+\-)?osx\-(x64|arm64)\.pkg') + { + $messageInstance = "$($_.Name) is not a valid package name" + $message += $messageInstance + Write-Warning $messageInstance + } + } + if($message.count -gt 0){throw ($message | out-string)} + displayName: Validate PKG Package Names + + - pwsh: | + $message = @() + Get-ChildItem $(System.ArtifactsDirectory)\* -recurse -include *.zip, *.msi | ForEach-Object { + if($_.Name -notmatch 'PowerShell-\d+\.\d+\.\d+\-([a-z]*.\d+\-)?win\-(fxdependent|x64|arm64|x86|fxdependentWinDesktop)\.(msi|zip){1}') + { + $messageInstance = "$($_.Name) is not a valid package name" + $message += $messageInstance + Write-Warning $messageInstance + } + } + + if($message.count -gt 0){throw ($message | out-string)} + displayName: Validate Zip and MSI Package Names + + - pwsh: | + $message = @() + Get-ChildItem $(System.ArtifactsDirectory)\* -recurse -filter *.deb | ForEach-Object { + if($_.Name -notmatch 'powershell(-preview|-lts)?_\d+\.\d+\.\d+([\-~][a-z]*.\d+)?-\d\.deb_amd64\.deb') + { + $messageInstance = "$($_.Name) is not a valid package name" + $message += $messageInstance + Write-Warning $messageInstance + } + } + if($message.count -gt 0){throw ($message | out-string)} + displayName: Validate Deb Package Names + +# Move to 1ES SBOM validation tool +# - job: validateBOM +# displayName: Validate Package Names +# pool: +# type: windows +# variables: +# - name: ob_outputDirectory +# value: '$(Build.ArtifactStagingDirectory)/ONEBRANCH_ARTIFACT' +# - name: ob_sdl_credscan_suppressionsFile +# value: $(Build.SourcesDirectory)\PowerShell\.config\suppress.json +# - name: ob_sdl_tsa_configFile +# value: $(Build.SourcesDirectory)\PowerShell\.config\tsaoptions.json +# - group: 'Azure Blob variable group' + +# steps: +# - checkout: self +# clean: true + +# - pwsh: | +# Get-ChildItem ENV: | Out-String -width 9999 -Stream | write-Verbose -Verbose +# displayName: Capture environment + +# - template: release-SetReleaseTagAndContainerName.yml + +# - pwsh: | +# $name = "{0}_{1:x}" -f '$(releaseTag)', (Get-Date).Ticks +# Write-Host $name +# Write-Host "##vso[build.updatebuildnumber]$name" +# displayName: Set Release Name + +# - task: DownloadPipelineArtifact@2 +# inputs: +# source: specific +# project: PowerShellCore +# pipeline: '696' +# preferTriggeringPipeline: true +# runVersion: latestFromBranch +# runBranch: '$(Build.SourceBranch)' +# artifact: finalResults +# path: $(System.ArtifactsDirectory) + + +# - pwsh: | +# Get-ChildItem $(System.ArtifactsDirectory)\* -recurse | Select-Object -ExpandProperty Name +# displayName: Capture Artifact Listing + +# - pwsh: | +# Install-module Pester -Scope CurrentUser -Force -MaximumVersion 4.99 +# displayName: Install Pester +# condition: succeededOrFailed() + +# - pwsh: | +# Import-module './build.psm1' +# Import-module './tools/packaging' +# $env:PACKAGE_FOLDER = '$(System.ArtifactsDirectory)' +# $path = Join-Path -Path $pwd -ChildPath './packageReleaseTests.xml' +# $results = invoke-pester -Script './tools/packaging/releaseTests' -OutputFile $path -OutputFormat NUnitXml -PassThru +# Write-Host "##vso[results.publish type=NUnit;mergeResults=true;runTitle=Package Release Tests;publishRunAttachments=true;resultFiles=$path;]" +# if($results.TotalCount -eq 0 -or $results.FailedCount -gt 0) +# { +# throw "Package Release Tests failed" +# } +# displayName: Run packaging release tests diff --git a/.pipelines/templates/release-validate-sdk.yml b/.pipelines/templates/release-validate-sdk.yml new file mode 100644 index 00000000000..3b0442f65d6 --- /dev/null +++ b/.pipelines/templates/release-validate-sdk.yml @@ -0,0 +1,98 @@ +parameters: + jobName: "" + displayName: "" + poolName: "windows" + imageName: 'none' + +jobs: +- job: ${{ parameters.jobName }} + displayName: ${{ parameters.displayName }} + pool: + type: linux + isCustom: true + ${{ if eq( parameters.poolName, 'Azure Pipelines') }}: + name: ${{ parameters.poolName }} + vmImage: ${{ parameters.imageName }} + ${{ else }}: + name: ${{ parameters.poolName }} + demands: + - ImageOverride -equals ${{ parameters.imageName }} + + variables: + - group: mscodehub-feed-read-general + - group: mscodehub-feed-read-akv + - group: DotNetPrivateBuildAccess + + steps: + - checkout: self + clean: true + lfs: false + + - template: /.pipelines/templates/insert-nuget-config-azfeed.yml@self + parameters: + repoRoot: "$(Build.SourcesDirectory)" + + - template: release-SetReleaseTagandContainerName.yml@self + + - download: PSPackagesOfficial + artifact: drop_nupkg_build_nupkg + displayName: Download nupkgs + + - pwsh: | + Get-ChildItem -Path env: | Out-String -width 9999 -Stream | write-Verbose -Verbose + displayName: Capture environment + + - pwsh: | + Get-ChildItem "$(Pipeline.Workspace)/PSPackagesOfficial/drop_nupkg_build_nupkg" -Recurse + displayName: 'Capture Downloaded Artifacts' + + - template: /.pipelines/templates/install-dotnet.yml@self + + - pwsh: | + $repoRoot = "$(Build.SourcesDirectory)" + + Import-Module "$repoRoot/build.psm1" -Force -Verbose + Start-PSBootstrap -Scenario Dotnet + + $env:DOTNET_NOLOGO=1 + + $localLocation = "$(Pipeline.Workspace)/PSPackagesOfficial/drop_nupkg_build_nupkg" + $xmlElement = @" + + + + "@ + + $releaseVersion = '$(OutputVersion.Version)' + + Write-Verbose -Message "Release Version: $releaseVersion" -Verbose + + Set-Location -Path $repoRoot/test/hosting + + Get-ChildItem + + ## register the packages download directory in the nuget file + $nugetPath = './NuGet.Config' + if(!(test-path $nugetPath)) { + $nugetPath = "$repoRoot/nuget.config" + } + Write-Verbose -Verbose "nugetPath: $nugetPath" + $nugetConfigContent = Get-Content $nugetPath -Raw + $updateNugetContent = $nugetConfigContent.Replace("", $xmlElement) + + $updateNugetContent | Out-File $nugetPath -Encoding ascii + + Get-Content $nugetPath + + dotnet --info + dotnet restore + dotnet test /property:RELEASE_VERSION=$releaseVersion --test-adapter-path:. "--logger:xunit;LogFilePath=$(System.DefaultWorkingDirectory)/test-hosting.xml" + displayName: Restore and execute tests + env: + __DOTNET_RUNTIME_FEED_KEY: $(RUNTIME_SOURCEFEED_KEY) + + - task: PublishTestResults@2 + displayName: 'Publish Test Results **\test-hosting.xml' + inputs: + testResultsFormat: XUnit + testResultsFiles: '**\test-hosting.xml' diff --git a/.pipelines/templates/set-reporoot.yml b/.pipelines/templates/set-reporoot.yml new file mode 100644 index 00000000000..af7983afaa1 --- /dev/null +++ b/.pipelines/templates/set-reporoot.yml @@ -0,0 +1,35 @@ +parameters: +- name: ob_restore_phase + type: boolean + default: true + +steps: +- pwsh: | + $path = "./build.psm1" + if($env:REPOROOT){ + Write-Verbose "reporoot already set to ${env:REPOROOT}" -Verbose + exit 0 + } + if(Test-Path -Path $path) + { + Write-Verbose "reporoot detected at: ." -Verbose + $repoRoot = '.' + } + else{ + $path = "./PowerShell/build.psm1" + if(Test-Path -Path $path) + { + Write-Verbose "reporoot detected at: ./PowerShell" -Verbose + $repoRoot = './PowerShell' + } + } + if($repoRoot) { + $vstsCommandString = "vso[task.setvariable variable=repoRoot]$repoRoot" + Write-Host ("sending " + $vstsCommandString) + Write-Host "##$vstsCommandString" + } else { + Write-Verbose -Verbose "repo not found" + } + displayName: 'Set repo Root' + env: + ob_restore_phase: ${{ parameters.ob_restore_phase }} diff --git a/.pipelines/templates/shouldSign.yml b/.pipelines/templates/shouldSign.yml new file mode 100644 index 00000000000..551297f3aaa --- /dev/null +++ b/.pipelines/templates/shouldSign.yml @@ -0,0 +1,30 @@ +parameters: +- name: ob_restore_phase + type: boolean + default: true + +steps: +- powershell: | + $shouldSign = $true + $authenticodeCert = 'CP-230012' + $msixCert = 'CP-230012' + if($env:IS_DAILY -eq 'true') + { + $authenticodeCert = 'CP-460906' + } + if($env:SKIP_SIGNING -eq 'Yes') + { + $shouldSign = $false + } + $vstsCommandString = "vso[task.setvariable variable=SHOULD_SIGN]$($shouldSign.ToString().ToLowerInvariant())" + Write-Host "sending " + $vstsCommandString + Write-Host "##$vstsCommandString" + $vstsCommandString = "vso[task.setvariable variable=MSIX_CERT]$($msixCert)" + Write-Host "sending " + $vstsCommandString + Write-Host "##$vstsCommandString" + $vstsCommandString = "vso[task.setvariable variable=AUTHENTICODE_CERT]$($authenticodeCert)" + Write-Host "sending " + $vstsCommandString + Write-Host "##$vstsCommandString" + displayName: 'Set SHOULD_SIGN Variable' + env: + ob_restore_phase: ${{ parameters.ob_restore_phase }} diff --git a/.pipelines/templates/step/finalize.yml b/.pipelines/templates/step/finalize.yml new file mode 100644 index 00000000000..78e0341c829 --- /dev/null +++ b/.pipelines/templates/step/finalize.yml @@ -0,0 +1,6 @@ +# This was used before migrating to OneBranch to deal with one of the SDL taks from failing with a warning instead of an error. +steps: +- pwsh: | + throw "Jobs with an Issue will not work for release. Please fix the issue and try again." + displayName: Check for SucceededWithIssues + condition: eq(variables['Agent.JobStatus'],'SucceededWithIssues') diff --git a/.pipelines/templates/testartifacts.yml b/.pipelines/templates/testartifacts.yml new file mode 100644 index 00000000000..751c9d5a53b --- /dev/null +++ b/.pipelines/templates/testartifacts.yml @@ -0,0 +1,138 @@ +jobs: +- job: build_testartifacts_win + variables: + - name: runCodesignValidationInjection + value: false + - name: NugetSecurityAnalysisWarningLevel + value: none + - group: DotNetPrivateBuildAccess + - name: ob_outputDirectory + value: '$(Build.ArtifactStagingDirectory)/ONEBRANCH_ARTIFACT' + - name: ob_sdl_tsa_configFile + value: $(Build.SourcesDirectory)\PowerShell\.config\tsaoptions.json + - name: ob_sdl_credscan_suppressionsFile + value: $(Build.SourcesDirectory)\PowerShell\.config\suppress.json + - name: ob_sdl_codeSignValidation_excludes + value: '-|**\*.ps1;-|**\*.psm1;-|**\*.ps1xml;-|**\*.psd1;-|**\*.exe;-|**\*.dll;-|**\*.cdxml' + + displayName: Build windows test artifacts + condition: succeeded() + pool: + type: windows + steps: + - checkout: self + clean: true + env: + ob_restore_phase: true + + - template: /.pipelines/templates/SetVersionVariables.yml@self + parameters: + ReleaseTagVar: $(ReleaseTagVar) + + - template: /.pipelines/templates/insert-nuget-config-azfeed.yml@self + parameters: + repoRoot: $(RepoRoot) + ob_restore_phase: true + + - template: /.pipelines/templates/install-dotnet.yml@self + + - pwsh: | + New-Item -Path '$(ob_outputDirectory)' -ItemType Directory -Force + Import-Module $(Build.SourcesDirectory)/PowerShell/build.psm1 + function BuildTestPackage([string] $runtime) + { + Write-Verbose -Verbose "Starting to build package for $runtime" + New-TestPackage -Destination $(System.ArtifactsDirectory) -Runtime $runtime + if (-not (Test-Path $(System.ArtifactsDirectory)/TestPackage.zip)) + { + throw "Test Package was not found at: $(System.ArtifactsDirectory)" + } + switch ($runtime) + { + win7-x64 { $packageName = "TestPackage-win-x64.zip" } + win7-x86 { $packageName = "TestPackage-win-x86.zip" } + win-arm64 { $packageName = "TestPackage-win-arm64.zip" } + } + Rename-Item $(System.ArtifactsDirectory)/TestPackage.zip $packageName + ## Write-Host "##vso[artifact.upload containerfolder=testArtifacts;artifactname=testArtifacts]$(System.ArtifactsDirectory)/$packageName" + + Copy-Item -Path $(System.ArtifactsDirectory)/$packageName -Destination $(ob_outputDirectory) -Force -Verbose + } + BuildTestPackage -runtime win7-x64 + BuildTestPackage -runtime win7-x86 + BuildTestPackage -runtime win-arm64 + displayName: Build test package and upload + retryCountOnTaskFailure: 1 + env: + ob_restore_phase: true + + - pwsh: | + Write-Host "This doesn't do anything but make the build phase run." + displayName: Dummy build task + + +- job: build_testartifacts_nonwin + variables: + - name: runCodesignValidationInjection + value: false + - name: NugetSecurityAnalysisWarningLevel + value: none + - group: DotNetPrivateBuildAccess + - name: ob_outputDirectory + value: '$(Build.ArtifactStagingDirectory)/ONEBRANCH_ARTIFACT' + displayName: Build non-windows test artifacts + condition: succeeded() + pool: + type: linux + steps: + - checkout: self + clean: true + env: + ob_restore_phase: true + + - template: /.pipelines/templates/SetVersionVariables.yml@self + parameters: + ReleaseTagVar: $(ReleaseTagVar) + + - template: /.pipelines/templates/insert-nuget-config-azfeed.yml@self + parameters: + repoRoot: $(Build.SourcesDirectory)/PowerShell + ob_restore_phase: true + + - template: /.pipelines/templates/install-dotnet.yml@self + + - pwsh: | + New-Item -Path '$(ob_outputDirectory)' -ItemType Directory -Force + Import-Module $(Build.SourcesDirectory)/PowerShell/build.psm1 + function BuildTestPackage([string] $runtime) + { + Write-Verbose -Verbose "Starting to build package for $runtime" + New-TestPackage -Destination $(System.ArtifactsDirectory) -Runtime $runtime + if (-not (Test-Path $(System.ArtifactsDirectory)/TestPackage.zip)) + { + throw "Test Package was not found at: $(System.ArtifactsDirectory)" + } + switch ($runtime) + { + linux-x64 { $packageName = "TestPackage-linux-x64.zip" } + linux-arm { $packageName = "TestPackage-linux-arm.zip" } + linux-arm64 { $packageName = "TestPackage-linux-arm64.zip" } + osx-x64 { $packageName = "TestPackage-macOS.zip" } + linux-musl-x64 { $packageName = "TestPackage-alpine-x64.zip"} + } + Rename-Item $(System.ArtifactsDirectory)/TestPackage.zip $packageName + Copy-Item -Path $(System.ArtifactsDirectory)/$packageName -Destination $(ob_outputDirectory) -Force -Verbose + } + BuildTestPackage -runtime linux-x64 + BuildTestPackage -runtime linux-arm + BuildTestPackage -runtime linux-arm64 + BuildTestPackage -runtime osx-x64 + BuildTestPackage -runtime linux-musl-x64 + displayName: Build test package and upload + retryCountOnTaskFailure: 1 + env: + ob_restore_phase: true + + - pwsh: | + Write-Host "This doesn't do anything but make the build phase run." + displayName: Dummy build task diff --git a/.pipelines/templates/uploadToAzure.yml b/.pipelines/templates/uploadToAzure.yml new file mode 100644 index 00000000000..20842de81cc --- /dev/null +++ b/.pipelines/templates/uploadToAzure.yml @@ -0,0 +1,433 @@ +jobs: +- job: upload_packages + displayName: Upload packages + condition: succeeded() + pool: + type: windows + variables: + - name: ob_sdl_sbom_enabled + value: true + - name: runCodesignValidationInjection + value: false + - name: NugetSecurityAnalysisWarningLevel + value: none + - name: DOTNET_NOLOGO + value: 1 + - name: ob_outputDirectory + value: '$(Build.ArtifactStagingDirectory)/ONEBRANCH_ARTIFACT' + - name: ob_sdl_codeSignValidation_enabled + value: false + - name: ob_sdl_binskim_enabled + value: false + - name: ob_sdl_tsa_configFile + value: $(Build.SourcesDirectory)\PowerShell\.config\tsaoptions.json + - name: ob_sdl_credscan_suppressionsFile + value: $(Build.SourcesDirectory)\PowerShell\.config\suppress.json + - name: ob_sdl_codeql_compiled_enabled + value: false + - group: 'Azure Blob variable group' + + steps: + - checkout: self + clean: true + env: + ob_restore_phase: true # This ensures checkout is done at the beginning of the restore phase + + - template: /.pipelines/templates/SetVersionVariables.yml@self + parameters: + ReleaseTagVar: $(ReleaseTagVar) + CreateJson: no + + - template: /.pipelines/templates/release-SetReleaseTagandContainerName.yml@self + + - template: /.pipelines/templates/cloneToOfficialPath.yml@self + + - pwsh: | + Get-ChildItem Env: | Out-String -width 9999 -Stream | write-Verbose -Verbose + displayName: 'Capture Environment Variables' + + - pwsh: | + New-Item -Path '$(Build.ArtifactStagingDirectory)/downloads' -ItemType Directory -Force + displayName: Create downloads directory + + - task: DownloadPipelineArtifact@2 + inputs: + buildType: 'current' + artifact: drop_linux_package_deb + itemPattern: '**/*.deb' + targetPath: '$(Build.ArtifactStagingDirectory)/downloads' + displayName: Download deb package + + - task: DownloadPipelineArtifact@2 + inputs: + buildType: 'current' + artifact: drop_linux_package_fxdependent + itemPattern: '**/*.tar.gz' + targetPath: '$(Build.ArtifactStagingDirectory)/downloads' + displayName: Download linux fxd package + + - task: DownloadPipelineArtifact@2 + inputs: + buildType: 'current' + artifact: drop_linux_package_mariner_arm64 + itemPattern: '**/*.rpm' + targetPath: '$(Build.ArtifactStagingDirectory)/downloads' + displayName: Download linux mariner arm64 package + + - task: DownloadPipelineArtifact@2 + inputs: + buildType: 'current' + artifact: drop_linux_package_mariner_x64 + itemPattern: '**/*.rpm' + targetPath: '$(Build.ArtifactStagingDirectory)/downloads' + displayName: Download linux mariner x64 package + + - task: DownloadPipelineArtifact@2 + inputs: + buildType: 'current' + artifact: drop_linux_package_minSize + itemPattern: '**/*.tar.gz' + targetPath: '$(Build.ArtifactStagingDirectory)/downloads' + displayName: Download linux minSize package + + - task: DownloadPipelineArtifact@2 + inputs: + buildType: 'current' + artifact: drop_linux_package_rpm + itemPattern: '**/*.rpm' + targetPath: '$(Build.ArtifactStagingDirectory)/downloads' + displayName: Download linux rpm package + + - task: DownloadPipelineArtifact@2 + inputs: + buildType: 'current' + artifact: drop_linux_package_tar + itemPattern: '**/*.tar.gz' + targetPath: '$(Build.ArtifactStagingDirectory)/downloads' + displayName: Download linux tar package + + - task: DownloadPipelineArtifact@2 + inputs: + buildType: 'current' + artifact: drop_linux_package_tar_alpine + itemPattern: '**/*.tar.gz' + targetPath: '$(Build.ArtifactStagingDirectory)/downloads' + displayName: Download linux alpine tar package + + - task: DownloadPipelineArtifact@2 + inputs: + buildType: 'current' + artifact: drop_linux_package_tar_alpine_fxd + itemPattern: '**/*.tar.gz' + targetPath: '$(Build.ArtifactStagingDirectory)/downloads' + displayName: Download linux alpine fxd tar package + + - task: DownloadPipelineArtifact@2 + inputs: + buildType: 'current' + artifact: drop_linux_package_tar_arm + itemPattern: '**/*.tar.gz' + targetPath: '$(Build.ArtifactStagingDirectory)/downloads' + displayName: Download linux arm32 tar package + + - task: DownloadPipelineArtifact@2 + inputs: + buildType: 'current' + artifact: drop_linux_package_tar_arm64 + itemPattern: '**/*.tar.gz' + targetPath: '$(Build.ArtifactStagingDirectory)/downloads' + displayName: Download linux arm64 tar package + + - task: DownloadPipelineArtifact@2 + inputs: + buildType: 'current' + artifact: drop_nupkg_build_nupkg + itemPattern: '**/*.nupkg' + targetPath: '$(Build.ArtifactStagingDirectory)/downloads' + displayName: Download nupkgs + + - task: DownloadPipelineArtifact@2 + inputs: + buildType: 'current' + artifact: drop_windows_package_package_win_arm64 + itemPattern: | + **/*.msi + **/*.msix + **/*.zip + **/*.exe + targetPath: '$(Build.ArtifactStagingDirectory)/downloads' + displayName: Download windows arm64 packages + + - task: DownloadPipelineArtifact@2 + inputs: + buildType: 'current' + artifact: drop_windows_package_package_win_fxdependent + itemPattern: '**/*.zip' + targetPath: '$(Build.ArtifactStagingDirectory)/downloads' + displayName: Download windows fxdependent packages + + - task: DownloadPipelineArtifact@2 + inputs: + buildType: 'current' + artifact: drop_windows_package_package_win_fxdependentWinDesktop + itemPattern: '**/*.zip' + targetPath: '$(Build.ArtifactStagingDirectory)/downloads' + displayName: Download windows fxdependentWinDesktop packages + + - task: DownloadPipelineArtifact@2 + inputs: + buildType: 'current' + artifact: drop_windows_package_package_win_minsize + itemPattern: '**/*.zip' + targetPath: '$(Build.ArtifactStagingDirectory)/downloads' + displayName: Download windows minsize packages + + - task: DownloadPipelineArtifact@2 + inputs: + buildType: 'current' + artifact: drop_windows_package_package_win_x64 + itemPattern: | + **/*.msi + **/*.msix + **/*.zip + **/*.exe + targetPath: '$(Build.ArtifactStagingDirectory)/downloads' + displayName: Download windows x64 packages + + - task: DownloadPipelineArtifact@2 + inputs: + buildType: 'current' + artifact: drop_windows_package_package_win_x86 + itemPattern: | + **/*.msi + **/*.msix + **/*.zip + **/*.exe + targetPath: '$(Build.ArtifactStagingDirectory)/downloads' + displayName: Download windows x86 packages + + - task: DownloadPipelineArtifact@2 + inputs: + buildType: 'current' + artifact: macos-pkgs + itemPattern: | + **/*.tar.gz + targetPath: '$(Build.ArtifactStagingDirectory)/downloads' + displayName: Download macos tar packages + + - task: DownloadPipelineArtifact@2 + inputs: + buildType: 'current' + artifact: drop_mac_package_sign_package_macos_arm64 + itemPattern: | + **/*.pkg + targetPath: '$(Build.ArtifactStagingDirectory)/downloads' + displayName: Download macos arm packages + + - task: DownloadPipelineArtifact@2 + inputs: + buildType: 'current' + artifact: drop_mac_package_sign_package_macos_x64 + itemPattern: | + **/*.pkg + targetPath: '$(Build.ArtifactStagingDirectory)/downloads' + displayName: Download macos x64 packages + + - task: DownloadPipelineArtifact@2 + inputs: + buildType: 'current' + artifact: drop_msixbundle_CreateMSIXBundle + itemPattern: | + **/*.msixbundle + targetPath: '$(Build.ArtifactStagingDirectory)/downloads' + displayName: Download MSIXBundle + + - pwsh: | + Get-ChildItem '$(Build.ArtifactStagingDirectory)/downloads' | Select-Object -ExpandProperty FullName + displayName: 'Capture downloads' + + - pwsh: | + Write-Verbose -Verbose "Copying Github Release files in $(Build.ArtifactStagingDirectory)/downloads to use in Release Pipeline" + + Write-Verbose -Verbose "Creating output directory for GitHub Release files: $(ob_outputDirectory)/GitHubPackages" + New-Item -Path $(ob_outputDirectory)/GitHubPackages -ItemType Directory -Force + Get-ChildItem -Path "$(Build.ArtifactStagingDirectory)/downloads/*" -Recurse | + Where-Object { $_.Extension -notin '.msix', '.nupkg' -and $_.Name -notmatch '-gc'} | + Copy-Item -Destination $(ob_outputDirectory)/GitHubPackages -Recurse -Verbose + + Write-Verbose -Verbose "Creating output directory for NuGet packages: $(ob_outputDirectory)/NuGetPackages" + New-Item -Path $(ob_outputDirectory)/NuGetPackages -ItemType Directory -Force + Get-ChildItem -Path "$(Build.ArtifactStagingDirectory)/downloads/*" -Recurse | + Where-Object { $_.Extension -eq '.nupkg' } | + Copy-Item -Destination $(ob_outputDirectory)/NuGetPackages -Recurse -Verbose + displayName: Copy downloads to Artifacts + + - pwsh: | + # Create output directory for packages which have been uploaded to blob storage + New-Item -Path $(Build.ArtifactStagingDirectory)/uploaded -ItemType Directory -Force + displayName: Create output directory for packages + + - task: AzurePowerShell@5 + displayName: Upload packages to blob + inputs: + azureSubscription: az-blob-cicd-infra + scriptType: inlineScript + azurePowerShellVersion: LatestVersion + pwsh: true + inline: | + $downloadsDirectory = '$(Build.ArtifactStagingDirectory)/downloads' + $uploadedDirectory = '$(Build.ArtifactStagingDirectory)/uploaded' + $storageAccountName = "pscoretestdata" + $containerName = $env:AZUREVERSION + + Write-Verbose -Verbose "Uploading packages to blob storage account: $storageAccountName container: $containerName" + + $context = New-AzStorageContext -StorageAccountName $storageAccountName -UseConnectedAccount + + # Create the blob container if it doesn't exist + $containerExists = Get-AzStorageContainer -Name $containerName -Context $context -ErrorAction SilentlyContinue + if (-not $containerExists) { + $null = New-AzStorageContainer -Name $containerName -Context $context + Write-Host "Blob container $containerName created successfully." + } + + $gcPackages = Get-ChildItem -Path $downloadsDirectory -Filter "powershell*gc.*" + Write-Verbose -Verbose "gc files to upload." + $gcPackages | Write-Verbose -Verbose + $gcContainerName = "$containerName-gc" + # Create the blob container if it doesn't exist + $containerExists = Get-AzStorageContainer -Name $gcContainerName -Context $context -ErrorAction SilentlyContinue + if (-not $containerExists) { + $null = New-AzStorageContainer -Name $gcContainerName -Context $context + Write-Host "Blob container $gcContainerName created successfully." + } + + $gcPackages | ForEach-Object { + $blobName = "${_.Name}" + Write-Verbose -Verbose "Uploading $($_.FullName) to $gcContainerName/$blobName" + $null = Set-AzStorageBlobContent -File $_.FullName -Container $gcContainerName -Blob $blobName -Context $context + # Move to folder to we wont upload again + Move-Item -Path $_.FullName -Destination $uploadedDirectory -Force -Verbose + } + + $nupkgFiles = Get-ChildItem -Path $downloadsDirectory -Filter "*.nupkg" | Where-Object { $_.Name -notlike "powershell*.nupkg" } + + # create a SHA512 checksum file for each nupkg files + + $checksums = $nupkgFiles | + ForEach-Object { + Write-Verbose -Verbose "Generating checksum file for $($_.FullName)" + $packageName = $_.Name + $hash = (Get-FileHash -Path $_.FullName -Algorithm SHA256).Hash.ToLower() + # the '*' before the packagename signifies it is a binary + "$hash *$packageName" + } + + $checksums | Out-File -FilePath "$downloadsDirectory\SHA512SUMS" -Force + $fileContent = Get-Content -Path "$downloadsDirectory\SHA512SUMS" -Raw | Out-String + Write-Verbose -Verbose -Message $fileContent + + Write-Verbose -Verbose "nupkg files to upload." + $nupkgFiles += (Get-Item "$downloadsDirectory\SHA512SUMS") + $nupkgFiles | Write-Verbose -Verbose + $nugetContainerName = "$containerName-nuget" + # Create the blob container if it doesn't exist + $containerExists = Get-AzStorageContainer -Name $nugetContainerName -Context $context -ErrorAction SilentlyContinue + if (-not $containerExists) { + $null = New-AzStorageContainer -Name $nugetContainerName -Context $context + Write-Host "Blob container $nugetContainerName created successfully." + } + + $nupkgFiles | ForEach-Object { + $blobName = $_.Name + Write-Verbose -Verbose "Uploading $($_.FullName) to $nugetContainerName/$blobName" + $null = Set-AzStorageBlobContent -File $_.FullName -Container $nugetContainerName -Blob $blobName -Context $context + # Move to folder to we wont upload again + Move-Item -Path $_.FullName -Destination $uploadedDirectory -Force -Verbose + } + + $globaltoolFiles = Get-ChildItem -Path $downloadsDirectory -Filter "powershell*.nupkg" + # create a SHA512 checksum file for each nupkg files + + $checksums = $globaltoolFiles | + ForEach-Object { + Write-Verbose -Verbose "Generating checksum file for $($_.FullName)" + $packageName = $_.Name + $hash = (Get-FileHash -Path $_.FullName -Algorithm SHA256).Hash.ToLower() + # the '*' before the packagename signifies it is a binary + "$hash *$packageName" + } + + New-Item -Path "$downloadsDirectory\globaltool" -ItemType Directory -Force + $checksums | Out-File -FilePath "$downloadsDirectory\globaltool\SHA512SUMS" -Force + $fileContent = Get-Content -Path "$downloadsDirectory\globaltool\SHA512SUMS" -Raw | Out-String + Write-Verbose -Verbose -Message $fileContent + + Write-Verbose -Verbose "globaltool files to upload." + $globaltoolFiles += Get-Item ("$downloadsDirectory\globaltool\SHA512SUMS") + $globaltoolFiles | Write-Verbose -Verbose + $globaltoolContainerName = "$containerName-nuget" + $globaltoolFiles | ForEach-Object { + $blobName = "globaltool/" + $_.Name + $globaltoolContainerName = "$containerName-nuget" + Write-Verbose -Verbose "Uploading $($_.FullName) to $globaltoolContainerName/$blobName" + $null = Set-AzStorageBlobContent -File $_.FullName -Container $globaltoolContainerName -Blob $blobName -Context $context + # Move to folder to we wont upload again + Move-Item -Path $_.FullName -Destination $uploadedDirectory -Force + } + + # To use -Include parameter, we need to use \* to get all files + $privateFiles = Get-ChildItem -Path $downloadsDirectory\* -Include @("*.msix", "*.exe") + Write-Verbose -Verbose "private files to upload." + $privateFiles | Write-Verbose -Verbose + $privateContainerName = "$containerName-private" + # Create the blob container if it doesn't exist + $containerExists = Get-AzStorageContainer -Name $privateContainerName -Context $context -ErrorAction SilentlyContinue + if (-not $containerExists) { + $null = New-AzStorageContainer -Name $privateContainerName -Context $context + Write-Host "Blob container $privateContainerName created successfully." + } + + $privateFiles | ForEach-Object { + $blobName = $_.Name + Write-Verbose -Verbose "Uploading $($_.FullName) to $privateContainerName/$blobName" + $null = Set-AzStorageBlobContent -File $_.FullName -Container $privateContainerName -Blob $blobName -Context $context + # Move to folder to we wont upload again + Move-Item -Path $_.FullName -Destination $uploadedDirectory -Force -Verbose + } + + # To use -Include parameter, we need to use \* to get all files + $files = Get-ChildItem -Path $downloadsDirectory\* -Include @("*.deb", "*.tar.gz", "*.rpm", "*.msi", "*.zip", "*.pkg") + Write-Verbose -Verbose "files to upload." + $files | Write-Verbose -Verbose + + $files | ForEach-Object { + $blobName = $_.Name + Write-Verbose -Verbose "Uploading $($_.FullName) to $containerName/$blobName" + $null = Set-AzStorageBlobContent -File $_.FullName -Container $containerName -Blob $blobName -Context $context + Write-Host "File $blobName uploaded to $containerName container." + Move-Item -Path $_.FullName -Destination $uploadedDirectory -Force -Verbose + } + + $msixbundleFiles = Get-ChildItem -Path $downloadsDirectory -Filter "*.msixbundle" + + $containerName = '$(OutputVersion.AzureVersion)-private' + $storageAccount = '$(StorageAccount)' + + $storageContext = New-AzStorageContext -StorageAccountName $storageAccount -UseConnectedAccount + + if ($msixbundleFiles) { + $bundleFile = $msixbundleFiles[0].FullName + $blobName = $msixbundleFiles[0].Name + + $existing = Get-AzStorageBlob -Container $containerName -Blob $blobName -Context $storageContext -ErrorAction Ignore + if ($existing) { + Write-Verbose -Verbose "MSIX bundle already exists at '$storageAccount/$containerName/$blobName', removing first." + $existing | Remove-AzStorageBlob -ErrorAction Stop -Verbose + } + + Write-Verbose -Verbose "Uploading $bundleFile to $containerName/$blobName" + Set-AzStorageBlobContent -File $bundleFile -Container $containerName -Blob $blobName -Context $storageContext -Force + } else { + throw "MSIXBundle not found in $downloadsDirectory" + } diff --git a/.pipelines/templates/variable/release-shared.yml b/.pipelines/templates/variable/release-shared.yml new file mode 100644 index 00000000000..325f72224f5 --- /dev/null +++ b/.pipelines/templates/variable/release-shared.yml @@ -0,0 +1,42 @@ +parameters: + - name: REPOROOT + type: string + default: $(Build.SourcesDirectory)\PowerShell + - name: SBOM + type: boolean + default: false + - name: RELEASETAG + type: string + default: 'Not Initialized' + - name: VERSION + type: string + default: 'Not Initialized' + +variables: + - name: ob_signing_setup_enabled + value: false + - name: ob_sdl_sbom_enabled + value: ${{ parameters.SBOM }} + - name: runCodesignValidationInjection + value: false + - name: DOTNET_NOLOGO + value: 1 + - group: 'mscodehub-code-read-akv' + - group: 'Azure Blob variable group' + - group: 'GitHubTokens' + - name: ob_outputDirectory + value: '$(Build.ArtifactStagingDirectory)/ONEBRANCH_ARTIFACT' + - name: ob_sdl_codeSignValidation_enabled + value: false + - name: ob_sdl_binskim_enabled + value: false + - name: ob_sdl_tsa_configFile + value: ${{ parameters.REPOROOT }}\.config\tsaoptions.json + - name: ob_sdl_credscan_suppressionsFile + value: ${{ parameters.REPOROOT }}\.config\suppress.json + - name: ob_sdl_codeql_compiled_enabled + value: false + - name: ReleaseTag + value: ${{ parameters.RELEASETAG }} + - name: Version + value: ${{ parameters.VERSION }} diff --git a/.pipelines/templates/windows-hosted-build.yml b/.pipelines/templates/windows-hosted-build.yml new file mode 100644 index 00000000000..44b83ad20aa --- /dev/null +++ b/.pipelines/templates/windows-hosted-build.yml @@ -0,0 +1,325 @@ +parameters: + Architecture: 'x64' + BuildConfiguration: 'release' + JobName: 'build_windows' + +jobs: +- job: build_windows_${{ parameters.Architecture }}_${{ parameters.BuildConfiguration }} + displayName: Build_Windows_${{ parameters.Architecture }}_${{ parameters.BuildConfiguration }} + condition: succeeded() + pool: + type: windows + variables: + - name: runCodesignValidationInjection + value: false + - name: NugetSecurityAnalysisWarningLevel + value: none + - name: DOTNET_NOLOGO + value: 1 + - group: DotNetPrivateBuildAccess + - group: certificate_logical_to_actual + - name: ob_outputDirectory + value: '$(Build.ArtifactStagingDirectory)/ONEBRANCH_ARTIFACT' + - name: ob_sdl_codeSignValidation_enabled + value: false + - name: ob_sdl_binskim_enabled + value: true + - name: ob_sdl_tsa_configFile + value: $(Build.SourcesDirectory)\PowerShell\.config\tsaoptions.json + - name: ob_sdl_credscan_suppressionsFile + value: $(Build.SourcesDirectory)\PowerShell\.config\suppress.json + - name: Architecture + value: ${{ parameters.Architecture }} + - name: BuildConfiguration + value: ${{ parameters.BuildConfiguration }} + - name: ob_sdl_sbom_packageName + value: 'Microsoft.Powershell.Windows.${{ parameters.Architecture }}' + # We add this manually, so we need it disabled the OneBranch auto-injected one. + - name: ob_sdl_codeql_compiled_enabled + value: false + + steps: + - checkout: self + clean: true + env: + ob_restore_phase: true # This ensures checkout is done at the beginning of the restore phase + + - template: /.pipelines/templates/SetVersionVariables.yml@self + parameters: + ReleaseTagVar: $(ReleaseTagVar) + + - template: /.pipelines/templates/cloneToOfficialPath.yml@self + + - template: /.pipelines/templates/insert-nuget-config-azfeed.yml@self + parameters: + repoRoot: $(PowerShellRoot) + + - task: CodeQL3000Init@0 # Add CodeQL Init task right before your 'Build' step. + condition: eq(variables['CODEQL_ENABLED'], 'true') + env: + ob_restore_phase: true # Set ob_restore_phase to run this step before '🔒 Setup Signing' step. + inputs: + Enabled: true + # AnalyzeInPipeline: false = upload results + # AnalyzeInPipeline: true = do not upload results + AnalyzeInPipeline: false + Language: csharp + + - template: /.pipelines/templates/install-dotnet.yml@self + + - pwsh: | + $runtime = switch ($env:Architecture) + { + "x64" { "win7-x64" } + "x86" { "win7-x86" } + "arm64" { "win-arm64" } + "fxdependent" { "fxdependent" } + "fxdependentWinDesktop" { "fxdependent-win-desktop" } + } + + $params = @{} + if ($env:BuildConfiguration -eq 'minSize') { + $params['ForMinimalSize'] = $true + } + + $vstsCommandString = "vso[task.setvariable variable=Runtime]$runtime" + Write-Host ("sending " + $vstsCommandString) + Write-Host "##$vstsCommandString" + + Write-Verbose -Message "Building PowerShell with Runtime: $runtime for '$env:BuildConfiguration' configuration" + Import-Module -Name $(PowerShellRoot)/build.psm1 -Force + $buildWithSymbolsPath = New-Item -ItemType Directory -Path $(Pipeline.Workspace)/Symbols_$(Architecture) -Force + + Start-PSBootstrap -Scenario Package + $null = New-Item -ItemType Directory -Path $buildWithSymbolsPath -Force -Verbose + + $ReleaseTagParam = @{} + + if ($env:RELEASETAGVAR) { + $ReleaseTagParam['ReleaseTag'] = $env:RELEASETAGVAR + } + + Start-PSBuild -Runtime $runtime -Configuration Release -Output $buildWithSymbolsPath -Clean -PSModuleRestore @params @ReleaseTagParam + + $refFolderPath = Join-Path $buildWithSymbolsPath 'ref' + Write-Verbose -Verbose "refFolderPath: $refFolderPath" + $outputPath = Join-Path '$(ob_outputDirectory)' 'psoptions' + $null = New-Item -ItemType Directory -Path $outputPath -Force + $psOptPath = "$outputPath/psoptions.json" + Save-PSOptions -PSOptionsPath $psOptPath + + Write-Verbose -Verbose "Verifying pdbs exist in build folder" + $pdbs = Get-ChildItem -Path $buildWithSymbolsPath -Recurse -Filter *.pdb + if ($pdbs.Count -eq 0) { + Write-Error -Message "No pdbs found in build folder" + } + else { + Write-Verbose -Verbose "Found $($pdbs.Count) pdbs in build folder" + $pdbs | ForEach-Object { + Write-Verbose -Verbose "Pdb: $($_.FullName)" + } + + $pdbs | Compress-Archive -DestinationPath "$(ob_outputDirectory)/symbols.zip" -Update + } + + Write-Verbose -Verbose "Completed building PowerShell for '$env:BuildConfiguration' configuration" + displayName: 'Build Windows Universal - $(Architecture)-$(BuildConfiguration) Symbols folder' + env: + __DOTNET_RUNTIME_FEED_KEY: $(RUNTIME_SOURCEFEED_KEY) + ob_restore_phase: true # Set ob_restore_phase to run this step before '🔒 Setup Signing' step. + + - pwsh: | + $runtime = switch ($env:Architecture) + { + "x64" { "win7-x64" } + "x86" { "win7-x86" } + "arm64" { "win-arm64" } + "fxdependent" { "fxdependent" } + "fxdependentWinDesktop" { "fxdependent-win-desktop" } + } + + Import-Module -Name $(PowerShellRoot)/build.psm1 -Force + Find-Dotnet + + ## Build global tool + Write-Verbose -Message "Building PowerShell global tool for Windows.x64" -Verbose + $globalToolCsProjDir = Join-Path $(PowerShellRoot) 'src' 'GlobalTools' 'PowerShell.Windows.x64' + Push-Location -Path $globalToolCsProjDir -Verbose + + $globalToolArtifactPath = Join-Path $(Build.SourcesDirectory) 'GlobalTool' + $vstsCommandString = "vso[task.setvariable variable=GlobalToolArtifactPath]${globalToolArtifactPath}" + Write-Host "sending " + $vstsCommandString + Write-Host "##$vstsCommandString" + + if ($env:RELEASETAGVAR) { + $ReleaseTagToUse = $env:RELEASETAGVAR -Replace '^v' + } + + Write-Verbose -Verbose "Building PowerShell global tool for Windows.x64 with cmdline: dotnet publish --no-self-contained --artifacts-path $globalToolArtifactPath /property:PackageVersion=$(Version) --configuration 'Release' /property:ReleaseTag=$ReleaseTagToUse" + dotnet publish --no-self-contained --artifacts-path $globalToolArtifactPath /property:PackageVersion=$(Version) --configuration 'Release' /property:ReleaseTag=$ReleaseTagToUse + $globalToolBuildModulePath = Join-Path $globalToolArtifactPath 'publish' 'PowerShell.Windows.x64' 'release' + Pop-Location + # do this to ensure everything gets signed. + Restore-PSModuleToBuild -PublishPath $globalToolBuildModulePath + + $buildWithSymbolsPath = Get-Item -Path "$(Pipeline.Workspace)/Symbols_$(Architecture)" + $refFolderPath = Join-Path $buildWithSymbolsPath 'ref' + Write-Verbose -Verbose "refFolderPath: $refFolderPath" + + # Copy reference assemblies + Copy-Item -Path $refFolderPath -Destination $globalToolBuildModulePath -Recurse -Force + + Write-Verbose -Verbose "clean unnecessary files in obj directory" + $objDir = Join-Path $globalToolArtifactPath 'obj' 'PowerShell.Windows.x64' 'release' + + $filesToKeep = @("apphost.exe", "PowerShell.Windows.x64.pdb", "PowerShell.Windows.x64.dll", "project.assets.json") + + # only four files are needed in obj folder for global tool packaging + Get-ChildItem -Path $objDir -File -Recurse | + Where-Object { -not $_.PSIsContainer } | + Where-Object { $_.name -notin $filesToKeep } | + Remove-Item -Verbose + + + displayName: 'Build Winx64 Global tool' + condition: and(succeeded(), eq(variables['Architecture'], 'fxdependent')) + env: + __DOTNET_RUNTIME_FEED_KEY: $(RUNTIME_SOURCEFEED_KEY) + ob_restore_phase: true # Set ob_restore_phase to run this step before '🔒 Setup Signing' step. + + - task: CodeQL3000Finalize@0 # Add CodeQL Finalize task right after your 'Build' step. + condition: eq(variables['CODEQL_ENABLED'], 'true') + env: + ob_restore_phase: true # Set ob_restore_phase to run this step before '🔒 Setup Signing' step. + + - pwsh: | + $platform = 'windows' + $vstsCommandString = "vso[task.setvariable variable=ArtifactPlatform]$platform" + Write-Host ("sending " + $vstsCommandString) + Write-Host "##$vstsCommandString" + displayName: Set artifact platform + + - template: /.pipelines/templates/obp-file-signing.yml@self + parameters: + binPath: '$(Pipeline.Workspace)/Symbols_$(Architecture)' + OfficialBuild: $(ps_official_build) + + ## first we sign all the files in the bin folder + - ${{ if eq(variables['Architecture'], 'fxdependent') }}: + - template: /.pipelines/templates/obp-file-signing.yml@self + parameters: + binPath: '$(GlobalToolArtifactPath)/publish/PowerShell.Windows.x64/release' + globalTool: 'true' + OfficialBuild: $(ps_official_build) + + - pwsh: | + Get-ChildItem '$(GlobalToolArtifactPath)/obj/PowerShell.Windows.x64/release' + displayName: Capture obj files + condition: and(succeeded(), eq(variables['Architecture'], 'fxdependent')) + + ## Now we sign couple of file from the obj folder which are needed for the global tool packaging + - task: onebranch.pipeline.signing@1 + displayName: Sign obj files + inputs: + command: 'sign' + signing_profile: external_distribution + files_to_sign: '**\*.dll;**\*.exe' + search_root: '$(GlobalToolArtifactPath)/obj/PowerShell.Windows.x64/release' + condition: and(succeeded(), eq(variables['Architecture'], 'fxdependent')) + + - pwsh: | + <# The way the packaging works is a bit tricky as when it is built, we cannot add the modules that come from gallery. + We have to use dotnet pack to build the nupkg and then expand it as a zip. + After expanding we restore the signed files for the modules from the gallery. + We also delete pdbs, content and contentFiles folder which are not necessary. + After that, we repack using Compress-Archive and rename it back to a nupkg. + #> + + Import-Module -Name $(PowerShellRoot)/build.psm1 -Force + Find-Dotnet + + $packagingStrings = Import-PowerShellDataFile "$(PowerShellRoot)\tools\packaging\packaging.strings.psd1" + + $outputPath = Join-Path '$(ob_outputDirectory)' 'globaltool' + $null = New-Item -ItemType Directory -Path $outputPath -Force + $globalToolCsProjDir = Join-Path $(PowerShellRoot) 'src' 'GlobalTools' 'PowerShell.Windows.x64' + Push-Location -Path $globalToolCsProjDir -Verbose + + if ($env:RELASETAGVAR) { + $ReleaseTagToUse = $env:RELASETAGVAR -Replace '^v' + } + + Write-Verbose -Verbose "Packing PowerShell global tool for Windows.x64 with cmdline: dotnet pack --output $outputPath --no-build --artifacts-path '$(GlobalToolArtifactPath)' /property:PackageVersion=$(Version) /property:PackageIcon=Powershell_64.png /property:Version=$(Version) /property:ReleaseTag=$ReleaseTagToUse" + + dotnet pack --output $outputPath --no-build --artifacts-path '$(GlobalToolArtifactPath)' /property:PackageVersion=$(Version) /property:PackageIcon=Powershell_64.png /property:Version=$(Version) /property:ReleaseTag=$ReleaseTagToUse + + Write-Verbose -Verbose "Deleting content and contentFiles folders from the nupkg" + + $nupkgs = Get-ChildItem -Path $outputPath -Filter powershell*.nupkg + + $nupkgName = $nupkgs.Name + $newName = $nupkgName -replace '(\.nupkg)$', '.zip' + Rename-Item -Path $nupkgs.FullName -NewName $newName + + $zipPath = Get-ChildItem -Path $outputPath -Filter powershell*.zip + + # Expand zip and remove content and contentFiles folders + Expand-Archive -Path $zipPath -DestinationPath "$outputPath\temp" -Force + + $modulesToCopy = @( + 'PowerShellGet' + 'PackageManagement' + 'Microsoft.PowerShell.PSResourceGet' + 'Microsoft.PowerShell.Archive' + 'PSReadLine' + 'Microsoft.PowerShell.ThreadJob' + ) + + $sourceModulePath = Join-Path '$(GlobalToolArtifactPath)' 'publish' 'PowerShell.Windows.x64' 'release' 'Modules' + $destModulesPath = Join-Path "$outputPath" 'temp' 'tools' 'net10.0' 'any' 'modules' + + $modulesToCopy | ForEach-Object { + $modulePath = Join-Path $sourceModulePath $_ + Copy-Item -Path $modulePath -Destination $destModulesPath -Recurse -Force + } + + # Copy ref assemblies + Copy-Item '$(Pipeline.Workspace)/Symbols_$(Architecture)/ref' "$outputPath\temp\tools\net10.0\any\ref" -Recurse -Force + + $contentPath = Join-Path "$outputPath\temp" 'content' + $contentFilesPath = Join-Path "$outputPath\temp" 'contentFiles' + + Remove-Item -Path $contentPath,$contentFilesPath -Recurse -Force + + # remove PDBs to reduce the size of the nupkg + Remove-Item -Path "$outputPath\temp\tools\net10.0\any\*.pdb" -Recurse -Force + + # create powershell.config.json + $config = [ordered]@{} + $config.Add("Microsoft.PowerShell:ExecutionPolicy", "RemoteSigned") + $config.Add("WindowsPowerShellCompatibilityModuleDenyList", @("PSScheduledJob", "BestPractices", "UpdateServices")) + + $configPublishPath = Join-Path "$outputPath" 'temp' 'tools' 'net10.0' 'any' "powershell.config.json" + Set-Content -Path $configPublishPath -Value ($config | ConvertTo-Json) -Force -ErrorAction Stop + + Compress-Archive -Path "$outputPath\temp\*" -DestinationPath "$outputPath\$nupkgName" -Force + + Remove-Item -Path "$outputPath\temp" -Recurse -Force + Remove-Item -Path $zipPath -Force + + if (-not (Test-Path "$outputPath\powershell.windows.x64.*.nupkg")) { + throw "Global tool package not found at $outputPath" + } + displayName: 'Pack Windows.x64 global tool' + condition: and(succeeded(), eq(variables['Architecture'], 'fxdependent')) + + - task: onebranch.pipeline.signing@1 + displayName: Sign nupkg files + inputs: + command: 'sign' + cp_code: 'CP-401405' + files_to_sign: '**\*.nupkg' + search_root: '$(ob_outputDirectory)\globaltool' + condition: and(succeeded(), eq(variables['Architecture'], 'fxdependent')) + + - template: /.pipelines/templates/step/finalize.yml@self diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 00000000000..222861c3415 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,4 @@ +{ + "tabWidth": 2, + "useTabs": false +} diff --git a/.spelling b/.spelling index d8a06f9798a..cc711f0aa5a 100644 --- a/.spelling +++ b/.spelling @@ -3,825 +3,88 @@ # global dictionary is at the start, file overrides afterwards # one word per line, to define a file override use ' - filename' # where filename is relative to this configuration file -ActiveDirectory -alpha.15 -alpha.7 -alpha.8 -alpha.9 -analyzing -AppImage -AppVeyor -artifact -artifacts -AssemblyLoadContext -behavior -behaviors -booleans -catalog -cataloged -CentOS -cmake -cmd -cmdlet -cmdlets -color -colors -config -ConsoleHost -ConvertFrom-Json -CoreCLR -CoreFX -crontab -crossgen -DevOps -DockerFile -DockerFiles -eBook -enum -env -exe -favor -favorite -frontload -FullCLR -Get-Acl -Get-AuthenticodeSignature -Get-ChildItem -Get-ComputerInfo -Get-PSSessionConfiguration -Get-WinEvent -github -hashtable -hashtables -homebrew -hotfix -init -Invoke-RestMethod -Invoke-WebRequest -json -labeled -lockfile -macOS -Microsoft.PowerShell.Archive -MS-PSRP -myget -New-PSSessionOption -New-PSTransportOption -NuGet -nuget -nunit -NUnit -nupkg -nuspec -OpenSSH -PackageManagement -param -parameterized -powershell -PowerShell -powershell.exe -PowerShellGet -ProgramFiles -ps1 -PSReadline -Register-EngineEvent -Register-PSSessionConfiguration -remoting -ResGen -RFCs -Runspace -runtimes -SecureString -Set-Acl -Set-AuthenticodeSignature -Set-ExecutionPolicy -ssh -StackOverflow -submodule -submodules -sudo -TeamCity -toolset -Unregister-Event -Unregister-PSSessionConfiguration -vscode -walkthrough -wget -whitespace -Win32 -WinRM -WSMan -xUnit +-title +0-powershell-crossplatform +0xfeeddeadbeef +100ms +1redone +1.final +2.x 2ae5d07 -codecov.io -microsoft.powershell.commands.diagnostics -microsoft.powershell.commands.management -microsoft.powershell.commands.utility -microsoft.powershell.consolehost -microsoft.powershell.coreclr.assemblyloadcontext -microsoft.powershell.coreclr.eventing -microsoft.powershell.psreadline -microsoft.powershell.security -microsoft.wsman.management -system.management.automation -microsoft.wsman.runtime -wsmansessionoption.cs -program.cs -convertto-xml -psobjects -argumentlist -comobject -debughandler -namedpipe +32-bit +4.final +64-bit +AAATechGuy +about_ +about_debuggers +about_jobs +about_Telemetry +about_PSDesiredStateConfiguration acl -authenticodesignature -utils.cs -credssp -ifdef'ed -cimsupport -cimsession -isnot -notcontains -snapin -sessionstateitem -sessionstatecontainer -childitem -showcommandinfo -psproxyjobs -throttlelimit -helpsystem -savehelp -helpproviderwithcache -helpproviderwithfullcache -utils -registryprovider -functionprovider +adamdriscoll +add-localgroupmember +add-ons +AddType.cs +adelton +adhoc aditya -patwardhan adityapatwardhan -ilya -sazonov - - demos/Azure/README.md -AzureRM.NetCore.Preview -AzureRM.Profile.NetCore.Preview -AzureRM.Resources.NetCore.Preview -ExcludeVersion -ProviderName - - demos/crontab/README.md -DayOfWeek -Get-CronJob -New-CronJob -Remove-CronJob -u - - demos/DSC/readme.md - - demos/install/README.md -download.sh - - demos/python/README.md -_script.ps1 -_script.ps1. - - demos/rest/README.md -rest.ps1 - - demos/SSHRemoting/README.md -_config -2kCbnhT2dUE6WCGgVJ8Hyfu1z2wE4lifaJXLO7QJy0Y -ComputerName -ComputerType -ConfigurationName -Enter-PSSession -HostName -KeyFilePath -KeyPath -New-PSSession -NoLogo -NoProfile -openssh-client -openssh-server -PasswordAuthentication -PSCredential -PSSessions -PubkeyAuthentication -RSAAuthentication -ssh.exe -sshd -sshd.exe -sshs -TestUser -UbuntuVM1 -UbuntuVM1s - - demos/SystemD/readme.md -Get-SystemDJournal -journalctl -SystemD - - docker/README.md -andschwa's -CurrentUser -hub.docker.com -microsoft -NanoServer-Insider -nanoserver-insider-powershell - - docs/building/internals.md -Catalog -src -powershell-unix -MSBuild - - docs/building/macos.md -preview3 - - docs/community/governance.md -Aiello -AngelCalvo -BrucePay -Calvo -DON'Ts -Hemant -HemantMahawar -joeyaiello -jpsnover -khansen00 -lzybkr -Mahawar -Payette -PRs -Snover -SteveL-MSFT - - docs/debugging/README.md -CmdletProviderClasses -CommandDiscovery -CommandSearch -ConsoleHostRunspaceInit -ConsoleHostUserInterface -ConsoleLineOutput -corehost -DisplayDataQuery -FileSystemProvider -FormatFileLoading -FormatViewBinding -LocationGlobber -MemberResolution -MshSnapinLoadUnload -OmniSharp -ParameterBinderBase -ParameterBinderController -ParameterBinding -PathResolution -PSDriveInfo -PSSnapInLoadUnload -RunspaceInit -SessionState -TypeConversion -TypeMatch -XTerm - - docs/dev-process/breaking-change-contract.md -cd -cdxml -int -p1 - - docs/FAQ.md -PoshCode -SS64.com -TypeGen -v6.0.0 - - docs/git/submodules.md -GoogleTest -superproject - - docs/installation/linux.md -OpenSUSE -TravisEz13 - - docs/installation/windows.md -Install-PowerShellRemoting -pwrshplugin.dll -System32 -Win8 -windir - - docs/KNOWNISSUES.md -cp -pipelining -psl-omi-provider -globbing -Register-WmiEvent -System.Management.Automation.SemanticVersion -System.Timers.Timer - - docs/learning-powershell/create-powershell-scripts.md -NetIP.ps1. -RemoteSigned - - docs/learning-powershell/debugging-from-commandline.md -_Debuggers -celsius -Set-PSBreakpoint -test.ps1 - - docs/learning-powershell/powershell-beginners-guide.md -dir -jen -LastWriteTime - - docs/learning-powershell/README.md -Lynda.com -Pluralsight -PowerShell.com -PowerShellMagazine.com -ScriptCenter -TechNet - - docs/learning-powershell/using-vscode.md -helloworld.ps1 -launch.json -OSs - - docs/learning-powershell/working-with-powershell-objects.md -ForEach-Object - - docs/maintainers/issue-management.md -Microsoft.PowerShell.Core -Microsoft.PowerShell.Management -Microsoft.PowerShell.Utility -omi - - docs/maintainers/pull-request-process.md -ci-system - - docs/maintainers/README.md -andschwa -daxian-dbw -Dongbo -mirichmo -Schwartzmeyer -Sergei -TravisEz13 -Vorobev -vors - - docs/maintainers/releasing.md -2012r2 -CHANGELOG.md -Dockerfiles -downlevel -Effing -PowerShellCore -Ronn -Toolset -v6 -WiX - - docs/testing-guidelines/PowerShellCoreTestStatus.md -Add-LocalGroupMember -add-on -adhoc -Clear-ItemProperty -Connect-PSSession -Connect-WSMan -ConvertFrom-Csv -ConvertFrom-SddlString -ConvertFrom-SecureString -ConvertFrom-StringData -ConvertTo-Csv -ConvertTo-Json -ConvertTo-SecureString -ConvertTo-Xml -Copy-ItemProperty -Debug-Runspace -Disable-LocalUser -Disable-PSBreakpoint -Disable-PSSessionConfiguration -Disable-PSTrace -Disable-PSWSManCombinedTrace -Disable-RunspaceDebug -Disable-WSManCredSSP -Disable-WSManTrace -Disconnect-PSSession -Disconnect-WSMan -Enable-LocalUser -Enable-PSBreakpoint -Enable-PSSessionConfiguration -Enable-PSTrace -Enable-PSWSManCombinedTrace -Enable-RunspaceDebug -Enable-WSManCredSSP -Enable-WSManTrace -Enter-PSHostProcess -Exit-PSHostProcess -Exit-PSSession -Export-BinaryMiLog -Export-Clixml -Export-Csv -Export-FormatData -Export-ModuleMember -Find-DscResource -Find-PackageProvider -Find-RoleCapability -Get-CimAssociatedInstance -Get-CimClass -Get-CimInstance -Get-CimSession -Get-EventSubscriber -Get-ExecutionPolicy -Get-FileHash -Get-FormatData -Get-InstalledModule -Get-InstalledScript -Get-ItemProperty -Get-ItemPropertyValue -Get-LocalGroup -Get-LocalGroupMember -Get-LocalUser -Get-LogProperties -Get-PackageProvider -Get-PackageSource -Get-PSBreakpoint -Get-PSCallStack -Get-PSDrive -Get-PSHostProcessInfo -Get-PSProvider -Get-PSReadlineKeyHandler -Get-PSReadlineOption -Get-PSRepository -Get-PSSession -Get-PSSessionCapability -Get-Runspace -Get-RunspaceDebug -Get-TimeZone -Get-TypeData -Get-UICulture -Get-WSManCredSSP -Get-WSManInstance -Import-BinaryMiLog -Import-Clixml -Import-Csv -Import-LocalizedData -Import-PackageProvider -Import-PowerShellDataFile -Install-PackageProvider -Invoke-CimMethod -Invoke-WSManAction -Move-ItemProperty -New-CimInstance -New-CimSession -New-CimSessionOption -New-FileCatalog -New-Guid -New-ItemProperty -New-LocalGroup -New-LocalUser -New-ModuleManifest -New-PSDrive -New-PSRoleCapabilityFile -New-PSSessionConfigurationFile -New-ScriptFileInfo -New-TemporaryFile -New-WinEvent -New-WSManInstance -New-WSManSessionOption -Receive-PSSession -Register-ArgumentCompleter -Register-CimIndicationEvent -Register-ObjectEvent -Register-PackageSource -Register-PSRepository -Remove-CimInstance -Remove-CimSession -Remove-ItemProperty -Remove-LocalGroup -Remove-LocalGroupMember -Remove-LocalUser -Remove-PSBreakpoint -Remove-PSDrive -Remove-PSReadlineKeyHandler -Remove-PSSession -Remove-TypeData -Remove-WSManInstance -Rename-ItemProperty -Rename-LocalGroup -Rename-LocalUser -Select-xml -Set-CimInstance -Set-ItemProperty -Set-LocalGroup -Set-LocalUser -Set-LogProperties -Set-PackageSource -Set-PSDebug -Set-PSReadlineKeyHandler -Set-PSReadlineOption -Set-PSRepository -Set-PSSessionConfiguration -Set-StrictMode -Set-TimeZone -Set-WSManInstance -Set-WSManQuickConfig -Test-FileCatalog -Test-ModuleManifest -Test-PSSessionConfigurationFile -Test-ScriptFileInfo -Test-WSMan -Unregister-PackageSource -Unregister-PSRepository -Update-FormatData -Update-ModuleManifest -Update-ScriptFileInfo -Update-TypeData - - docs/testing-guidelines/testing-guidelines.md -100ms -Api -build.psm1 -DotNet -Interop -MessageAnalyzer -Microsoft.PowerShell.Core -Microsoft.PowerShell.Diagnostics -Microsoft.PowerShell.Management -Microsoft.PowerShell.Security -Microsoft.PowerShell.Utility -NativeExecution -TabCompletion - - docs/testing-guidelines/TestRoadmap.md -corefx -DotCover -Downlevel -KPIs -loopback -MVPs -OpenCover -org -PowerBI -Syslog - - demos/Apache/readme.md -Get-ApacheModule -Get-ApacheVHost -New-ApacheVHost -Restart-ApacheHTTPserver - - docs/testing-guidelines/WritingPesterTests.md -FQErrorId -FullyQualifiedErrorId -nGet-ContentOut-String -nGet-MultiLineString -PSDefaultParameterValues-skip -ShouldShouldIt -StreamDescribeCIFeatureScenarioDescribeContextItContextContextBeforeAllAfterAllBeforeEachAfterEachshould -TestDrive -throw-testcasesItMockDescribe -HelpersCommon.psm1 -ErrorRecord - - README.md -Gitter -microsoft.com -msi -omnisharp-vscode -opencode -pkg -UserVoice - - src/libpsl-native/README.md -Ansi -C#'s -codepage -libpsl-native - - src/Microsoft.PowerShell.PSReadLine/en-US/PSReadline.md -_history.txt -_PSReadline -50ms -AddToHistoryHandler -AppData -BackgroundColor -BellStyle -BriefDescription -coloring -CompletionQueryItems -ConsoleColor -ConsoleKeyInfo -ContinuationPrompt -ContinuationPromptBackgroundColor -ContinuationPromptForegroundColor -DingDuration -DingTone -EditMode -EmphasisBackgroundColor -EmphasisForegroundColor -ErrorBackgroundColor -ErrorForegroundColor -ExtraPromptLineCount -ForegroundColor -ForwardWord -Func -HistoryNoDuplicates -HistorySavePath -HistorySaveStyle -HistorySearchBackward -HistorySearchCaseSensitive -HistorySearchCursorMovesToEnd -host.Name -Int32 -KillWord -MaximumHistoryCount -MaximumKillRingCount -Microsoft.PowerShell.KeyHandler -ResetTokenColors -ReverseSearchHistory -SaveAtExit -SaveIncrementally -SaveNothing -ScriptBlock -ShowToolTips -TokenClassification -TokenKind -ToString -ValidateAndAcceptLine -ValidationHandler -WordDelimiters - - src/Microsoft.PowerShell.SDK/README.md -metapackage -project.json - - src/Modules/README.md -ps1xml -psd1 -psm1 - - src/Modules/Shared/Pester/CHANGELOG.md -_be -AfterAll -AfterEach -ArgumentList -Assert-MockCalled -BeExactly -BeforeAll -BeforeEach -BeGreaterThan -BeLessThan -BeNullOrEmpty -BeOfType -beta2 -cleanup -CodeCoverage -DisableOldStyleAssertions -dynamicparam -eg -EnableExit -EnableLegacyAssertions -EnableLegacyExpectations -ErrorAction -ExcludeTagFilter -ExclusiveFilter -ExecutionContext -ExecutionPolicy -FunctionName -fynctions -Get-MockDynamicParameters -Get-TestDriveItem -GetDynamicParameters -GH-100 -GH-102 -GH-107 -GH-109 -GH-113 -GH-114 -GH-123 -GH-125 -GH-126 -GH-129 -GH-13 -GH-130 -GH-134 -GH-135 -GH-136 -GH-137 -GH-139 -GH-143 -GH-144 -GH-147 -GH-148 -GH-149 -GH-150 -GH-152 -GH-155 -GH-156 -GH-158 -GH-163 -GH-164 -GH-165 -GH-166 -GH-167 -GH-168 -GH-171 -GH-172 -GH-174 -GH-176 -GH-183 -GH-185 -GH-186 -GH-187 -GH-188 -GH-19 -GH-190 -GH-192 -GH-195 -GH-200 -GH-203 -GH-209 -GH-215 -GH-223 -GH-232 -GH-234 -GH-249 -GH-254 -GH-26 -GH-266 -GH-267 -GH-27 -GH-272 -GH-274 -GH-276 -GH-278 -GH-281 -GH-290 -GH-295 -GH-304 -GH-306 -GH-307 -GH-322 -GH-323 -GH-324 -GH-326 -GH-327 -GH-333 -GH-341 -GH-346 -GH-35 -GH-354 -GH-358 -GH-362 -GH-368 -GH-37 -GH-38 -GH-39 -GH-40 -GH-42 -GH-46 -GH-49 -GH-50 -GH-52 -GH-58 -GH-61 -GH-68 -GH-69 -GH-70 -GH-71 -GH-84 -GH-86 -GH-9 -GH-90 -GH-92 -GH-94 -GH-95 -GH-98 -GH-99 -InModuleScope -Invoke-Pester's -ISESteroids -LegacyNUnitXml -ModuleName -MyInvocation -nuget.exe -OutputFile -OutputFormat -OutputXml -ParameterFilter -PassThru -Passthu -pester.bat -Pester.bat -PesterException -PesterThrowFailureMessage -PSCommandPath -PSv3 -should.not -ShouldArgs -StrictMode -TestName -Tests.ps1. -v2 -v3 -v3.0 -Validate-Xml -Write-UsageForNewFixture - - src/Modules/Shared/Pester/README.md -hoc -pester-bdd-for-the-system-administrator -powershell-bdd-testing-pester-screencast -v1.0. - - src/powershell/README.md -powershell-unix - - src/TypeCatalogGen/README.md -TypeCatalogGen - - test/README.md -csharp -fullclr -shebang - - CHANGELOG.md -_ -_Jobs --Command --Exclude --File --Include --Title -0xfeeddeadbeef -acceptance +ADOPTERS.md +aetos382 +aiello +Aishat452 +al-cheb +alepauly +alexandair +alexjordan6 alpha.10 alpha.11 alpha.12 alpha.13 alpha.14 +alpha.15 alpha.16 alpha.17 alpha.18 -behavioral +alpha.7 +alpha.8 +alpha.9 +alternatestream +alvarodelvalle +amd64 +ananya26-vishnoi +andschwa +anmenaga +api +apis +APIScan +appimage +applocker +appveyor +appx +ArchitectureSensitiveAttribute +args +argumentlist +arm32 +arm64 +asp.net +ast.cs +assemblyloadcontext +AssemblyInfo +assessibility +AtariDreams +authenticode +authenticodesignature +azdevops +AzFileCopy +AzureFileCopy +azurerm.netcore.preview +azurerm.profile.netcore.preview +azurerm.resources.netcore.preview +backgrounded +backgrounding +backport +beatcracker bergmeister beta.1 beta.2 @@ -829,127 +92,1666 @@ beta.3 beta.4 beta.5 beta.6 -binding +beta.7 +beta.8 +beta.9 +beta.406 +beta.507 +beta2 +bgelens +Bhaal22 +BinaryFormatter +bjh7242 +bnot bool +bpayette +brcrista +breakpoint +brianbunke +britishben +brotli +brucepay +bugfix +build.json +build.psm1 +bulid +buildInfoJson +callmejoebob +CarloToso +catchable +cdxml +celsius +CentOS +CimDscParser +codeql-action +CGManifest +cgmanifest.json +cgmanifest +changelog +changelog.md +changelogs +changeset +changesets +channel9 +charltonstanley charset -cleanup -CodeOwner -ContentType -ConvertTo-Html -CoreConsoleHost +checkbox +checksum +chibi +childitem +ChuckieChen945 +ChrisLGardner +chrullrich +cimsession +cimsupport +ci.psm1 +cgmanifest +classlib +clear-itemproperty +cloudydino +cls +cmake +cmd +cmdlet +cmdletproviderclasses +cmdlets +codebase +codecov.io +codecoverage.zip +codefactor +CodeFormatter +codeowner +codepage +commanddiscovery +CommandInvocationIntrinsics +commandsearch +CommandSearcher +comobject +Compiler.cs +composability +computerinfo +ComRuntimeHelpers.cs +config +connect-pssession +consolehost +consolehostrunspaceinit +consolehostuserinterface +consolelineoutput +contenttype +convertfrom-csv +convertfrom-json +convertfrom-sddlstring +convertfrom-securestring +convertfrom-stringdata +convertto-csv +convertto-html +convertto-json +convertto-securestring +convertto-xml +copy-itemproperty +CopyItem.Tests.ps1 +corbob +coreclr +coreconsolehost +corefx +CorePsAssemblyLoadContext.cs +coveralls.exe +coveralls.io. +coveralls.net +CreateFile +CreateFileW +credssp +cron +crontab +crossgen crossgen'ing -DarwinJS +crossplatform +csharp +csmacnz +csphysicallyinstalledmemory +ctrl +CurrentCulture +CustomShellCommands.cs +DamirAinullin +DarylGraves +darquewarrior +darwinjs +DateTime +DateTime.UnixEpoch +daxian-dbw +dayofweek dchristian3188 +ddwr +debughandler +dee-see +defaultRefAssemblies +dependabot +deps deserialization deserialize +deserialized +deserializing +dest +dest.txt +dev +devblackops +devcontainer +deviceguard +devlead +devops +dgoldman-msft +Dictionary.TryAdd +diddledan +disable-localuser +disable-psbreakpoint +disable-pstrace +disable-pswsmancombinedtrace +disable-runspacedebug +disable-wsmantrace +disconnect-pssession +displaydataquery +Distribution_Request.md +distro +distros +dkaszews +dll +DllImport +dlls +dlwyatt +dockerbasedbuild +dockerfile +dockerfiles +learn.microsoft.com +doctordns +don'ts +dongbo +dotcover +dotnet +dotnetcore +dotnetmetadata.json +DotnetRutimeMetadata.json +DotnetRuntimeMetadata.json +dottedscopes +downlevel +dropdown +dwtaber +e.g. +ebook +ebooks +ece-jacob-scott +editorconfig +edyoung +enable-localuser +enable-psbreakpoint +enable-pstrace +enable-pswsmancombinedtrace +enable-runspacedebug +enable-wsmantrace +encodings +endian +enter-pshostprocess +enter-pssession +enum enums -EXE's -ExecutionPolicy -FileCatalog -FilterHashtable +Environment.NewLine +ergo3114 +errorrecord +etl +eugenesmlv +EventLogLogProvider +excludeversion +exe +executables +executionpolicy +exit-pshostprocess +exit-pssession +export-binarymilog +export-clixml +export-csv +export-formatdata +export-modulemember +fabricbot.json +failurecode +failurecount +farmerau +fbehrens +felixfbecker +ffeldhaus +ffi +fflaten +File.OpenHandle +filecatalog +filename +filesystem +filesystemprovider +files.wxs +filterhashtable +find-dscresource +find-packageprovider +find-rolecapability +findMissingNotices.ps1 +firefox +folderName foreach -GetParentProcess +formatfileloading +formatviewbinding +FormatWideCommand +Francisco-Gamino +frontload +fullclr +FunctionInfo +functionprovider +FunctionTable +fxdependent +gabrielsroka +GAC_Arm64 +gamified +gc.regions.xml +Generic.SortedList +get-apachemodule +get-apachevhost +get-childitem +get-cimassociatedinstance +get-cimclass +get-ciminstance +get-computerinfo +get-cronjob +get-eventsubscriber +Get-ExperimentalFeature +get-filehash +get-formatdata +get-installedmodule +get-installedscript +get-itemproperty +get-itempropertyvalue +get-localgroup +get-localgroupmember +get-localuser +get-logproperties +get-packageprovider +get-packagesource +get-psbreakpoint +get-pscallstack +get-pshostprocessinfo +get-psprovider +get-psreadlinekeyhandler +get-psreadlineoption +get-psrepository +get-pssession +get-pssessioncapability +get-runspacedebug +get-systemdjournal +get-typedata +get-uiculture +get-winevent +get-wsmaninstance +Get-WSManSupport +GetExceptionForHR +getparentprocess +gettype +Geweldig +GigaScratch +gitcommitid +github +githug +gitter +glachancecmaisonneuve +global.json globbing -honors +GoogleTest +gregsdennis +GUIs +gzip +hackathons +HashData +HashSet +hashtable +hashtables +hayhay27 +helloworld.ps1 +helpproviderwithcache +helpproviderwithfullcache +helpsystem +hemant +hemantmahawar +Higinbotham +himura2la +hololens +homebrew +hostifaces hostname -IncludeUserName -InformationRecord -IoT -iSazonov -IsCore -IsCoreCLR +hotfix +httpbin.org +httpbin's +https +hubuk +hvitved +i3arnon +i.e. +ico +idera +IDictionary +ifdef'ed +iisresetme +ilya +import-binarymilog +import-clixml +import-csv +import-localizeddata +import-packageprovider +import-powershelldatafile +includeide +includeusername +informationrecord +initializers +InitialSessionState.cs +InlineAsTypeCheck +install-packageprovider +IntelliSense +interactivetesting +interop +interoperation +interlocked.read +invoke-cimmethod +Invoke-DSCResource +invoke-restmethod +invoke-wsmanaction +InvokeRestMethodCommand.Common +iot +isazonov +iscore +iscoreclr +isError +isnot +itemtype +itpro +jackdcasey +jameswtruher +Jawz84 +jazzdelightsme jeffbi -JsonConfigFileAccessor -KeyFileParameter -KeyHandler -KirkMunro +jellyfrog +jen +joandrsn +joeltankam +joeyaiello +jokajak +JohnLBevan +josea +joshuacooper +journalctl +jpsnover +json +jsonconfigfileaccessor +JsonSchema.Net +judgement +jumplist +jwmoss +kanjibates +kasper3 +katacoda +Kellen-Stuart +kevinmarquette +kevinoid +KevRitchie +keyfileparameter +keyhandler +khansen00 +kiazhi +kirkmunro kittholland +korygill +kpis +krishnayalavarthi +kvprasoon kwiknick -Lee303 +kwkam +kylesferrazza +labelling +LabhanshAgrawal +lastwritetime +launch.json +ldspits +lee303 +Leonhardt +Libera.Chat +libicu +LibraryImport +libpsl libpsl-native +libunwind8 +license.rtf +linux +locationglobber +lockdown +loopback +lossless +louistio +LucaFilipozzi +lukexjeremy +lupino3 +lynda.com +lzybkr +m1k0net +M1kep +mababio +macos +macports +maertendmsft +mahawar +mailmap +Markdig.Signed +markdown.yml +manifest.spdx.json markekraus -meta -MiaRomero -Microsoft.Management.Infrastructure.Native -Microsoft.PowerShell.LocalAccounts +marktiedemann +Marusyk +MarvTheRobot +mattifestation +matt9ucci +mcbobke +mcr.microsoft.com +md +meir017 +memberresolution +Menagarishvili +messageanalyzer +metadata +metadata.json +miaromero +michaeltlombardi +microsoft +Microsoft.ApplicationInsights +Microsoft.CodeAnalysis.CSharp +Microsoft.CodeAnalysis.NetAnalyzers +microsoft.com +Microsoft.Management +microsoft.management.infrastructure.cimcmdlets +microsoft.management.infrastructure.native +Microsoft.Management.Infrastructure.Runtime.Win +microsoft.net.test.sdk +microsoft.powershell.archive +Microsoft.PowerShell.Commands +microsoft.powershell.commands.diagnostics +microsoft.powershell.commands.management +microsoft.powershell.commands.utility +microsoft.powershell.consolehost +microsoft.powershell.core +microsoft.powershell.coreclr.assemblyloadcontext +microsoft.powershell.coreclr.eventing +microsoft.powershell.diagnostics +microsoft.powershell.localaccounts +microsoft.powershell.management +microsoft.powershell.markdownrender +microsoft.powershell.psreadline +microsoft.powershell.security +microsoft.powershell.utility +Microsoft.Security.Extensions +Microsoft.WSMan +microsoft.wsman.management +microsoft.wsman.runtime +mikeTWC1984 +mirichmo +mjanko5 +mkdir +mkht mklement0 +ModuleCmdletBase.cs +MohiTheFish +Molkree +move-itemproperty +ms-psrp +msbuild +msftrncs +mshexpression.cs +mshsnapinloadunload +msi +multiline +multipart +mv +mvps mwrock -nanoserver-insider +myget +namedpipe +nameof +NameObscurerTelemetryInitializer +namespace +nano +nanoserver +NativeCommandProcessor.cs +NativeCultureResolver +nativeexecution +net5.0 +net10.0 +netcoreapp5.0 +netip.ps1. +netstandard.dll +new-apachevhost +new-ciminstance +new-cimsessionoption +new-cronjob +New-DockerTestBuild +new-guid +new-itemproperty +new-localgroup +new-localuser +new-modulemanifest +new-psrolecapabilityfile +new-pssession +new-pssessionconfigurationfile +new-pssessionoption +new-pstransportoption +new-scriptfileinfo +new-temporaryfile +new-timespan +new-winevent +new-wsmaninstance +new-wsmansessionoption +NextTurn +ngharo +Newtonsoft.Json +NJsonSchema +nohwnd +NoMoreFood non-22 -non-CIM -non-R2 +non-cim +non-https +non-nullable +non-r2 +noresume +notcontains +nuget +nuget.config +nuget.exe +nugetfeed +Nullable +numberbytes +numberOfPowershellRefAssemblies +nupkg +oauth +object.ReferenceEquals +offthewoll oising +omi +omnisharp +OneDrive oneget.org -PetSerAl +OneScripter +opencover +opencover.zip +openssh +openssl +opensuse +oss +OutputType +p1 +packagemanagement +PackageVersion +parameshbabu +parameterbinderbase +parameterbindercontroller +parameterbinding +ParenExpression +ParseError.ToString +Path.Join +pathresolution +PathResolvedToMultiple +patochun +patwardhan +paulhigin +pawamoy +payette +perf +perfview +perfview.exe +peter-evans +petseral +PingPathCommand.cs +pinvoke +pinvokes +plaintext +pluggable +pluralsight +poshcode +pougetat +powerbi powercode -PowerShellProperties +powershell +powershell-unix +powershell.6 +powershell.com +PowerShell.Common.props +powershell.core.instrumentation +powershell.exe +powershell.org +powershellcore +powershellgallery +powershellget +powershellmagazine.com +powershellninja +powershellpr0mpt +powershellproperties +ppadmavilasom +pre-build +pre-compiled +pre-defined +pre-generated +pre-installed +pre-parse +pre-release +pre-releases +pre-requisites +prepend +preprocessor +preview.1 +preview.2 +preview.2.22153.17 +preview.3 +preview.4 +preview.5 +preview.5.20279.10 +preview.5.20278.13 +preview.5.20269.29 +preview.5.20268.9 +preview.5.20272.6 +preview.5.22307.18 +preview.6 +preview.6.20318.15 +preview.6.21355.2 +preview.7 +preview.7.20356.2 +preview.7.20358.6 +preview.7.20364.3 +preview.7.20366.2 +preview.7.20366.15 +preview.7.22377.5 +preview.4.20258.7 +preview.4.20229.10 +preview.4.22252.9 +preview.8 preview1-24530-04 -PSDrive -PseudoParameterBinder -PSReadLine -PSScriptAnalyzer -PVS-Studio -Pwrshplughin.dll +preview1.22217.1 +preview7 +ProcessorArchitecture +ProductCode +productversion +program.cs +prototyyppi +providername +proxycommand +ps1 +ps1xml +pscore +pscredential +psd1 +psdrive +psdriveinfo +pseudoparameterbinder +psgallery +PSGalleryModules +psm1 +psobject +psobjects +psoptions.json +psproxyjobs +psreadline +psresourceget +psrp.windows +psscriptanalyzer +pssessionconfiguration +pssnapinloadunload +pssnapins +psversion +psversiontable +PSWindowsPowerShellCompatibility +PublishReadyToRun +pvs-studio +pwd +pwrshplughin.dll +pwsh +pwsh.deps.json +qmfrederik +raghav710 +Random.Shared +RandomNoun7 +RandomNumberGenerator.Fill +raspbian +rc +rc.1 +rc.1.21455.2 +rc.1.21458.32 +rc.2 +rc.2.22477.20 +rc.3 rc2-24027 rc3-24011 -RelationLink +rcedit +readme +readme.md +readonly +ReadyToRun +rebase +rebase.yml +rebasing +receive-pssession +recurse +reddit +redhat +redirections +redistributable +redistributables +register-argumentcompleter +register-cimindicationevent +register-engineevent +register-objectevent +register-packagesource +register-psrepository +registryprovider +relationlink +releaseTools.psm1 +RemoteSessionNamedPipe +remotesigned +remoting +remove-ciminstance +remove-cronjob +remove-itemproperty +remove-localgroup +remove-localgroupmember +remove-localuser +remove-psbreakpoint +remove-psreadlinekeyhandler +remove-pssession +remove-typedata +remove-wsmaninstance +rename-itemproperty +rename-localgroup +rename-localuser +renehernandez +reparse +replicaJunction +repo +reportgenerator +resgen +responseheaders +REST +rest.ps1 +restart-apachehttpserver +resx +Rethrow +reynoldsbd richardszalay +Rin +rjmholt rkeithhill -shebang -ShellExecute -startup +rkitover +robo210 +ronn +rpalo +rpolley +runas +runspace +runspaceinit +runspaces +runtime +runtimes +Ryan-Hutchison-USAF +SA1026CodeMustNotContainSpaceAfterNewKeywordInImplicitlyTypedArrayAllocation +Saancreed +SafeRegistryHandle +sample-dotnet1 +sample-dotnet2 +sarithsutha +sarthakmalik +savehelp +sazonov +sba923 +schvartzman +schwartzmeyer +scriptblock +securestring +seemethere +select-xml +SemanticChecks +semver +serverless +sessionid +sessionstate +sessionstatecontainer +sessionstateitem +set-ciminstance +set-itemproperty +set-localgroup +set-localuser +set-logproperties +set-packagesource +set-psbreakpoint +set-psdebug +set-psreadlinekeyhandler +set-psreadlineoption +set-psrepository +set-strictmode +set-wsmaninstance +set-wsmanquickconfig +sethvs +setversionvariables +sha256 +ShaydeNofziger +shellexecute +shouldbeerrorid +showcommandinfo +Shriram0908 +silijon +simonwahlin +singleline +sles15 +smes +snapcraft +snapin +snover +sometext +SortedList +source.txt +spongemike2 +src +ss64.com +st0le +stackoverflow +stanzilla +start-codecoveragerun +start-pspester stdin -StringBuilder -SxS +stevel-msft +StevenLiekens +stevend811 +stknohg +strawgate +streamdescribecifeaturescenariodescribecontextitcontextcontextbeforeallafterallbeforeeachaftereachshould +StrictMode +string.split +stringbuilder +stuntguy3000 +StyleCop +subfolder +submodule +submodules +sudo +superproject +svg +swarfegagit +SwitchParameter +sxs +sydneyhsmith +symlink +symlinks +syscall +syslog +System.IO.Packaging +System.InvalidOperationException system.manage -Tadas -TestCase -TheFlyingCorpse -TimCurwick +system.management.automation +System.Management.Automation.utils +systemd +SytzeAndr +tabcompletion +tadas +tandasat +tasnimzotder +TargetFramework +test-modulemanifest +test-pssessionconfigurationfile +test-scriptfileinfo +tar.gz +test.ps1 +test.txt. +Tests.ps1 +test1.txt +test2.txt +testcase +testdrive +TestPathCommand.cs +tests.zip +tgz +theflyingcorpse +thenewstellw +thezim +thlac +ThomasNieto +threadjob +ThreadStatic +throttlelimit +throw-testcasesitmockdescribe +ThrowExceptionForHR +timcurwick timestamp -TimeZone -TPA -TTY's -UserData -UTF8NoBOM +timothywlewis +TKey +tobias +tokenizer.cs +tokenizing +tomconte +tommymaynard +toolchain +toolset +tracesource +travisez13 +travisty +trossr32 +truher +TSAUpload +turbedi +TValue +tylerleonhardt +typecataloggen +typeconversion +typegen +typematch +t_ +ubuntu +ubuntu22.04 +uint +un-versioned +unicode +UnixSocket +unregister-event +unregister-packagesource +unregister-psrepository +unregister-pssessionconfiguration +unregistering +untracked +unvalidated +UpdateDotnetRuntime.ps1 +update-formatdata +update-modulemanifest +update-scriptfileinfo +update-typedata +uri +urizen-source +urls +UseMU +userdata +uservoice +utf-8 +utf8 +utf8nobom +utils +utils.cs v0.1.0 v0.2.0 v0.3.0 v0.4.0 v0.5.0 v0.6.0 -v6.0.0 -ValidateNotNullOrEmpty -WebRequest -win7-x86 -WindowsVersion -WSManCredSSP -XPath -Youtube - - test/tools/CodeCoverageAutomation/README.md -CodeCoverage.zip -Coveralls.exe -Coveralls.io. -Coveralls.net -csmacnz -OpenCover.zip -powershell.version -Start-CodeCoverageRun -tests.zip +v141 +v2 +v3 +v4 +v5 v5.0 +v6 v6.0. - - docs/host-powershell/README.md -CorePsAssemblyLoadContext.cs -sample-dotnet1 -NTLM-based -Kerberos-based -preview1-002106-00 -sample-dotnet2 -0-powershell -post-6 -- CODE_OF_CONDUCT.md -opencode -microsoft.com - - ./tools/install-powershell.readme.md -includeide -sed +v6.0.0 +v6.0.1 +v6.0.2 +v6.0.4 +v6.0.5 +v6.1.0 +v6.1.1 +v6.1.2 +v6.2.0 +v6.2.1 +v6.2.2 +v6.2.3 +v6.2.4 +v7.0.0 +v7.0.3 +v7.0.4 +v7.0.9 +v7.1.0 +v7.1.6 +v7.1.7 +v7.2.2 +v7.2.6 +v7.3.0 +v7.4.0 +v7.0.12 +v7.0.13 +validatenotnullorempty +ValidateSet +varunsh-coder +versioned +versioning +vexx32 +Virtualization +visualstudio +vmsilvamolina +vorobev +vors +vpondala +vscode +vstsbuild.ps1 +walkthrough +webcmdlets +weblistener +webrequest +webrequestpscmdlet.common.cs +webresponseobject.common +weltner +wesholton84 +wget +whitespace +wildcard +wildcarded +wildcards +win32 +win32-openssh +win7 +win8 +windos +windows.json +windowspsmodulepath +windowsversion +winrm +wix +wmentha +WNetGetConnection +WNetAddConnection2 +worrenb +wpr +wprui.exe +writingpestertests.md +wsl +wsman +wsmancredssp +wsmansessionoption.cs +www.github.com +x64 +x86 +xpath +xtqqczze +xunit +xunit.runner.visualstudio +Xunit.SkippableFact +yaml +yashrajbharti +yecril71pl +yml +youtube +Youssef1313 +Yulv-git +zackjknight +ComInterop +ryneandal +runtime#33060 +vexx32 +perf +britishben +felixfbecker +vpondala +dependabot +jellyfrog +1redone +tommymaynard +vmsilvamolina +fbehrens +lockdown +lukexjeremy +deserializing +kiazhi +v6.1.2 +Menagarishvili +anmenaga +fxdependent +sba923 +replicaJunction +lupino3 +hvitved +unvalidated +Geweldig +mjanko5 +v7.0.0 +v7.0.10 +renehernandez +ece-jacob-scott +st0le +MohiTheFish +CodeFormatter +StyleCop +SytzeAndr +yashrajbharti +Leonhardt +tylerleonhardt +nuget.config +libmi +rpms +authenticode +env +MarianoAlipi +Microsoft.PowerShell.Native +davidBar-On +parameterized +misconfigured +hez2010 +ZhiZe-ZG +SecureStringHelper.FromPlainTextString +ProcessBaseCommand.AllProcesses +Parser.cs +MultipleServiceCommandBase.AllServices +JustinGrote +Newtonsoft.Json +minSize +WGs +wg-definitions +thejasonhelmick +winps +componentization +CimCmdlets +Microsoft.PowerShell.Host +PSDiagnostics +nightlies +wg +Visio +triaged +lifecycle +v2.0.5 +mutex +gukoff +dinhngtu +globbed +octos4murai +PSCommand +System.Management.Automation.ICommandRuntime +AppDomain.CreateDomain +AppDomain.Unload +ProcessModule.FileName +Environment.ProcessPath +PSUtils.GetMainModule +schuelermine +SupportsShouldProcess +Start-PSBootstrap +DotnetMetadataRuntime.json +deps.json +Jaykul +eltociear +consolehost.proto +IDisposable +ConvertToJsonCommand +CommandPathSearch +UseCoalesceExpression +UseSystemHashCode +UseCoalesceExpressionForNullable +substring +RemoveAll +MakeFieldReadonly +Microsoft.Management.UI.Internal +StringComparison +osx-arm64 +crossgen2 +MartinGC94 +BrannenGH +SergeyZalyadeev +KiwiThePoodle +Thomas-Yu +cgmanifest.json +mcr.microsoft.com +global.json +tar.gz +psoptions.json +manifest.spdx.json +buildinfo +SKUs +vmImage +InternalCommands.cs +CommonCommandParameters.cs +preview.6.22352.1 +v2.2.6 +ResultsComparer +pre-defined +System.Runtime.CompilerServices.Unsafe +TabExpansion +PSv2 +System.Data.SqlClient +Microsoft.CSharp +v7.2.10 +7.2.x +v7.3.3 +http +webcmdlet +argumentexception.throwifnullorempty +bitconverter.tostring +convert.tohexstring +requires.notnullorempty +argumentoutofrangeexception.throwifnegativeorzero +callerargumentexpression +requires.notnull +argumentnullexception +throwifnull +process.cs +setrequestcontent +streamhelper.cs +invokerestmethodcommand.common.cs +gethttpmethod +httpmethod +removenulls +notnull +argumentnullexception.throwifnull +disable_telemetry +langversion +microsoft.extensions.objectpool +microsoft.codeanalysis.analyzers +benchmarkdotnet +winforms +MicrosoftDocs +about_Scripts +debugging-from-commandline +about_Object_Creation +about_Functions_Advanced +Microsoft.PowerShell.SDK +NuGet.org. + - CHANGELOG.md +aavdberg +asrosent +azkarmoulana +chucklu +Claustn +CVE-2018-8256 +CVE-2018-8415 +daviddreher2 +honour +iGotenz +jeis2497052 +Jocapear +lassehastrup +markwragg +nbkalex +NeoBeum +nycjan +paalbra +robdy +SeeminglyScience +StingyJack +ThreeFive-O +tobvil +uninstallation +vongrippen +yurko7 +zhenggu +davinci26 +vedhasp +SeeminglyScience +eugenesmlv +steviecoaster +machgo +derek-xia +weltkante +kilasuit +tnieto88 +Orca88 +OrderBy +centreboard +romero126 +Greg-Smulko +danstur +vdamewood +MJECloud +DamirAinullin +NoMoreFood +silijon +NextTurn +mikeTWC1984 +Marusyk +M1kep +doctordns +alvarodelvalle +devlead +RandomNoun7 +edyoung +stevend811 +LabhanshAgrawal +ShaydeNofziger +alepauly +bpayette +joeltankam +OneScripter +Francisco-Gamino +adamdriscoll +analytics +deserialized +string.Join +string.Split +StringSplitOptions.TrimEntries +Dictionary.TryAdd +Environment.NewLine +ParseError.ToString +EditorConfig +GetExceptionForHR +ThrowExceptionForHR +Get-ExperimentalFeature +PSWindowsPowerShellCompatibility +currentculture +NJsonSchema +Microsoft.CodeAnalysis.CSharp +NJsonSchema +StrictMode +devcontainer +AzFileCopy +metadata.json +ADOPTERS.md +powershell.exe +SetVersionVariables +yml +DateTime +DeploymentScripts +GetValues +GetNames +SessionStateStrings +Enum.HasFlags +ConsoleInfoErrorStrings.resx +ContentHelper.Common.cs +FusionAssemblyIdentity +GlobalAssemblyCache +StringManipulationHelper +testexe.exe +echocmdline +MemoryExtensions.IndexOfAny +PSv2CompletionCompleter +RemoteRunspacePoolInternal.cs +PSVersionInfo +WildcardPattern +UTF8Encoding +PowerShell.Core.Instrumentation.man +Encoding.Default +WinTrust +System.Runtime.CompilerServices.Unsafe +azCopy +APISets +ApiScan +System.Data.SqlClient +minimatch +2.final +SessionStateInternal +Microsoft.PowerShell.SDK +Markdig.Signed + - docs/debugging/README.md +corehost + - docs/learning-powershell/README.md +PSKoans + - docs/testing-guidelines/CodeCoverageAnalysis.md +de5f69c + - docs/testing-guidelines/TestRoadmap.md +_no_ + - docs/testing-guidelines/WritingPesterTests.md +nGet-ContentOut-String +nGet-MultiLineString +PSDefaultParameterValues-skip +ShouldShouldIt + - tools/performance/README.md +Invoke-PerfviewPS +JIT.Regions.xml +PowerShell.Regions.xml +PowerShell.stacktags +PowerShell.wpaProfile +PowerShell.wprp +wpa +wpaProfile + - demos/WindowsPowerShellModules/README.md +2.x. + - CHANGELOG/7.1.md +ThomasNieto +spongemike2 +davidseibel +HumanEquivalentUnit +hemisphera +tamasvajk +boolean +bitwise +StringUtil +StringUtil.Format +CommandLineParameterParser +jackerr3 +preview.8.20407.11 +pre-check +davidreis97 +soccypowa +nologo +InstallLocation +PkgES +Microsoft.PowerShell.Native +rtm.20526.5 +ini +package.json +jcotton42 +RPMs +PSDesiredStateConfiguration +dotnet5 + - CHANGELOG/7.2.md +Gimly +jborean93 +mkswd +PatLeong +paul-cheung +georgettica +ProcessId +CurrentManagedThreadId +System.Environment +LongCount +nullable +Guid +accessor +CancellationToken +struct +IsEmpty +StringBuilder.Append +String.Concat +String.Substring +NoLanguage +kyanha +accessors +matthewjdegarmo +Reset-PWSHSystemPath +StyleCop.Analyzers +UseIsNullCheck +ConvertTypeOfToNameOf +usings +PriorityAttribute +System.Management.Automation.Interpreter.IBoxableInstruction +System.Management.Automation.Provider.IDynamicPropertyProvider +System.Management.Automation.Language.IScriptExtent +System.Management.Automation.Language.ICustomAstVisitor2 +System.Management.Automation.LanguagePrimitives.IConversionData +System.Automation.Remoting.Client.IWSManNativeApiFacade +System.Management.Automation.Language.ISupportsAssignment +System.Management.Automation.ICommandRuntime2 +System.Management.Automation.IOutputProcessingState +System.Management.Automation.IJobDebugger +System.Management.Automation.Interpreter.IInstructionProvider +System.Management.Automation.IHasSessionStateEntryVisibility +System.Management.Automation.Tracing.IEtwEventCorrelator +AclCommands +System.Management.Automation.Language.IAstPostVisitHandler +System.Management.Automation.IModuleAssemblyInitializer +nuget.org +GetFiles +TestModuleManifestCommand +System.Management.Automation.Provider.IContentWriter +TranscriptionOption.FlushContentToDisk +structs +System.Management.Automation.IArgumentCompleter +GetDirectories +System.Management.Automation.Host.IHostSupportsInteractiveSession +System.Management.Automation.Provider.IPropertyCmdletProvider +SimplifyConditionalExpression +MarkdownLint +AsSpan +AsMemory +xml +finalizer +finalizers +const +UseAutoProperty +SuppressFinalize +string.Empty +LoadBinaryModule +GetListOfFilesFromData +RPMs +NullableAttribute +PSDesiredStateConfiguration +brianary +Fs00 +MartinGC94 +jakekerr +strikethrough +string.Contains +imba-tjd +url +ArrayList +SDKs +XunitXml.TestLogger +Backport +v7.1.1 +about_PSDesiredStateConfiguration +hbuckle +preview.2.21155.3 +3.final +codesign +powershell.config.json +romero126 +boolean +rtm.20526.5 +dbaileyut +un-localized +awakecoding +bcwood +ThrowTerminatingError +DoesNotReturn +GetValueOrDefault +PSLanguageMode +adamsitnik +msixbundle +PowerShell-Native#70 +AppxManifest.xml +preview.9 +preview.10 +ArmaanMcleod +entrypoint +lselden +SethFalco +CodeQL +slowy07 +rc.2.21505.57 +ThirdPartyNotices +ThirdPartyNotices.txt +cgmanifest.json +buildinfo +tar.gz +psoptions.json +manifest.spdx.json +vPack +kondratyev-nv +v7.2.0 +v7.2.3 +cgmanifest.json +pwsh.exe +6.0.100-rtm.21527.11 +6.0.100-rc.2.21505.57 +ThirdPartyNotices.txt +rtm.21527.11 +SKUs +vmImage +Ubuntu22.04 + - CHANGELOG/7.0.md +codesign +release-BuildJson +yml +dotnet5 +buildinfo +SKUs +CGManifest +vmImage +ci.psm1 +centos-7 +PSDesiredStateConfiguration +NoLanguage +createdump +vPack +PkgES + - test/perf/benchmarks/README.md +benchmarked +BenchmarkDotNet + - docs/community/working-group-definitions.md +gaelcolas +jdhitsolutions +jhoneill +kilasuit +michaeltlombardi +SeeminglyScience +TobiasPSP + - CHANGELOG/7.3.md +ayousuf23 +AzCopy.exe +hammy3502 +PowerShellExecutionHelper.cs +ClientRemotePowerShell +DOTNET_ROOT +SkipExperimentalFeatureGeneration +AzCopy +Start-PSBootStrap +precheck +SKUs +powershell.config.json +Microsoft.PowerShell.GlobalTool.Shim.csproj +InvokeCommand +UseDotNet +vmImage +NoLanguage +GetValueOrDefault +kondratyev-nv +penimc_cor3.dll +PkgES +v7.2.0 +preview.9 +pwsh.exe +XunitXml.TestLogger +rtm.21527.11 +ThirdPartyNotices.txt +buildinfo +tar.gz +rc.2.21505.57 +psoptions.json +manifest.spdx.json +AzureFileCopy +vPack +dotnet5 +buildinfo +SKUs +CGManifest +vmImage +ci.psm1 +jcotton42 +centos-7 +Security.types.ps1xml +optout + - ADOPTERS.md +MicrosoftPowerBIMgmt + - tools/clearlyDefined/readme.md +ClearlyDefined + - CHANGELOG/preview.md +stevenebutler +spaette +syntax-tm +URIs +typeDataXmlLoader.cs +GetResponseObject +ContentHelper +BasicHtmlWebResponseObject +WebRequestSession.cs +dkattan +preview.3.23178.7 +PoolNames +techguy16 +sdwheeler +MicrosoftDocs +about_Scripts +about_Object_Creation +about_Functions_Advanced +Microsoft.PowerShell.SDK +NuGet.org. diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 6bd3fd23fb0..00000000000 --- a/.travis.yml +++ /dev/null @@ -1,42 +0,0 @@ -language: cpp - -git: - depth: 1000 - -os: - - linux - - osx -sudo: required -dist: trusty -osx_image: xcode8.1 - -matrix: - fast_finish: true - -addons: - artifacts: - paths: - - $(ls powershell*{deb,pkg,AppImage} | tr "\n" ":") - - pester-tests.xml - -install: - - pushd tools - - ./install-powershell.sh - - popd - # Default 2.0.0 Ruby is buggy - # Default bundler version is buggy - - if [[ "$TRAVIS_OS_NAME" == "osx" ]]; then rvm install ruby-2.3.1; rvm use 2.3.1; fi - # spellcheck - - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then - nvm install 6.4.0 && - npm install -g markdown-spellcheck@0.11.0; - fi - - ulimit -n 4096 - - powershell -File tools/travis.ps1 -Bootstrap - -script: - - powershell -File tools/travis.ps1 - # spellcheck - - if [[ "$TRAVIS_OS_NAME" == "linux" ]]; then - mdspell '**/*.md' '!powershell/**/*.md' --ignore-numbers --ignore-acronyms --report; - fi diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 00000000000..fa227cd9944 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,14 @@ +{ + // See https://go.microsoft.com/fwlink/?LinkId=827846 + // for the documentation about the extensions.json format + "recommendations": [ + "editorconfig.editorconfig", + "ms-azure-devops.azure-pipelines", + "ms-vscode.cpptools", + "ms-dotnettools.csharp", + "ms-vscode.PowerShell", + "twxs.cmake", + "DavidAnson.vscode-markdownlint", + "vitaliymaz.vscode-svg-previewer" + ] +} diff --git a/.vscode/launch.json b/.vscode/launch.json index 5a51dd2dc7d..4ba261df08c 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -7,8 +7,8 @@ "request": "launch", "justMyCode": false, "stopAtEntry": true, - "program": "${workspaceRoot}/debug/powershell", - "preLaunchTask": "build", + "program": "${workspaceRoot}/debug/pwsh", + "preLaunchTask": "Build", "externalConsole": true, "cwd": "${workspaceRoot}" }, @@ -18,6 +18,24 @@ "request": "attach", "justMyCode": false, "processId": "${command:pickProcess}" + }, + { + "type": "PowerShell", + "request": "launch", + "name": "PowerShell Launch Current File", + "script": "${file}", + "args": [], + "cwd": "${file}" + }, + { + "type": "PowerShell", + "request": "launch", + "name": "PowerShell Launch Current File w/Args Prompt", + "script": "${file}", + "args": [ + "${command:SpecifyScriptArgs}" + ], + "cwd": "${file}" } ] } diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 00000000000..ad298823d10 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,40 @@ +// Place your settings in this file to overwrite default and user settings. +{ + "editor.tabSize": 4, + "editor.insertSpaces": true, + + "files.insertFinalNewline": true, + + // Based on current .markdownlist.json settings: + // https://github.com/PowerShell/PowerShell/blob/master/.markdownlint.json + "markdownlint.config": { + "MD004": false, + "MD024": false, + "MD033": false, + "MD034": false, + "MD038": false, + "MD042": false + }, + + "[powershell]": { + "files.trimTrailingWhitespace": true + }, + + // Sets the codeformatting options to follow the given indent style in a way that is compatible with PowerShell syntax. For more information about the brace styles please refer to https://github.com/PoshCode/PowerShellPracticeAndStyle/issues/81. + "powershell.codeFormatting.preset": "OTBS", + + // Adds a space between a keyword and its associated scriptblock expression. + "powershell.codeFormatting.whitespaceBeforeOpenBrace": true, + + // Adds a space between a keyword (if, elseif, while, switch, etc) and its associated conditional expression. + "powershell.codeFormatting.whitespaceBeforeOpenParen": true, + + // Adds spaces before and after an operator ('=', '+', '-', etc.). + "powershell.codeFormatting.whitespaceAroundOperator": true, + + // Adds a space after a separator (',' and ';'). + "powershell.codeFormatting.whitespaceAfterSeparator": true, + + // Omnisharp : Enable EditorConfig support + "omnisharp.enableEditorConfigSupport": true +} diff --git a/.vscode/tasks.json b/.vscode/tasks.json index 3806a5a8119..b92aaddeb0d 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -1,16 +1,63 @@ { - "version": "0.1.0", - "command": "powershell", - "isShellCommand": true, - "showOutput": "always", - "suppressTaskName": true, - "args": [ "-command" ], + "version": "2.0.0", + + "windows": { + "options": { + "shell": { + "executable": "pwsh.exe", + "args": [ + "-NoProfile", + "-ExecutionPolicy", + "Bypass", + "-Command" + ] + } + } + }, + "linux": { + "options": { + "shell": { + "executable": "/usr/bin/pwsh", + "args": [ + "-NoProfile", + "-Command" + ] + } + } + }, + "osx": { + "options": { + "shell": { + "executable": "/usr/local/bin/pwsh", + "args": [ + "-NoProfile", + "-Command" + ] + } + } + }, "tasks": [ { - "taskName": "build", - "args": [ "Import-Module ${workspaceRoot}/build.psm1; Start-PSBuild -Output ${workspaceRoot}/debug" ], - "isBuildCommand": true, + "label": "Bootstrap", + "type": "shell", + "command": "Import-Module '${workspaceFolder}/build.psm1'; Start-PSBootstrap", + "problemMatcher": [] + }, + { + "label": "Clean Build", + "type": "shell", + "command": "Import-Module '${workspaceFolder}/build.psm1'; Start-PSBuild -Clean -Output (Join-Path '${workspaceFolder}' debug)", + "problemMatcher": "$msCompile" + }, + { + "label": "Build", + "type": "shell", + "command": "Import-Module '${workspaceFolder}/build.psm1'; Start-PSBuild -Output (Join-Path '${workspaceFolder}' debug)", + "group": { + "kind": "build", + "isDefault": true + }, "problemMatcher": "$msCompile" } ] diff --git a/.vsts-ci/install-ps.yml b/.vsts-ci/install-ps.yml new file mode 100644 index 00000000000..7190e228578 --- /dev/null +++ b/.vsts-ci/install-ps.yml @@ -0,0 +1,161 @@ +name: PR-$(System.PullRequest.PullRequestNumber)-$(Date:yyyyMMdd)$(Rev:.rr) +trigger: + # Batch merge builds together while a merge build is running + batch: true + branches: + include: + - master + - release* + - feature* + paths: + include: + - /tools/install-powershell.* + - /tools/installpsh-*.sh + - /.vsts-ci/install-ps.yml +pr: + branches: + include: + - master + - release* + - feature* + paths: + include: + - /tools/install-powershell.sh + - /tools/installpsh-*.sh + - /tools/install-powershell.ps1 + - /.vsts-ci/install-ps.yml + +variables: + DOTNET_CLI_TELEMETRY_OPTOUT: 1 + POWERSHELL_TELEMETRY_OPTOUT: 1 + +resources: +- repo: self + clean: true +phases: +- template: templates/install-ps-phase.yml + parameters: + scriptName: sudo ./tools/install-powershell.sh + jobName: InstallPowerShellUbuntu + pool: ubuntu-latest + verification: | + if ([Version]"$($PSVersionTable.PSVersion.Major).$($PSVersionTable.PSVersion.Minor).$($PSVersionTable.PSVersion.Patch)" -lt [version]"7.3.0") + { + throw "powershell was not upgraded: $($PSVersionTable.PSVersion)" + } + +- template: templates/install-ps-phase.yml + parameters: + scriptName: sudo ./tools/install-powershell.sh + jobName: InstallPowerShellMariner2 + pool: ubuntu-latest + container: mcr.microsoft.com/powershell/test-deps:mariner-2.0 + verification: | + if ([Version]"$($PSVersionTable.PSVersion.Major).$($PSVersionTable.PSVersion.Minor).$($PSVersionTable.PSVersion.Patch)" -lt [version]"7.3.0") + { + throw "powershell was not upgraded: $($PSVersionTable.PSVersion)" + } + +- template: templates/install-ps-phase.yml + parameters: + scriptName: sudo ./tools/install-powershell.sh + jobName: InstallPowerShellAmazonLinux + pool: ubuntu-latest + container: pshorg/powershellcommunity-test-deps:amazonlinux-2.0 + verification: | + if ([Version]"$($PSVersionTable.PSVersion.Major).$($PSVersionTable.PSVersion.Minor).$($PSVersionTable.PSVersion.Patch)" -lt [version]"7.3.0") + { + throw "powershell was not upgraded: $($PSVersionTable.PSVersion)" + } + +- template: templates/install-ps-phase.yml + parameters: + scriptName: sudo ./tools/installpsh-amazonlinux.sh + jobName: InstallPSHAmazonLinux + pool: ubuntu-latest + container: pshorg/powershellcommunity-test-deps:amazonlinux-2.0 + verification: | + if ([Version]"$($PSVersionTable.PSVersion.Major).$($PSVersionTable.PSVersion.Minor).$($PSVersionTable.PSVersion.Patch)" -lt [version]"7.3.0") + { + throw "powershell was not upgraded: $($PSVersionTable.PSVersion)" + } + continueOnError: false + +# TODO: add sudo to script and use image with sudo +- template: templates/install-ps-phase.yml + parameters: + scriptName: ./tools/install-powershell.sh + jobName: InstallPowerShellCentOS + pool: ubuntu-latest + container: mcr.microsoft.com/powershell/test-deps:centos-7 + +- template: templates/install-ps-phase.yml + parameters: + scriptName: ./tools/install-powershell.sh + jobName: InstallPowerShellDebian9 + pool: ubuntu-latest + container: mcr.microsoft.com/powershell/test-deps:debian-9 + + +# VSTS could not find pwsh in: +# mcr.microsoft.com/powershell:opensuse-42.3 +# could not repo locally + +# sudo is not needed on macOS +- template: templates/install-ps-phase.yml + parameters: + scriptName: ./tools/install-powershell.sh + jobName: InstallPowerShellMacOS + pool: macOS-latest + verification: | + if ([Version]"$($PSVersionTable.PSVersion.Major).$($PSVersionTable.PSVersion.Minor).$($PSVersionTable.PSVersion.Patch)" -lt [version]"7.3.0") + { + # The script does not upgrade on mac os https://github.com/PowerShell/PowerShell/issues/9322 + Write-Warning "powershell was not upgraded: $($PSVersionTable.PSVersion)" + } + +- template: templates/install-ps-phase.yml + parameters: + scriptName: pwsh -c ./tools/install-powershell.ps1 -AddToPath + jobName: InstallPowerShellPS1Ubuntu + pool: ubuntu-latest + +- template: templates/install-ps-phase.yml + parameters: + scriptName: pwsh -c ./tools/install-powershell.ps1 -AddToPath -Daily + jobName: InstallPowerShellPS1UbuntuDaily + pool: ubuntu-latest + verification: | + Write-Verbose $PSVersionTable.PSVersion -verbose + if ([Version]"$($PSVersionTable.PSVersion.Major).$($PSVersionTable.PSVersion.Minor).$($PSVersionTable.PSVersion.Patch)" -lt [version]"7.3.0") + { + throw "powershell was not upgraded: $($PSVersionTable.PSVersion)" + } + +- template: templates/install-ps-phase.yml + parameters: + scriptName: pwsh -c ./tools/install-powershell.ps1 -AddToPath -Daily + jobName: InstallPowerShellMacOSDaily + pool: macOS-latest + verification: | + Write-Verbose $PSVersionTable.PSVersion -verbose + if ([Version]"$($PSVersionTable.PSVersion.Major).$($PSVersionTable.PSVersion.Minor).$($PSVersionTable.PSVersion.Patch)" -lt [version]"7.0.0") + { + throw "powershell was not upgraded: $($PSVersionTable.PSVersion)" + } + +- template: templates/install-ps-phase.yml + parameters: + scriptName: | + pwsh -c ./tools/install-powershell.ps1 -AddToPath -Daily + jobName: InstallPowerShellWindowsDaily + pool: windows-latest + verification: | + $newVersion = &$env:LOCALAPPDATA\Microsoft\powershell-daily\pwsh -v + $newVersion -match '^PowerShell ((\d*\.\d*\.\d*)(-\w*(\.\d*)?)?){1}' + $versionOnly = $Matches[2] + Write-verbose "$newVersion; versionOnly: $versionOnly" -verbose + if ([Version]$versionOnly -lt [version]"7.0.0") + { + throw "powershell was not upgraded: $newVersion" + } diff --git a/.vsts-ci/linux-daily.yml b/.vsts-ci/linux-daily.yml new file mode 100644 index 00000000000..10effadd1e3 --- /dev/null +++ b/.vsts-ci/linux-daily.yml @@ -0,0 +1,149 @@ +name: PR-$(System.PullRequest.PullRequestNumber)-$(Date:yyyyMMdd)$(Rev:.rr) +trigger: + # Batch merge builds together while a merge build is running + batch: true + branches: + include: + - master + - release* + - feature* + paths: + include: + - '*' + exclude: + - /.vsts-ci/misc-analysis.yml + - /.github/ISSUE_TEMPLATE/* + - /.dependabot/config.yml +pr: + branches: + include: + - master + paths: + include: + - .vsts-ci/linux-daily.yml + +variables: + DOTNET_CLI_TELEMETRY_OPTOUT: 1 + POWERSHELL_TELEMETRY_OPTOUT: 1 + DOTNET_NOLOGO: 1 + __SuppressAnsiEscapeSequences: 1 + +resources: +- repo: self + clean: true + +stages: +- stage: BuildLinux + displayName: Build for Linux + jobs: + - template: templates/ci-build.yml + parameters: + pool: ubuntu-20.04 + jobName: linux_build + displayName: linux Build + +- stage: TestLinux + displayName: Test for Linux + jobs: + - job: linux_test + timeoutInMinutes: 90 + pool: + vmImage: ubuntu-20.04 + displayName: Linux Test + + steps: + - pwsh: | + Get-ChildItem -Path env: | Out-String -width 9999 -Stream | write-Verbose -Verbose + displayName: Capture Environment + condition: succeededOrFailed() + + - task: DownloadBuildArtifacts@0 + displayName: 'Download Build Artifacts' + inputs: + downloadType: specific + itemPattern: | + build/**/* + xunit/**/* + downloadPath: '$(System.ArtifactsDirectory)' + + - pwsh: | + Get-ChildItem "$(System.ArtifactsDirectory)\*" -Recurse + displayName: 'Capture Artifacts Directory' + continueOnError: true + + - pwsh: | + Import-Module .\tools\ci.psm1 + Invoke-CIInstall -SkipUser + displayName: Bootstrap + condition: succeededOrFailed() + + - pwsh: | + Import-Module .\build.psm1 + Restore-PSOptions -PSOptionsPath '$(System.ArtifactsDirectory)\build\psoptions.json' + $output = (Get-PSOptions).Output + $rootPath = Split-Path (Split-Path $output) + Expand-Archive -Path '$(System.ArtifactsDirectory)\build\build.zip' -DestinationPath $rootPath -Force + + ## Fix permissions + Get-ChildItem $rootPath -Recurse | ForEach-Object { + if ($_ -is [System.IO.DirectoryInfo]) { + chmod +rwx $_.FullName + } else { + chmod +rw $_.FullName + } + } + chmod a+x $output + + Write-Host "=== Capture Unzipped Directory ===" + Get-ChildItem $rootPath -Recurse + displayName: 'Unzip Build and Fix Permissions' + condition: succeeded() + + - pwsh: | + Import-Module .\tools\ci.psm1 + Restore-PSOptions -PSOptionsPath '$(System.ArtifactsDirectory)\build\psoptions.json' + Invoke-CITest -Purpose UnelevatedPesterTests -TagSet CI + displayName: Test - UnelevatedPesterTests - CI + condition: succeeded() + + - pwsh: | + Import-Module .\tools\ci.psm1 + Restore-PSOptions -PSOptionsPath '$(System.ArtifactsDirectory)\build\psoptions.json' + Invoke-CITest -Purpose ElevatedPesterTests -TagSet CI + displayName: Test - ElevatedPesterTests - CI + condition: succeededOrFailed() + + - pwsh: | + Import-Module .\tools\ci.psm1 + Restore-PSOptions -PSOptionsPath '$(System.ArtifactsDirectory)\build\psoptions.json' + Invoke-CITest -Purpose UnelevatedPesterTests -TagSet Others + displayName: Test - UnelevatedPesterTests - Others + condition: succeededOrFailed() + + - pwsh: | + Import-Module .\tools\ci.psm1 + Restore-PSOptions -PSOptionsPath '$(System.ArtifactsDirectory)\build\psoptions.json' + Invoke-CITest -Purpose ElevatedPesterTests -TagSet Others + displayName: Test - ElevatedPesterTests - Others + condition: succeededOrFailed() + + - pwsh: | + Import-Module .\build.psm1 + $xUnitTestResultsFile = "$(System.ArtifactsDirectory)\xunit\xUnitTestResults.xml" + Test-XUnitTestResults -TestResultsFile $xUnitTestResultsFile + displayName: Verify xUnit Test Results + condition: succeededOrFailed() + +- stage: CodeCovTestPackage + displayName: CodeCoverage and Test Packages + dependsOn: [] # by specifying an empty array, this stage doesn't depend on the stage before it + jobs: + - job: CodeCovTestPackage + displayName: CodeCoverage and Test Packages + pool: + vmImage: ubuntu-20.04 + steps: + - pwsh: | + Import-Module .\tools\ci.psm1 + New-CodeCoverageAndTestPackage + displayName: CodeCoverage and Test Package diff --git a/.vsts-ci/linux-internal.yml b/.vsts-ci/linux-internal.yml new file mode 100644 index 00000000000..c1c8bcef62d --- /dev/null +++ b/.vsts-ci/linux-internal.yml @@ -0,0 +1,115 @@ +# Pipeline to run Linux CI internally +name: PR-$(System.PullRequest.PullRequestNumber)-$(Date:yyyyMMdd)$(Rev:.rr) +trigger: + # Batch merge builds together while a merge build is running + batch: true + branches: + include: + - master + - release* + - feature* + paths: + include: + - '*' + exclude: + - .vsts-ci/misc-analysis.yml + - .github/ISSUE_TEMPLATE/* + - .github/workflows/* + - .dependabot/config.yml + - .pipelines/* + - test/perf/* +pr: + branches: + include: + - master + - release* + - feature* + paths: + include: + - '*' + exclude: + - .dependabot/config.yml + - .github/ISSUE_TEMPLATE/* + - .github/workflows/* + - .vsts-ci/misc-analysis.yml + - .vsts-ci/windows.yml + - .vsts-ci/windows/* + - tools/cgmanifest.json + - LICENSE.txt + - test/common/markdown/* + - test/perf/* + - tools/releaseBuild/* + - tools/install* + - tools/releaseBuild/azureDevOps/templates/* + - README.md + - .spelling + - .pipelines/* + +variables: + DOTNET_CLI_TELEMETRY_OPTOUT: 1 + POWERSHELL_TELEMETRY_OPTOUT: 1 + DOTNET_NOLOGO: 1 + __SuppressAnsiEscapeSequences: 1 + nugetMultiFeedWarnLevel: none + +resources: + repositories: + - repository: Docker + type: github + endpoint: PowerShell + name: PowerShell/PowerShell-Docker + ref: master + +stages: +- stage: BuildLinuxStage + displayName: Build for Linux + jobs: + - template: templates/ci-build.yml + parameters: + pool: ubuntu-20.04 + jobName: linux_build + displayName: linux Build + +- stage: TestUbuntu + displayName: Test for Ubuntu + dependsOn: [BuildLinuxStage] + jobs: + - template: templates/nix-test.yml + parameters: + name: Ubuntu + pool: ubuntu-20.04 + purpose: UnelevatedPesterTests + tagSet: CI + + - template: templates/nix-test.yml + parameters: + name: Ubuntu + pool: ubuntu-20.04 + purpose: ElevatedPesterTests + tagSet: CI + + - template: templates/nix-test.yml + parameters: + name: Ubuntu + pool: ubuntu-20.04 + purpose: UnelevatedPesterTests + tagSet: Others + + - template: templates/nix-test.yml + parameters: + name: Ubuntu + pool: ubuntu-20.04 + purpose: ElevatedPesterTests + tagSet: Others + + - template: templates/verify-xunit.yml + parameters: + pool: ubuntu-20.04 + +- stage: PackageLinux + displayName: Package Linux + dependsOn: ["BuildLinuxStage"] + jobs: + - template: linux/templates/packaging.yml + parameters: + pool: ubuntu-20.04 diff --git a/.vsts-ci/linux.yml b/.vsts-ci/linux.yml new file mode 100644 index 00000000000..5d9dc663e1c --- /dev/null +++ b/.vsts-ci/linux.yml @@ -0,0 +1,79 @@ +parameters: + - name: ContainerPattern + displayName: | + Pattern to match JobName of the container. + Update this to force a container. + `.` will match everything + type: string + default: . + +name: PR-$(System.PullRequest.PullRequestNumber)-$(Date:yyyyMMdd)$(Rev:.rr) +trigger: + # Batch merge builds together while a merge build is running + batch: true + branches: + include: + - master + - release* + - feature* + paths: + include: + - '*' + exclude: + - .vsts-ci/misc-analysis.yml + - .github/ISSUE_TEMPLATE/* + - .github/workflows/* + - .dependabot/config.yml + - .pipelines/* + - test/perf/* +pr: + branches: + include: + - master + - release* + - feature* + paths: + include: + - .vsts-ci/linux.yml + - .vsts-ci/linux/templates/packaging.yml + - assets/manpage/* + - build.psm1 + - global.json + - nuget.config + - PowerShell.Common.props + - src/*.csproj + - tools/ci.psm1 + - tools/packaging/* + +variables: + DOTNET_CLI_TELEMETRY_OPTOUT: 1 + POWERSHELL_TELEMETRY_OPTOUT: 1 + DOTNET_NOLOGO: 1 + __SuppressAnsiEscapeSequences: 1 + nugetMultiFeedWarnLevel: none + +resources: + repositories: + - repository: Docker + type: github + endpoint: PowerShell + name: PowerShell/PowerShell-Docker + ref: master + +stages: +- stage: BuildLinuxStage + displayName: Build for Linux + jobs: + - template: templates/ci-build.yml + parameters: + pool: ubuntu-latest + jobName: linux_build + displayName: linux Build + +- stage: PackageLinux + displayName: Package Linux + dependsOn: ["BuildLinuxStage"] + jobs: + - template: linux/templates/packaging.yml + parameters: + pool: ubuntu-latest diff --git a/.vsts-ci/linux/templates/packaging.yml b/.vsts-ci/linux/templates/packaging.yml new file mode 100644 index 00000000000..8f77b8e24a0 --- /dev/null +++ b/.vsts-ci/linux/templates/packaging.yml @@ -0,0 +1,99 @@ +parameters: + pool: 'ubuntu-20.04' + parentJobs: [] + name: 'Linux' + +jobs: +- job: ${{ parameters.name }}_packaging + dependsOn: + ${{ parameters.parentJobs }} + pool: + vmImage: ${{ parameters.pool }} + + displayName: ${{ parameters.name }} packaging + + steps: + - task: UseDotNet@2 + displayName: 'Use .NET Core sdk' + inputs: + useGlobalJson: true + packageType: 'sdk' + + - pwsh: | + Get-ChildItem -Path env: | Out-String -width 9999 -Stream | write-Verbose -Verbose + displayName: Capture Environment + condition: succeededOrFailed() + + - task: DownloadBuildArtifacts@0 + displayName: 'Download build artifacts' + inputs: + downloadType: specific + itemPattern: | + build/**/* + downloadPath: '$(System.ArtifactsDirectory)' + + - pwsh: | + Get-ChildItem "$(System.ArtifactsDirectory)\*" -Recurse + displayName: 'Capture Artifacts Directory' + continueOnError: true + + - pwsh: | + Import-Module .\build.psm1 + Start-PSBootstrap -Scenario Package + displayName: Bootstrap + + - pwsh: | + Import-Module ./build.psm1 + displayName: 'Capture Artifacts Directory' + continueOnError: true + + - task: ExtractFiles@1 + displayName: 'Extract Build ZIP' + inputs: + archiveFilePatterns: '$(System.ArtifactsDirectory)/build/build.zip' + destinationFolder: '$(System.ArtifactsDirectory)/bins' + + - bash: | + find "$(System.ArtifactsDirectory)/bins" -type d -exec chmod +rwx {} \; + find "$(System.ArtifactsDirectory)/bins" -type f -exec chmod +rw {} \; + displayName: 'Fix permissions' + continueOnError: true + + - pwsh: | + Get-ChildItem "$(System.ArtifactsDirectory)\bins\*" -Recurse -ErrorAction SilentlyContinue + displayName: 'Capture Extracted Build ZIP' + continueOnError: true + + - pwsh: | + Import-Module .\tools\ci.psm1 + Restore-PSOptions -PSOptionsPath '$(System.ArtifactsDirectory)\build\psoptions.json' + $options = (Get-PSOptions) + $rootPath = '$(System.ArtifactsDirectory)\bins' + $originalRootPath = Split-Path -path $options.Output + $path = Join-Path -path $rootPath -ChildPath (split-path -leaf -path $originalRootPath) + $pwshPath = Join-Path -path $path -ChildPath 'pwsh' + chmod a+x $pwshPath + $options.Output = $pwshPath + Set-PSOptions $options + Invoke-CIFinish + displayName: Packaging Tests + condition: succeeded() + + - pwsh: | + Get-ChildItem "${env:BUILD_ARTIFACTSTAGINGDIRECTORY}\*.deb" -Recurse | ForEach-Object { + $packagePath = $_.FullName + Write-Host "Uploading $packagePath" + Write-Host "##vso[artifact.upload containerfolder=deb;artifactname=deb]$packagePath" + } + Get-ChildItem "${env:BUILD_ARTIFACTSTAGINGDIRECTORY}\*.rpm" -Recurse | ForEach-Object { + $packagePath = $_.FullName + Write-Host "Uploading $packagePath" + Write-Host "##vso[artifact.upload containerfolder=rpm;artifactname=rpm]$packagePath" + } + Get-ChildItem "${env:BUILD_ARTIFACTSTAGINGDIRECTORY}\*.tar.gz" -Recurse | ForEach-Object { + $packagePath = $_.FullName + Write-Host "Uploading $packagePath" + Write-Host "##vso[artifact.upload containerfolder=rpm;artifactname=rpm]$packagePath" + } + displayName: Upload packages + retryCountOnTaskFailure: 2 diff --git a/.vsts-ci/mac.yml b/.vsts-ci/mac.yml new file mode 100644 index 00000000000..4d3681edca1 --- /dev/null +++ b/.vsts-ci/mac.yml @@ -0,0 +1,114 @@ +name: PR-$(System.PullRequest.PullRequestNumber)-$(Date:yyyyMMdd)$(Rev:.rr) +trigger: + # Batch merge builds together while a merge build is running + batch: true + branches: + include: + - master + - release* + - feature* + paths: + include: + - '*' + exclude: + - tools/releaseBuild/**/* + - .vsts-ci/misc-analysis.yml + - .github/ISSUE_TEMPLATE/* + - .github/workflows/* + - .dependabot/config.yml + - .pipelines/* + - test/perf/* +pr: + branches: + include: + - master + - release* + - feature* + paths: + include: + - '*' + exclude: + - .dependabot/config.yml + - .github/ISSUE_TEMPLATE/* + - .github/workflows/* + - .vsts-ci/misc-analysis.yml + - .vsts-ci/windows.yml + - .vsts-ci/windows/* + - tools/cgmanifest.json + - LICENSE.txt + - test/common/markdown/* + - test/perf/* + - tools/packaging/* + - tools/releaseBuild/* + - tools/releaseBuild/azureDevOps/templates/* + - README.md + - .spelling + - .pipelines/* + +variables: + DOTNET_CLI_TELEMETRY_OPTOUT: 1 + POWERSHELL_TELEMETRY_OPTOUT: 1 + DOTNET_NOLOGO: 1 + # Turn off Homebrew analytics + HOMEBREW_NO_ANALYTICS: 1 + __SuppressAnsiEscapeSequences: 1 + nugetMultiFeedWarnLevel: none + +resources: +- repo: self + clean: true + +stages: +- stage: BuildMac + displayName: Build for macOS + jobs: + - template: templates/ci-build.yml + parameters: + pool: macOS-latest + jobName: mac_build + displayName: macOS Build + +- stage: TestMac + displayName: Test for macOS + jobs: + - template: templates/nix-test.yml + parameters: + purpose: UnelevatedPesterTests + tagSet: CI + + - template: templates/nix-test.yml + parameters: + purpose: ElevatedPesterTests + tagSet: CI + + - template: templates/nix-test.yml + parameters: + purpose: UnelevatedPesterTests + tagSet: Others + + - template: templates/nix-test.yml + parameters: + purpose: ElevatedPesterTests + tagSet: Others + + - template: templates/verify-xunit.yml + parameters: + pool: macOS-latest + +- stage: PackageMac + dependsOn: ['BuildMac'] + displayName: Package macOS (bootstrap only) + jobs: + - job: macos_packaging + pool: + vmImage: macOS-latest + + displayName: macOS packaging (bootstrap only) + steps: + - checkout: self + clean: true + - pwsh: | + import-module ./build.psm1 + start-psbootstrap -Scenario package + displayName: Bootstrap packaging + condition: succeededOrFailed() diff --git a/.vsts-ci/misc-analysis/generateMarkdownMatrix.yml b/.vsts-ci/misc-analysis/generateMarkdownMatrix.yml new file mode 100644 index 00000000000..56a43accd55 --- /dev/null +++ b/.vsts-ci/misc-analysis/generateMarkdownMatrix.yml @@ -0,0 +1,46 @@ +parameters: + - name: jobName + - name: taskName + +jobs: +- job: ${{ parameters.jobName }} + displayName: Generate Markdown Matrix + + pool: + vmImage: ubuntu-20.04 + + variables: + - name: repoPath + value: $(Agent.BuildDirectory)/$(repoFolder) + + steps: + - checkout: self + clean: true + path: $(repoFolder) + + - powershell: | + $matrix = @{} + $matrix += @{ + 'root' = @{ + markdown_folder = "$(repoPath)" + markdown_recurse = $false + } + } + Get-ChildItem -path '$(repoPath)' -Directory | Foreach-Object { + $folder = $_ + $matrix += @{ + $_.Name = @{ + markdown_folder = $_.fullName + markdown_recurse = $true + } + } + } + + $matrixJson = $matrix | ConvertTo-Json -Compress + $variableName = "matrix" + $command = "vso[task.setvariable variable=$variableName;isoutput=true]$($matrixJson)" + Write-Verbose "sending command: '$command'" + Write-Host "##$command" + displayName: Create Matrix + condition: succeededOrFailed() + name: ${{ parameters.taskName }} diff --git a/.vsts-ci/psresourceget-acr.yml b/.vsts-ci/psresourceget-acr.yml new file mode 100644 index 00000000000..194c7ba9f57 --- /dev/null +++ b/.vsts-ci/psresourceget-acr.yml @@ -0,0 +1,155 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +name: PR-$(System.PullRequest.PullRequestNumber)-$(Date:yyyyMMdd)$(Rev:.rr) +trigger: + # Batch merge builds together while a merge build is running + batch: true + branches: + include: + - master + - release* + - feature* + paths: + include: + - '*' + exclude: + - .vsts-ci/misc-analysis.yml + - .github/ISSUE_TEMPLATE/* + - .github/workflows/* + - .dependabot/config.yml + - test/perf/* + - .pipelines/* +pr: + branches: + include: + - master + - release* + - feature* + paths: + include: + - '*' + exclude: + - .dependabot/config.yml + - .github/ISSUE_TEMPLATE/* + - .github/workflows/* + - .vsts-ci/misc-analysis.yml + - tools/cgmanifest.json + - LICENSE.txt + - test/common/markdown/* + - test/perf/* + - tools/packaging/* + - tools/releaseBuild/* + - tools/releaseBuild/azureDevOps/templates/* + - README.md + - .spelling + - .pipelines/* + +variables: + GIT_CONFIG_PARAMETERS: "'core.autocrlf=false'" + DOTNET_CLI_TELEMETRY_OPTOUT: 1 + POWERSHELL_TELEMETRY_OPTOUT: 1 + DOTNET_NOLOGO: 1 + __SuppressAnsiEscapeSequences: 1 + NugetSecurityAnalysisWarningLevel: none + nugetMultiFeedWarnLevel: none + +resources: +- repo: self + clean: true + +stages: +- stage: BuildWin + displayName: Build for Windows + jobs: + - template: templates/ci-build.yml + +- stage: TestWin + displayName: Test PSResourceGetACR + jobs: + - job: win_test_ACR + displayName: PSResourceGet ACR Tests + pool: + vmImage: 'windows-latest' + + steps: + - pwsh: | + Get-ChildItem -Path env: + displayName: Capture Environment + condition: succeededOrFailed() + + - task: DownloadBuildArtifacts@0 + displayName: 'Download Build Artifacts' + inputs: + downloadType: specific + itemPattern: | + build/**/* + downloadPath: '$(System.ArtifactsDirectory)' + + - pwsh: | + Get-ChildItem "$(System.ArtifactsDirectory)\*" -Recurse + displayName: 'Capture Artifacts Directory' + continueOnError: true + + - pwsh: | + # Remove "Program Files\dotnet" from the env variable PATH, so old SDKs won't affect us. + Write-Host "Old Path:" + Write-Host $env:Path + + $dotnetPath = Join-Path $env:SystemDrive 'Program Files\dotnet' + $paths = $env:Path -split ";" | Where-Object { -not $_.StartsWith($dotnetPath) } + $env:Path = $paths -join ";" + + Write-Host "New Path:" + Write-Host $env:Path + + # Bootstrap + Import-Module .\tools\ci.psm1 + Invoke-CIInstall + displayName: Bootstrap + + - pwsh: | + Install-Module -Name 'Microsoft.PowerShell.SecretManagement' -force -SkipPublisherCheck -AllowClobber + Install-Module -Name 'Microsoft.PowerShell.SecretStore' -force -SkipPublisherCheck -AllowClobber + $vaultPassword = ConvertTo-SecureString $("a!!"+ (Get-Random -Maximum ([int]::MaxValue))) -AsPlainText -Force + Set-SecretStoreConfiguration -Authentication None -Interaction None -Confirm:$false -Password $vaultPassword + Register-SecretVault -Name SecretStore -ModuleName Microsoft.PowerShell.SecretStore -DefaultVault + displayName: 'Install Secret store' + + - task: AzurePowerShell@5 + inputs: + azureSubscription: PSResourceGetACR + azurePowerShellVersion: LatestVersion + ScriptType: InlineScript + pwsh: true + inline: | + Write-Verbose -Verbose "Getting Azure Container Registry" + Get-AzContainerRegistry -ResourceGroupName 'PSResourceGet' -Name 'psresourcegettest' | Select-Object -Property * + Write-Verbose -Verbose "Setting up secret for Azure Container Registry" + $azt = Get-AzAccessToken + $tenantId = $azt.TenantID + Set-Secret -Name $tenantId -Secret $azt.Token -Verbose + $vstsCommandString = "vso[task.setvariable variable=TenantId]$tenantId" + Write-Host "sending " + $vstsCommandString + Write-Host "##$vstsCommandString" + displayName: 'Setup Azure Container Registry secret' + + - pwsh: | + Import-Module .\build.psm1 -force + Import-Module .\tools\ci.psm1 + Restore-PSOptions -PSOptionsPath '$(System.ArtifactsDirectory)\build\psoptions.json' + $options = (Get-PSOptions) + $path = split-path -path $options.Output + $rootPath = split-Path -path $path + Expand-Archive -Path '$(System.ArtifactsDirectory)\build\build.zip' -DestinationPath $rootPath -Force + + $pwshExe = Get-ChildItem -Path $rootPath -Recurse -Filter pwsh.exe | Select-Object -First 1 + + $outputFilePath = "$(Build.SourcesDirectory)\test\powershell\Modules\Microsoft.PowerShell.PSResourceGet\ACRTests.xml" + $cmdline = "`$env:ACRTESTS = 'true'; Invoke-Pester -Path '$(Build.SourcesDirectory)\test\powershell\Modules\Microsoft.PowerShell.PSResourceGet\Microsoft.PowerShell.PSResourceGet.Tests.ps1' -TestName 'PSResourceGet - ACR tests' -OutputFile $outputFilePath -OutputFormat NUnitXml" + Write-Verbose -Verbose "Running $cmdline" + + & $pwshExe -Command $cmdline + + Publish-TestResults -Title "PSResourceGet - ACR tests" -Path $outputFilePath -Type NUnit + displayName: 'PSResourceGet ACR functional tests using AzAuth' diff --git a/.vsts-ci/sshremoting-tests.yml b/.vsts-ci/sshremoting-tests.yml new file mode 100644 index 00000000000..72c5710016b --- /dev/null +++ b/.vsts-ci/sshremoting-tests.yml @@ -0,0 +1,97 @@ +name: PR-$(System.PullRequest.PullRequestNumber)-$(Date:yyyyMMdd)$(Rev:.rr) +trigger: + # Batch merge builds together while a merge build is running + batch: true + branches: + include: + - master + - release* + - feature* + paths: + include: + - '/src/System.Management.Automation/engine/*' + - '/test/SSHRemoting/*' +pr: + branches: + include: + - master + - release* + - feature* + paths: + include: + - '/src/System.Management.Automation/engine/*' + - '/test/SSHRemoting/*' + +variables: + - name: DOTNET_CLI_TELEMETRY_OPTOUT + value: 1 + - name: POWERSHELL_TELEMETRY_OPTOUT + value: 1 + - name: DOTNET_NOLOGO + value: 1 + - name: __SuppressAnsiEscapeSequences + value: 1 + - name: NugetSecurityAnalysisWarningLevel + value: none +# Prevents auto-injection of nuget-security-analysis@0 + - name: skipNugetSecurityAnalysis + value: true + + +resources: +- repo: self + clean: true +jobs: +- job: SSHRemotingTests + pool: + vmImage: ubuntu-20.04 + container: mcr.microsoft.com/powershell/test-deps:ubuntu-18.04 + displayName: SSH Remoting Tests + + steps: + - pwsh: | + Get-ChildItem -Path env: | Out-String -width 9999 -Stream | write-Verbose -Verbose + displayName: Capture Environment + condition: succeededOrFailed() + + - pwsh: Write-Host "##vso[build.updatebuildnumber]$env:BUILD_SOURCEBRANCHNAME-$env:BUILD_SOURCEVERSION-$((get-date).ToString("yyyyMMddhhmmss"))" + displayName: Set Build Name for Non-PR + condition: ne(variables['Build.Reason'], 'PullRequest') + + - template: /tools/releaseBuild/azureDevOps/templates/insert-nuget-config-azfeed.yml + + - pwsh: | + sudo apt-get update + sudo apt-get install -y git + displayName: Install Github + condition: succeeded() + + - pwsh: | + Import-Module .\tools\ci.psm1 + Invoke-CIInstall -SkipUser + displayName: Bootstrap + condition: succeededOrFailed() + + - pwsh: | + Import-Module .\tools\ci.psm1 + Invoke-CIBuild + displayName: Build + condition: succeeded() + + - pwsh: | + Import-Module .\tools\ci.psm1 + Restore-PSOptions + $options = (Get-PSOptions) + Import-Module .\test\tools\Modules\HelpersRemoting + Install-SSHRemoting -PowerShellFilePath $options.Output + displayName: Install SSH Remoting + condition: succeeded() + + - pwsh: | + Import-Module .\tools\ci.psm1 + Restore-PSOptions + $options = (Get-PSOptions) + Import-Module .\build.psm1 + Start-PSPester -Path test/SSHRemoting -powershell $options.Output -OutputFile "$PWD/sshTestResults.xml" + displayName: Test + condition: succeeded() diff --git a/.vsts-ci/templates/ci-build.yml b/.vsts-ci/templates/ci-build.yml new file mode 100644 index 00000000000..5ec458c3c5a --- /dev/null +++ b/.vsts-ci/templates/ci-build.yml @@ -0,0 +1,87 @@ +parameters: + - name: pool + default: 'windows-latest' + - name: imageName + default: 'PSWindows11-ARM64' + - name: jobName + default: 'win_build' + - name: displayName + default: Windows Build + - name: PoolType + default: AzDoHosted + type: string + values: + - AzDoHosted + - 1esHosted + +jobs: +- job: ${{ parameters.jobName }} + pool: + ${{ if eq( parameters.PoolType, 'AzDoHosted') }}: + vmImage: ${{ parameters.pool }} + ${{ else }}: + name: ${{ parameters.pool }} + demands: + - ImageOverride -equals ${{ parameters.imageName }} + + displayName: ${{ parameters.displayName }} + + steps: + - powershell: | + [System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]::Tls12 + $pwsh = Get-Command pwsh -ErrorAction SilentlyContinue -CommandType Application + + if ($null -eq $pwsh) { + $powerShellPath = Join-Path -Path $env:AGENT_TEMPDIRECTORY -ChildPath 'powershell' + Invoke-WebRequest -Uri https://raw.githubusercontent.com/PowerShell/PowerShell/master/tools/install-powershell.ps1 -outfile ./install-powershell.ps1 + ./install-powershell.ps1 -Destination $powerShellPath + $vstsCommandString = "vso[task.setvariable variable=PATH]$powerShellPath;$env:PATH" + Write-Host "sending " + $vstsCommandString + Write-Host "##$vstsCommandString" + } + + displayName: Install PowerShell + + - checkout: self + fetchDepth: 1000 + + - pwsh: | + Get-ChildItem -Path env: | Out-String -width 9999 -Stream | write-Verbose -Verbose + displayName: Capture Environment + condition: succeededOrFailed() + + - pwsh: Write-Host "##vso[build.updatebuildnumber]$env:BUILD_SOURCEBRANCHNAME-$env:BUILD_SOURCEVERSION-$((get-date).ToString("yyyyMMddhhmmss"))" + displayName: Set Build Name for Non-PR + condition: ne(variables['Build.Reason'], 'PullRequest') + + - ${{ if ne(variables['UseAzDevOpsFeed'], '') }}: + - template: /tools/releaseBuild/azureDevOps/templates/insert-nuget-config-azfeed.yml + + - task: UseDotNet@2 + displayName: 'Use .NET Core sdk' + inputs: + useGlobalJson: true + packageType: 'sdk' + + - pwsh: | + Import-Module .\tools\ci.psm1 + Invoke-CIInstall -SkipUser + Write-Verbose -Verbose "Start Sync-PSTags" + Sync-PSTags -AddRemoteIfMissing + Write-Verbose -Verbose "End Sync-PSTags" + displayName: Bootstrap + condition: succeeded() + + - pwsh: | + Import-Module .\tools\ci.psm1 + Invoke-CIBuild + displayName: Build + condition: succeeded() + + - pwsh: | + Import-Module .\tools\ci.psm1 + Restore-PSOptions + Invoke-CIxUnit -SkipFailing + displayName: xUnit Tests + condition: succeeded() + continueOnError: true diff --git a/.vsts-ci/templates/credscan.yml b/.vsts-ci/templates/credscan.yml new file mode 100644 index 00000000000..60094ff3d77 --- /dev/null +++ b/.vsts-ci/templates/credscan.yml @@ -0,0 +1,29 @@ +parameters: + pool: 'windows-latest' + jobName: 'credscan' + displayName: Secret Scan + +jobs: +- job: ${{ parameters.jobName }} + pool: + vmImage: ${{ parameters.pool }} + + displayName: ${{ parameters.displayName }} + + steps: + - task: securedevelopmentteam.vss-secure-development-tools.build-task-credscan.CredScan@2 + displayName: 'Scan for Secrets' + inputs: + suppressionsFile: tools/credScan/suppress.json + toolMajorVersion: V2 + debugMode: false + + - task: securedevelopmentteam.vss-secure-development-tools.build-task-publishsecurityanalysislogs.PublishSecurityAnalysisLogs@2 + displayName: 'Publish Secret Scan Logs to Build Artifacts' + continueOnError: true + + - task: securedevelopmentteam.vss-secure-development-tools.build-task-postanalysis.PostAnalysis@1 + displayName: 'Check for Failures' + inputs: + CredScan: true + ToolLogsNotFoundAction: Error diff --git a/.vsts-ci/templates/install-ps-phase.yml b/.vsts-ci/templates/install-ps-phase.yml new file mode 100644 index 00000000000..4e650273264 --- /dev/null +++ b/.vsts-ci/templates/install-ps-phase.yml @@ -0,0 +1,42 @@ +parameters: + pool: 'ubuntu-latest' + jobName: 'none' + scriptName: '' + container: '' + verification: '' + continueOnError: false + +jobs: + +- job: ${{ parameters.jobName }} + variables: + scriptName: ${{ parameters.scriptName }} + + ${{ if ne(parameters.container, '') }}: + container: ${{ parameters.container }} + + pool: + vmImage: ${{ parameters.pool }} + + displayName: ${{ parameters.jobName }} + + steps: + - pwsh: | + Get-ChildItem -Path env: | Out-String -width 9999 -Stream | write-Verbose -Verbose + displayName: Capture Environment + condition: succeededOrFailed() + + - powershell: Write-Host "##vso[build.updatebuildnumber]$env:BUILD_SOURCEBRANCHNAME-$env:BUILD_SOURCEVERSION-$((get-date).ToString("yyyyMMddhhmmss"))" + displayName: Set Build Name for Non-PR + condition: ne(variables['Build.Reason'], 'PullRequest') + + - bash: | + $(scriptName) + displayName: Run Script - $(scriptName) + condition: succeededOrFailed() + continueOnError: ${{ parameters.continueOnError }} + + - ${{ if ne(parameters.verification, '') }}: + - pwsh: ${{ parameters.verification }} + displayName: Verification + continueOnError: ${{ parameters.continueOnError }} diff --git a/.vsts-ci/templates/nix-test.yml b/.vsts-ci/templates/nix-test.yml new file mode 100644 index 00000000000..214ae14b2c6 --- /dev/null +++ b/.vsts-ci/templates/nix-test.yml @@ -0,0 +1,25 @@ +parameters: + pool: 'macOS-latest' + purpose: '' + tagSet: 'CI' + name: 'mac' + +jobs: +- job: ${{ parameters.name }}_test_${{ parameters.purpose }}_${{ parameters.tagSet }} + + pool: + vmImage: ${{ parameters.pool }} + + displayName: ${{ parameters.name }} Test - ${{ parameters.purpose }} - ${{ parameters.tagSet }} + + steps: + - task: UseDotNet@2 + displayName: 'Use .NET Core sdk' + inputs: + useGlobalJson: true + packageType: 'sdk' + + - template: ./test/nix-test-steps.yml + parameters: + purpose: ${{ parameters.purpose }} + tagSet: ${{ parameters.tagSet }} diff --git a/.vsts-ci/templates/test/nix-container-test.yml b/.vsts-ci/templates/test/nix-container-test.yml new file mode 100644 index 00000000000..37c60a4c53b --- /dev/null +++ b/.vsts-ci/templates/test/nix-container-test.yml @@ -0,0 +1,36 @@ +parameters: + pool: 'macOS-latest' + purpose: '' + tagSet: 'CI' + name: 'mac' + +jobs: +- job: ${{ parameters.name }}_test_${{ parameters.purpose }}_${{ parameters.tagSet }} + + dependsOn: + - getContainerJob + + variables: + __INCONTAINER: 1 + getContainerJob: $[ dependencies.getContainerJob.outputs['getContainerTask.containerName'] ] + containerBuildName: $[ dependencies.getContainerJob.outputs['getContainerTask.containerBuildName'] ] + + container: $[ variables.getContainerJob ] + + pool: + vmImage: ${{ parameters.pool }} + + displayName: ${{ parameters.name }} Test - ${{ parameters.purpose }} - ${{ parameters.tagSet }} + + steps: + - task: UseDotNet@2 + displayName: 'Use .NET Core sdk' + inputs: + useGlobalJson: true + packageType: 'sdk' + + - template: ./nix-test-steps.yml + parameters: + purpose: ${{ parameters.purpose }} + tagSet: ${{ parameters.tagSet }} + buildName: $(containerBuildName) diff --git a/.vsts-ci/templates/test/nix-test-steps.yml b/.vsts-ci/templates/test/nix-test-steps.yml new file mode 100644 index 00000000000..f15d59ea73a --- /dev/null +++ b/.vsts-ci/templates/test/nix-test-steps.yml @@ -0,0 +1,60 @@ +parameters: + purpose: '' + tagSet: 'CI' + buildName: 'Ubuntu' + +steps: + - pwsh: | + Get-ChildItem -Path env: | Out-String -width 9999 -Stream | write-Verbose -Verbose + displayName: Capture Environment + condition: succeededOrFailed() + + - task: DownloadBuildArtifacts@0 + displayName: 'Download build artifacts' + inputs: + downloadType: specific + itemPattern: | + build/**/* + downloadPath: '$(System.ArtifactsDirectory)' + + - pwsh: | + Get-ChildItem "$(System.ArtifactsDirectory)\*" -Recurse + displayName: 'Capture Artifacts Directory' + continueOnError: true + + - pwsh: | + Import-Module .\tools\ci.psm1 + Invoke-CIInstall -SkipUser + displayName: Bootstrap + + - task: ExtractFiles@1 + displayName: 'Extract Build ZIP' + inputs: + archiveFilePatterns: '$(System.ArtifactsDirectory)/build/build.zip' + destinationFolder: '$(System.ArtifactsDirectory)/bins' + + - bash: | + find "$(System.ArtifactsDirectory)/bins" -type d -exec chmod +rwx {} \; + find "$(System.ArtifactsDirectory)/bins" -type f -exec chmod +rw {} \; + displayName: 'Fix permissions' + continueOnError: true + + - pwsh: | + Get-ChildItem "$(System.ArtifactsDirectory)\bins\*" -Recurse -ErrorAction SilentlyContinue + displayName: 'Capture Extracted Build ZIP' + continueOnError: true + + - pwsh: | + Import-Module .\tools\ci.psm1 + Restore-PSOptions -PSOptionsPath '$(System.ArtifactsDirectory)\build\psoptions.json' + $options = (Get-PSOptions) + $rootPath = '$(System.ArtifactsDirectory)\bins' + $originalRootPath = Split-Path -path $options.Output + $path = Join-Path -path $rootPath -ChildPath (split-path -leaf -path $originalRootPath) + $pwshPath = Join-Path -path $path -ChildPath 'pwsh' + chmod a+x $pwshPath + $options.Output = $pwshPath + Set-PSOptions $options + Invoke-CITest -Purpose '${{ parameters.purpose }}' -TagSet '${{ parameters.tagSet }}' -TitlePrefix '${{ parameters.buildName }}' + displayName: Test + condition: succeeded() diff --git a/.vsts-ci/templates/verify-xunit.yml b/.vsts-ci/templates/verify-xunit.yml new file mode 100644 index 00000000000..b43cb9339f9 --- /dev/null +++ b/.vsts-ci/templates/verify-xunit.yml @@ -0,0 +1,33 @@ +parameters: + parentJobs: [] + pool: 'windows-latest' + jobName: 'xunit_verify' + +jobs: +- job: verify_xunit + displayName: Verify xUnit Results + pool: + vmImage: ${{ parameters.pool }} + dependsOn: + ${{ parameters.parentJobs }} + steps: + - task: DownloadBuildArtifacts@0 + displayName: 'Download build artifacts' + inputs: + downloadType: specific + itemPattern: | + xunit/**/* + downloadPath: '$(System.ArtifactsDirectory)' + + - pwsh: | + dir "$(System.ArtifactsDirectory)\*" -Recurse + displayName: 'Capture artifacts directory' + continueOnError: true + + - pwsh: | + Import-Module .\tools\ci.psm1 + $xUnitTestResultsFile = "$(System.ArtifactsDirectory)\xunit\xUnitTestResults.xml" + + Test-XUnitTestResults -TestResultsFile $xUnitTestResultsFile + displayName: Test + condition: succeeded() diff --git a/.vsts-ci/windows-arm64.yml b/.vsts-ci/windows-arm64.yml new file mode 100644 index 00000000000..4c75c1d31e0 --- /dev/null +++ b/.vsts-ci/windows-arm64.yml @@ -0,0 +1,94 @@ +name: PR-$(System.PullRequest.PullRequestNumber)-$(Date:yyyyMMdd)$(Rev:.rr) +trigger: + # Batch merge builds together while a merge build is running + batch: true + branches: + include: + - master + - release* + - feature* + paths: + include: + - '*' + exclude: + - .vsts-ci/misc-analysis.yml + - .github/ISSUE_TEMPLATE/* + - .dependabot/config.yml + - test/perf/* +pr: + branches: + include: + - master + - release* + - feature* + paths: + include: + - '*' + exclude: + - .dependabot/config.yml + - .github/ISSUE_TEMPLATE/* + - .vsts-ci/misc-analysis.yml + - tools/cgmanifest.json + - LICENSE.txt + - test/common/markdown/* + - test/perf/* + - tools/packaging/* + - tools/releaseBuild/* + - tools/releaseBuild/azureDevOps/templates/* + - README.md + - .spelling + +variables: + - name: GIT_CONFIG_PARAMETERS + value: "'core.autocrlf=false'" + - name: DOTNET_CLI_TELEMETRY_OPTOUT + value: 1 + - name: POWERSHELL_TELEMETRY_OPTOUT + value: 1 + - name: DOTNET_NOLOGO + value: 1 + - name: __SuppressAnsiEscapeSequences + value: 1 + - group: PoolNames + +resources: +- repo: self + clean: true + +stages: +- stage: BuildWin + displayName: Build for Windows + jobs: + - template: templates/ci-build.yml + parameters: + pool: $(armPool) + PoolType: 1esHosted + +- stage: TestWin + displayName: Test for Windows + jobs: + - template: templates/windows-test.yml + parameters: + purpose: UnelevatedPesterTests + tagSet: CI + pool: $(armPool) + + - template: templates/windows-test.yml + parameters: + purpose: ElevatedPesterTests + tagSet: CI + pool: $(armPool) + + - template: templates/windows-test.yml + parameters: + purpose: UnelevatedPesterTests + tagSet: Others + pool: $(armPool) + + - template: templates/windows-test.yml + parameters: + purpose: ElevatedPesterTests + tagSet: Others + pool: $(armPool) + + - template: templates/verify-xunit.yml diff --git a/.vsts-ci/windows.yml b/.vsts-ci/windows.yml new file mode 100644 index 00000000000..4171d09643d --- /dev/null +++ b/.vsts-ci/windows.yml @@ -0,0 +1,83 @@ +name: PR-$(System.PullRequest.PullRequestNumber)-$(Date:yyyyMMdd)$(Rev:.rr) +trigger: + # Batch merge builds together while a merge build is running + batch: true + branches: + include: + - master + - release* + - feature* + paths: + include: + - '*' + exclude: + - .vsts-ci/misc-analysis.yml + - .github/ISSUE_TEMPLATE/* + - .github/workflows/* + - .dependabot/config.yml + - test/perf/* + - .pipelines/* +pr: + branches: + include: + - master + - release* + - feature* + paths: + include: + - .vsts-ci/templates/* + - .vsts-ci/windows.yml + - '*.props' + - build.psm1 + - src/* + - test/* + - tools/buildCommon/* + - tools/ci.psm1 + - tools/WindowsCI.psm1 + exclude: + - test/common/markdown/* + - test/perf/* + +variables: + GIT_CONFIG_PARAMETERS: "'core.autocrlf=false'" + DOTNET_CLI_TELEMETRY_OPTOUT: 1 + POWERSHELL_TELEMETRY_OPTOUT: 1 + DOTNET_NOLOGO: 1 + __SuppressAnsiEscapeSequences: 1 + NugetSecurityAnalysisWarningLevel: none + nugetMultiFeedWarnLevel: none + +resources: +- repo: self + clean: true + +stages: +- stage: BuildWin + displayName: Build for Windows + jobs: + - template: templates/ci-build.yml + +- stage: TestWin + displayName: Test for Windows + jobs: + - template: templates/windows-test.yml + parameters: + purpose: UnelevatedPesterTests + tagSet: CI + + - template: templates/windows-test.yml + parameters: + purpose: ElevatedPesterTests + tagSet: CI + + - template: templates/windows-test.yml + parameters: + purpose: UnelevatedPesterTests + tagSet: Others + + - template: templates/windows-test.yml + parameters: + purpose: ElevatedPesterTests + tagSet: Others + + - template: templates/verify-xunit.yml diff --git a/.vsts-ci/windows/templates/windows-packaging.yml b/.vsts-ci/windows/templates/windows-packaging.yml new file mode 100644 index 00000000000..d23b745c30f --- /dev/null +++ b/.vsts-ci/windows/templates/windows-packaging.yml @@ -0,0 +1,111 @@ +parameters: + - name: pool + default: 'windows-latest' + - name: jobName + default: 'win_packaging' + - name: runtimePrefix + default: 'win7' + - name: architecture + default: 'x64' + - name: channel + default: 'preview' + +jobs: +- job: ${{ parameters.jobName }}_${{ parameters.channel }}_${{ parameters.architecture }} + + variables: + - name: repoFolder + value: PowerShell + - name: repoPath + value: $(Agent.BuildDirectory)\$(repoFolder) + - name: complianceRepoFolder + value: compliance + - name: complianceRepoPath + value: $(Agent.BuildDirectory)\$(complianceRepoFolder) + + pool: + vmImage: ${{ parameters.pool }} + + displayName: Windows Packaging - ${{ parameters.architecture }} - ${{ parameters.channel }} + + steps: + - checkout: self + clean: true + path: $(repoFolder) + + - checkout: ComplianceRepo + clean: true + path: $(complianceRepoFolder) + + - powershell: | + Get-ChildItem -Path env: | Out-String -width 9999 -Stream | write-Verbose -Verbose + displayName: Capture environment + condition: succeededOrFailed() + + - pwsh: | + $PSVersionTable + displayName: Capture PowerShell Version Table + condition: succeededOrFailed() + + - pwsh: | + Import-Module .\tools\ci.psm1 + Switch-PSNugetConfig -Source Public + displayName: Switch to public feeds + condition: succeeded() + workingDirectory: $(repoPath) + + - task: UseDotNet@2 + displayName: 'Use .NET Core sdk' + inputs: + useGlobalJson: true + packageType: 'sdk' + workingDirectory: $(repoPath) + + - pwsh: | + Import-Module .\tools\ci.psm1 + Invoke-CIInstall -SkipUser + displayName: Bootstrap + condition: succeeded() + workingDirectory: $(repoPath) + + - pwsh: | + Import-Module .\tools\ci.psm1 + New-CodeCoverageAndTestPackage + Invoke-CIFinish -Runtime ${{ parameters.runtimePrefix }}-${{ parameters.architecture }} -channel ${{ parameters.channel }} -Stage Build + displayName: Build + workingDirectory: $(repoPath) + + - template: Sbom.yml@ComplianceRepo + parameters: + BuildDropPath: '$(System.ArtifactsDirectory)/mainBuild' + Build_Repository_Uri: $(build.repository.uri) + displayName: SBOM + sourceScanPath: '$(repoPath)\tools' + signSBOM: false + + # This is needed as SBOM task removed the installed .NET and installs .NET 3.1 + - pwsh: | + Import-Module .\tools\ci.psm1 + Invoke-CIInstall -SkipUser + displayName: Bootstrap + condition: succeeded() + workingDirectory: $(repoPath) + + - pwsh: | + $manifestFolder = Join-Path -Path '$(System.ArtifactsDirectory)/mainBuild' -ChildPath '_manifest' + + if (-not (Test-Path $manifestFolder)) { + throw "_manifest folder does not exist under $(System.ArtifactsDirectory)/mainBuild" + } + + $null = New-Item -Path "$manifestFolder/spdx_2.2/bsi.json" -Verbose -Force + $null = New-Item -Path "$manifestFolder/spdx_2.2/manifest.cat" -Verbose -Force + + displayName: Create fake SBOM manifest signed files + + - pwsh: | + Import-Module .\tools\ci.psm1 + New-CodeCoverageAndTestPackage + Invoke-CIFinish -Runtime ${{ parameters.runtimePrefix }}-${{ parameters.architecture }} -channel ${{ parameters.channel }} -Stage Package + displayName: Package and Test + workingDirectory: $(repoPath) diff --git a/.vsts-ci/windows/windows-packaging.yml b/.vsts-ci/windows/windows-packaging.yml new file mode 100644 index 00000000000..6b73ca05723 --- /dev/null +++ b/.vsts-ci/windows/windows-packaging.yml @@ -0,0 +1,87 @@ +name: PR-$(System.PullRequest.PullRequestNumber)-$(Date:yyyyMMdd)$(Rev:.rr) +trigger: + # Batch merge builds together while a merge build is running + batch: true + branches: + include: + - master + - release* + - feature* + paths: + exclude: + - tests/* + - docs/* + - demos/* + - CHANGELOG/* + - .devcontainer/* + - .github/* + - .poshchan/* + - .vscode/* + - code-server/* + - docker/* + +pr: + branches: + include: + - master + - release* + - feature* + paths: + include: + - .vsts-ci/windows/*.yml + - assets/wix/* + - build.psm1 + - global.json + - nuget.config + - PowerShell.Common.props + - src/*.csproj + - test/packaging/windows/* + - tools/ci.psm1 + - tools/packaging/* + - tools/wix/* + +variables: + - name: GIT_CONFIG_PARAMETERS + value: "'core.autocrlf=false'" + - name: DOTNET_CLI_TELEMETRY_OPTOUT + value: 1 + - name: POWERSHELL_TELEMETRY_OPTOUT + value: 1 + - name: DOTNET_NOLOGO + value: 1 + - name: __SuppressAnsiEscapeSequences + value: 1 + - group: fakeNugetKey + - name: SBOMGenerator_Formats + value: spdx:2.2 + - name: nugetMultiFeedWarnLevel + value: none + +resources: + repositories: + - repository: ComplianceRepo + type: github + endpoint: PowerShell + name: PowerShell/compliance + ref: master + +stages: +- stage: PackagingWin + displayName: Packaging for Windows + dependsOn: [] # by specifying an empty array, this stage doesn't depend on the stage before it + jobs: + # Unlike daily builds, we do not upload nuget package to MyGet so we do not wait on tests to finish. + - template: templates/windows-packaging.yml + - template: templates/windows-packaging.yml + parameters: + channel: stable + architecture: x86 + - template: templates/windows-packaging.yml + parameters: + channel: preview + architecture: x86 + - template: templates/windows-packaging.yml + parameters: + channel: preview + architecture: arm64 + runtimePrefix: win diff --git a/ADOPTERS.md b/ADOPTERS.md new file mode 100644 index 00000000000..186b99dd093 --- /dev/null +++ b/ADOPTERS.md @@ -0,0 +1,39 @@ +# Adopters + + + +This is a list of adopters using PowerShell in production or in their products (in alphabetical order): + +* [Azure Cloud Shell](https://shell.azure.com/) provides a battery-included browser-based PowerShell environment used by Azure administrators to manage their environment. + It includes up-to-date PowerShell modules for `Azure`, `AzureAD`, `Exchange`, `Teams`, and many more. + More information about Azure Cloud Shell is available at [Azure Cloud Shell Overview.](https://learn.microsoft.com/azure/cloud-shell/overview) +* [Azure Functions - PowerShell](https://github.com/Azure/azure-functions-powershell-worker) is a serverless compute service to execute PowerShell scripts on the cloud without worrying about managing resources. + In addition, Azure Functions provides client tools such as [`Az.Functions`](https://www.powershellgallery.com/packages/Az.Functions), a cross-platform PowerShell module for managing function apps and service plans in the cloud. + For more information about Functions, please visit [functions overview](https://learn.microsoft.com/azure/azure-functions/functions-overview). +* [PowerShell Universal](https://ironmansoftware.com/powershell-universal) is a cross-platform web framework for PowerShell. + It provides the ability to create robust, interactive sites, REST APIs, and Electron-based desktop apps with PowerShell script. + More information about PowerShell Universal Dashboard is available at the [PowerShell Universal Dashboard Docs](https://docs.universaldashboard.io). +* [System Frontier](https://systemfrontier.com/solutions/powershell/) provides dynamically generated web GUIs and REST APIs for PowerShell and other scripting languages. + Enable non-admins like help desk and tier 1 support teams to execute secure web based tools on any platform `without admin rights`. + Configure flexible RBAC permissions from an intuitive interface, without a complex learning curve. + Script output along with all actions are audited. Manage up to 5,000 nodes for free with the [Community Edition](https://systemfrontier.com/solutions/community-edition/). +* [Amazon AWS](https://aws.com) supports PowerShell in a wide variety of its products including [AWS tools for PowerShell](https://github.com/aws/aws-tools-for-powershell), + [AWS Lambda Support For PowerShell](https://github.com/aws/aws-lambda-dotnet/tree/master/PowerShell) and [AWS PowerShell Tools for `CodeBuild`](https://docs.aws.amazon.com/powershell/latest/reference/items/CodeBuild_cmdlets.html) + as well as supporting PowerShell Core in both Windows and Linux EC2 Images. +* [Azure Resource Manager Deployment Scripts](https://learn.microsoft.com/azure/azure-resource-manager/templates/deployment-script-template) Complete the "last mile" of your Azure Resource Manager (ARM) template deployments with a Deployment Script, which enables you to run an arbitrary PowerShell script in the context of a deployment. + It is designed to let you complete tasks that should be part of a deployment, but are not possible in an ARM template today — for example, creating a Key Vault certificate or querying an external API for a new CIDR block. +* [Azure Pipelines Hosted Agents](https://learn.microsoft.com/azure/devops/pipelines/agents/hosted?view=azure-devops) Windows, Ubuntu, and macOS Agents used by Azure Pipelines customers have PowerShell pre-installed so that customers can make use of it for all their CI/CD needs. +* [GitHub Actions Virtual Environments for Hosted Runners](https://help.github.com/actions/reference/virtual-environments-for-github-hosted-runners) Windows, Ubuntu, and macOS virtual environments are used by customers of GitHub Actions include PowerShell out of the box. +* [GitHub Actions Python builds](https://github.com/actions/python-versions) GitHub Actions uses PowerShell to automate building Python from source for its runners. +* [Microsoft HoloLens](https://www.microsoft.com/hololens) makes extensive use of PowerShell 7+ throughout the development cycle to automate tasks such as firmware assembly and automated testing. +* [Power BI](https://powerbi.microsoft.com/) provides PowerShell users a set of cmdlets in [MicrosoftPowerBIMgmt](https://learn.microsoft.com/powershell/power-bi) module to manage and automate the Power BI service. + This is in addition to Power BI leveraging PowerShell, internally for various engineering systems and infrastructure for its service. +* [Windows 10 IoT Core](https://learn.microsoft.com/windows/iot-core/windows-iot-core) is a small form factor Windows edition for IoT devices and now you can easily include the [PowerShell package](https://github.com/ms-iot/iot-adk-addonkit/blob/master/Tools/IoTCoreImaging/Docs/Import-PSCoreRelease.md#Import-PSCoreRelease) in your imaging process. diff --git a/Analyzers.props b/Analyzers.props new file mode 100644 index 00000000000..6f906496c73 --- /dev/null +++ b/Analyzers.props @@ -0,0 +1,6 @@ + + + + + + diff --git a/CHANGELOG.md b/CHANGELOG.md index b25fb690fa4..73ffd17cdb6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,652 +1,3 @@ # Changelog -## v6.0.0-beta.6 - 2017-08-24 - -### Breaking change - -* Make invalid argument error messages for -File and -Command consistent and make exit codes consistent with Unix standards (#4573) - -### Engine updates and fixes - -* Make resource loading to work with PowerShell SxS installation (#4139) -* Add missing assemblies to TPA list to make Pwrshplughin.dll work (#4502) -* Make sure running `powershell` starts instance of the current version of PowerShell. (#4481) -* Make sure we only use Unicode output by default on Nano and IoT systems (#4074) -* Enable `powershell -WindowStyle` to work on Windows. (#4573) -* Enable enumeration of COM collections. (#4553) - -### General cmdlet updates and fixes - -* Fix Web CmdLets `-SkipHeaderValidation` to work with non-standard User-Agent headers. (#4479 & #4512) (Thanks @markekraus) -* Add Certificate authentication support for Web CmdLets. (#4646) (Thanks @markekraus) -* Add support for content headers to Web CmdLets. (#4494 & #4640) (Thanks @markekraus) -* Add support for converting enums to string (#4318) (Thanks @KirkMunro) -* Ignore casing when binding PSReadline KeyHandler functions (#4300) (Thanks @oising) -* Fix `Unblock-File` for the case of a read-only file. (#4395) (Thanks @iSazonov) -* Use supported API to set Central Access Policy ID (CAPID) in SACL. (#4496) -* Make `Start-Trace` support paths that require escaping in the underlying APIs (#3863) -* Removing `#if CORECLR` enabled, `Enable-PSRemoting` and `Disable-PSRemoting` (#2671) -* Enable WSManCredSSP cmdlets and add tests. (#4336) -* Use .NET Core's implementation for ShellExecute. (#4523) -* Fix SSH Remoting handling of KeyFileParameter when the path must be quoted. (#4529) -* Make Web CmdLets use HTML meta charset attribute value, if present (#4338) -* Move to .NET Core 2.0 final (#4603) - -### Build/test and code cleanup - -* Add Amazon Linux Docker image and enable related tests. (#4393) (Thanks @DarwinJS) -* Make MSI verify pre-requisites are installed. (#4602) (Thank @bergmeister) -* Fixed formatting issues in build files. (#4630) (Thanks @iSazonov) -* Make sure `install-powershell.sh` installs latest powershell on macOS, even if an old version is cached in brew. (#4509) (Thanks @richardszalay for reporting.) -* Fixes install scripts issue for macOS. (#4631) (Thanks @DarwinJS) -* Many stability improvements to our nightly code coverage automation. (#4313 & #4550) -* Remove hash validation from nanoserver-insider Docker file, due to frequent changes. (#4498) -* Update to make Travis-CI daily build badge more reliable. (#4522) -* Remove unused build files, build code, and product code. (#4532, #4580, #4590, #4589, #4588, #4587, #4586, #4583, #4582, #4581) -* Add additional acceptance tests for PowerShellGet. (#4531) -* Only publish a NuGet of the full PowerShell core package on daily builds and not merge. (#4517) -* Update nanoserver-insider Docker file due to breaking changes in the base image. (#4555) -* Cleanup engine tests (#4551) -* Fix intermittent failures in filesystem tests (#4566) -* Add tests for - * `New-WinEvent`. (#4384) - * tab completion. (#4560) - * various types. (#4503) - * CDXML CmdLets. (#4537) -* Only allow packaging of powershell, if it was built from a repo at the root of the file system named powershell. (#4569 & #4600) -* Update `Format-Hex` test cases to use -TestCase instead of foreach loops. (#3800) -* Added functionality to get code coverage for a single file locally. (#4556) - -### Documentation - -* Added Ilya (@iSazonov) as a Maintainer. (#4365) -* Grammar fix to the Pull Request Guide. (#4322) -* Add homebrew for macOS to install documentation. (#3838) -* Added a CodeOwner file. (#4565 & #4597) - -### Cleanup `#if CORECLR` code - -PowerShell 6.0 will be exclusively built on top of CoreCLR, -so we are removing a large amount of code that's built only for FullCLR. -To read more about this, check out [this blog post](https://blogs.msdn.microsoft.com/powershell/2017/07/14/powershell-6-0-roadmap-coreclr-backwards-compatibility-and-more/). - -## v6.0.0-beta.5 - 2017-08-02 - -### Breaking changes - -* Remove the `*-Counter` cmdlets in `Microsoft.PowerShell.Diagnostics` due to the use of unsupported APIs until a better solution is found. (#4303) -* Remove the `Microsoft.PowerShell.LocalAccounts` due to the use of unsupported APIs until a better solution is found. (#4302) - -### Engine updates and fixes - -* Fix the issue where PowerShell Core wasn't working on Windows 7 or Windows Server 2008 R2/2012 (non-R2). (#4463) -* `ValidateSetAttribute` enhancement: support set values to be dynamically generated from a custom `ValidateSetValueGenerator`. (#3784) (Thanks to @iSazonov!) -* Disable breaking into debugger on Ctrl+Break when running non-interactively. (#4283) (Thanks to @mwrock!) -* Give error instead of crashing if WSMan client library is not available. (#4387) -* Allow passing `$true`/`$false` as a parameter to scripts using `powershell.exe -File`. (#4178) -* Enable `DataRow`/`DataRowView` adapters in PowerShell Core to fix an issue with `DataTable` usage. (#4258) -* Fix an issue where PowerShell class static methods were being shared across `Runspace`s/`SessionState`s. (#4209) -* Fix array expression to not return null or throw error. (#4296) -* Fixes a CIM deserialization bug where corrupted CIM classes were instantiating non-CIM types. (#4234) -* Improve error message when `HelpMessage` property of `ParameterAttribute` is set to empty string. (#4334) -* Make `ShellExecuteEx` run in a STA thread. (#4362) - -### General cmdlet updates and fixes - -* Add `-SkipHeaderValidation` switch to `Invoke-WebRequest` and `Invoke-RestMethod` to support adding headers without validating the header value. (#4085) -* Add support for `Invoke-Item -Path `. (#4262) -* Fix `ConvertTo-Html` output when using a single column header. (#4276) -* Fix output of `Length` for `FileInfo` when using `Format-List`. (#4437) -* Fix an issue in implicit remoting where restricted sessions couldn't use `Get-FormatData –PowerShellVersion`. (#4222) -* Fix an issue where `Register-PSSessionConfiguration` fails if `SessionConfig` folder doesn't exist. (#4271) - -### Installer updates - -* Create script to install latest PowerShell from Microsoft package repositories (or Homebrew) on non-Windows platforms. (#3608) (Thanks to @DarwinJS!) -* Enable MSI upgrades rather than a side-by-side install. (#4259) -* Add a checkbox to open PowerShell after the Windows MSI installer has finished. (#4203) (Thanks to @bergmeister!) -* Add Amazon Linux compatibility to `install-powershell.sh`. (#4360) (Thanks to @DarwinJS!) -* Add ability to package PowerShell Core as a NuGet package. (#4363) - -### Build/test and code cleanup - -* Add build check for MFC for Visual C++ during Windows builds. - This fixes a long-standing (and very frustrating!) issue with missing build dependencies! (#4185) (Thanks to @KirkMunro!) -* Move building Windows PSRP binary out of `Start-PSBuild`. - Now `Start-PSBuild` doesn't build PSRP binary on windows. Instead, we consume the PSRP binary from a NuGet package. (#4335) -* Add tests for built-in type accelerators. (#4230) (Thanks to @dchristian3188!) -* Increase code coverage of `Get-ChildItem` on file system. (#4342) (Thanks to @jeffbi!) -* Increase test coverage for `Rename-Item` and `Move-Item`. (#4329) (Thanks to @jeffbi!) -* Add test coverage for Registry provider. (#4354) (Thanks to @jeffbi!) -* Fix warnings and errors thrown by PSScriptAnalyzer. (#4261) (Thanks to @bergmeister!) -* Fix regressions that cause implicit remoting tests to fail. (#4326) -* Disable legacy UTC and SQM Windows telemetry by enclosing the code in '#if LEGACYTELEMETRY'. (#4190) - -### Cleanup `#if CORECLR` code - -PowerShell 6.0 will be exclusively built on top of CoreCLR, -so we are removing a large amount of code that's built only for FullCLR. -To read more about this, check out [this blog post](https://blogs.msdn.microsoft.com/powershell/2017/07/14/powershell-6-0-roadmap-coreclr-backwards-compatibility-and-more/). - -## v6.0.0-beta.4 - 2017-07-12 - -## Windows PowerShell backwards compatibility - -In the `beta.4` release, we've introduced a change to add the Windows PowerShell `PSModulePath` to the default `PSModulePath` in PowerShell Core on Windows. (#4132) - -Along with the introduction of .NET Standard 2.0 in `6.0.0-beta.1` and a GAC probing fix in `6.0.0-beta.3`, -**this change will enable a large number of your existing Windows PowerShell modules/scripts to "just work" inside of PowerShell Core on Windows**. -(Note: We have also fixed the CDXML modules on Windows that were regressed in `6.0.0-beta.2` as part of #4144). - -So that we can further enable this backwards compatibility, -we ask that you tell us more about what modules or scripts do and don't work in Issue #4062. -This feedback will also help us determine if `PSModulePath` should include the Windows PowerShell values by default in the long run. - -For more information on this, we invite you to read [this blog post explaining PowerShell Core and .NET Standard in more detail](https://blogs.msdn.microsoft.com/powershell/?p=13355). - -### Engine updates and fixes - -- Add Windows PowerShell `PSModulePath` by default on Windows. (#4132) -- Move PowerShell to `2.0.0-preview3-25426-01` and using the .NET CLI version `2.0.0-preview2-006502`. (#4144) -- Performance improvement in PSReadline by minimizing writing ANSI escape sequences. (#4110) -- Implement Unicode escape parsing so that users can use Unicode characters as arguments, strings or variable names. (#3958) (Thanks to @rkeithhill!) -- Script names or full paths can have commas. (#4136) (Thanks to @TimCurwick!) -- Added `semver` as a type accelerator for `System.Management.Automation.SemanticVersion`. (#4142) (Thanks to @oising!) -- Close `eventLogSession` and `EventLogReader` to unlock an ETL log. (#4034) (Thanks to @iSazonov!) - -### General cmdlet updates and fixes - -- `Move-Item` cmdlet honors `-Include`, `-Exclude`, and `-Filter` parameters. (#3878) -- Add a parameter to `Get-ChildItem` called `-FollowSymlink` that traverses symlinks on demand, with checks for link loops. (#4020) -- Change `New-ModuleManifest` encoding to UTF8NoBOM on non-Windows platforms. (#3940) -- `Get-AuthenticodeSignature` cmdlets can now get file signature timestamp. (#4061) -- Add tab completion for `Export-Counter` `-FileFormat` parameter. (#3856) -- Fixed `Import-Module` on non-Windows platforms so that users can import modules with `NestedModules` and `RootModules`. (#4010) -- Close `FileStream` opened by `Get-FileHash`. (#4175) (Thanks to @rkeithhill!) - -### Remoting - -- Fixed hang when the SSH client abruptly terminates. (#4123) - -### Documentation - -- Added recommended settings for VS Code. (#4054) (Thanks to @iSazonov!) - -## v6.0.0-beta.3 - 2017-06-20 - -### Breaking changes - -- Remove the `BuildVersion` property from `$PSVersionTable`. - This property was strongly tied to the Windows build version. - Instead, we recommend that you use `GitCommitId` to retrieve the exact build version of PowerShell Core. - (#3877) (Thanks to @iSazonov!) -- Change positional parameter for `powershell.exe` from `-Command` to `-File`. - This fixes the usage of `#!` (aka as a shebang) in PowerShell scripts that are being executed from non-PowerShell shells on non-Windows platforms. - This also means that you can now do things like `powershell foo.ps1` or `powershell fooScript` without specifying `-File`. - However, this change now requires that you explicitly specify `-c` or `-Command` when trying to do things like `powershell.exe Get-Command`. - (#4019) -- Remove `ClrVersion` property from `$PSVersionTable`. - (This property is largely irrelevant for .NET Core, - and was only preserved in .NET Core for specific legacy purposes that are inapplicable to PowerShell.) - (#4027) - -### Engine updates and fixes - -- Add support to probe and load assemblies from GAC on Windows platform. - This means that you can now load Windows PowerShell modules with assembly dependencies which reside in the GAC. - If you're interested in running your traditional Windows PowerShell scripts and cmdlets using the power of .NET Standard 2.0, - try adding your Windows PowerShell module directories to your PowerShell Core `$PSModulePath`. - (E.g. `$env:PSModulePath += ';C:\Program Files\WindowsPowerShell\Modules;C:\WINDOWS\system32\WindowsPowerShell\v1.0\Modules'`) - Even if the module isn't owned by the PowerShell Team, please tell us what works and what doesn't by leaving a comment in [issue #4062][issue-4062]! (#3981) -- Enhance type inference in tab completion based on runtime variable values. (#2744) (Thanks to @powercode!) - This enables tab completion in situations like: - ```powershell - $p = Get-Process - $p | Foreach-Object Prio - ``` -- Add `GitCommitId` to PowerShell Core banner. - Now you don't have to run `$PSVersionTable` as soon as you start PowerShell to get the version! (#3916) (Thanks to @iSazonov!) -- Fix a bug in tab completion to make `native.exe --` call into native completer. (#3633) (Thanks to @powercode!) -- Fix PowerShell Core to allow use of long paths that are more than 260 characters. (#3960) -- Fix ConsoleHost to honour `NoEcho` on Unix platforms. (#3801) -- Fix transcription to not stop when a Runspace is closed during the transcription. (#3896) - -[issue-4062]: https://github.com/PowerShell/PowerShell/issues/4062 - -### General cmdlet updates and fixes - -- Enable `Send-MailMessage` in PowerShell Core. (#3869) -- Fix `Get-Help` to support case insensitive pattern matching on Unix platforms. (#3852) -- Fix tab completion on `Get-Help` for `about_*` topics. (#4014) -- Fix PSReadline to work in Windows Server Core container image. (#3937) -- Fix `Import-Module` to honour `ScriptsToProcess` when `-Version` is specified. (#3897) -- Strip authorization header on redirects with web cmdlets. (#3885) -- `Start-Sleep`: add the alias `ms` to the parameter `-Milliseconds`. (#4039) (Thanks to @Tadas!) - -### Developer experience - -- Make hosting PowerShell Core in your own .NET applications much easier by refactoring PowerShell Core to use the default CoreCLR loader. (#3903) -- Update `Add-Type` to support `CSharpVersion7`. (#3933) (Thanks to @iSazonov) - -## v6.0.0-beta.2 - 2017-06-01 - -### Support backgrounding of pipelines with ampersand (`&`) (#3360) - -- Putting `&` at the end of a pipeline will cause the pipeline to be run as a PowerShell job. -- When a pipeline is backgrounded, a job object is returned. -- Once the pipeline is running as a job, all of the standard `*-Job` cmdlets can be used to manage the job. -- Variables (ignoring process-specific variables) used in the pipeline are automatically copied to the job so `Copy-Item $foo $bar &` just works. -- The job is also run in the current directory instead of the user's home directory. -- For more information about PowerShell jobs, see [about_Jobs](https://msdn.microsoft.com/en-us/powershell/reference/6/about/about_jobs). - -### Engine updates and fixes - -- Crossgen more of the .NET Core assemblies to improve PowerShell Core startup time. (#3787) -- Enable comparison between a `SemanticVersion` instance and a `Version` instance that is constructed only with `Major` and `Minor` version values. - This will fix some cases where PowerShell Core was failing to import older Windows PowerShell modules. (#3793) (Thanks to @mklement0!) - -### General cmdlet updates and fixes - -- Support Link header pagination in web cmdlets (#3828) - - For `Invoke-WebRequest`, when the response includes a Link header we create a RelationLink property as a Dictionary representing the URLs and `rel` attributes and ensure the URLs are absolute to make it easier for the developer to use. - - For `Invoke-RestMethod`, when the response includes a Link header we expose a `-FollowRelLink` switch to automatically follow `next` `rel` links until they no longer exist or once we hit the optional `-MaximumFollowRelLink` parameter value. -- Update `Get-ChildItem` to be more in line with the way that the *nix `ls -R` and the Windows `DIR /S` native commands handle symbolic links to directories during a recursive search. - Now, `Get-ChildItem` returns the symbolic links it encountered during the search, but it won't search the directories those links target. (#3780) -- Fix `Get-ChildItem` to continue enumeration after throwing an error in the middle of a set of items. - This fixes some issues where inaccessible directories or files would halt execution of `Get-ChildItem`. (#3806) -- Fix `ConvertFrom-Json` to deserialize an array of strings from the pipeline that together construct a complete JSON string. - This fixes some cases where newlines would break JSON parsing. (#3823) -- Enable `Get-TimeZone` for macOS/Linux. (#3735) -- Change to not expose unsupported aliases and cmdlets on macOS/Linux. (#3595) (Thanks to @iSazonov!) -- Fix `Invoke-Item` to accept a file path that includes spaces on macOS/Linux. (#3850) -- Fix an issue where PSReadline was not rendering multi-line prompts correctly on macOS/Linux. (#3867) -- Fix an issue where PSReadline was not working on Nano Server. (#3815) - -## v6.0.0-beta.1 - 2017-05-08 - -### Move to .NET Core 2.0 (.NET Standard 2.0 support) - -PowerShell Core has moved to using .NET Core 2.0 so that we can leverage all the benefits of .NET Standard 2.0. (#3556) -To learn more about .NET Standard 2.0, there's some great starter content [on Youtube](https://www.youtube.com/playlist?list=PLRAdsfhKI4OWx321A_pr-7HhRNk7wOLLY), -on [the .NET blog](https://blogs.msdn.microsoft.com/dotnet/2016/09/26/introducing-net-standard/), -and [on GitHub](https://github.com/dotnet/standard/blob/master/docs/faq.md). -We'll also have more content soon in our [repository documentation](https://github.com/PowerShell/PowerShell/tree/master/docs) (which will eventually make its way to [official documentation](https://github.com/powershell/powershell-docs)). -In a nutshell, .NET Standard 2.0 allows us to have universal, portable modules between Windows PowerShell (which uses the full .NET Framework) and PowerShell Core (which uses .NET Core). -Many modules and cmdlets that didn't work in the past may now work on .NET Core, so import your favorite modules and tell us what does and doesn't work in our GitHub Issues! - -### Telemetry - -- For the first beta of PowerShell Core 6.0, telemetry has been to the console host to report two values (#3620): - - the OS platform (`$PSVersionTable.OSDescription`) - - the exact version of PowerShell (`$PSVersionTable.GitCommitId`) - -If you want to opt-out of this telemetry, simply delete `$PSHome\DELETE_ME_TO_DISABLE_CONSOLEHOST_TELEMETRY`. -Even before the first run of Powershell, deleting this file will bypass all telemetry. -In the future, we plan on also enabling a configuration value for whatever is approved as part of [RFC0015](https://github.com/PowerShell/PowerShell-RFC/blob/master/1-Draft/RFC0015-PowerShell-StartupConfig.md). -We also plan on exposing this telemetry data (as well as whatever insights we leverage from the telemetry) in [our community dashboard](https://blogs.msdn.microsoft.com/powershell/2017/01/31/powershell-open-source-community-dashboard/). - -If you have any questions or comments about our telemetry, please file an issue. - -### Engine updates and fixes - -- Add support for native command globbing on Unix platforms. (#3643) - - This means you can now use wildcards with native binaries/commands (e.g. `ls *.txt`). -- Fix PowerShell Core to find help content from `$PSHome` instead of the Windows PowerShell base directory. (#3528) - - This should fix issues where about_* topics couldn't be found on Unix platforms. -- Add the `OS` entry to `$PSVersionTable`. (#3654) -- Arrange the display of `$PSVersionTable` entries in the following way: (#3562) (Thanks to @iSazonov!) - - `PSVersion` - - `PSEdition` - - alphabetical order for rest entries based on the keys -- Make PowerShell Core more resilient when being used with an account that doesn't have some key environment variables. (#3437) -- Update PowerShell Core to accept the `-i` switch to indicate an interactive shell. (#3558) - - This will help when using PowerShell as a default shell on Unix platforms. -- Relax the PowerShell `SemanticVersion` constructors to not require 'minor' and 'patch' portions of a semantic version name. (#3696) -- Improve performance to security checks when group policies are in effect for ExecutionPolicy. (#2588) (Thanks to @powercode) -- Fix code in PowerShell to use `IntPtr(-1)` for `INVALID_HANDLE_VALUE` instead of `IntPtr.Zero`. (#3544) (Thanks to @0xfeeddeadbeef) - -### General cmdlet updates and fixes - -- Change the default encoding and OEM encoding used in PowerShell Core to be compatible with Windows PowerShell. (#3467) (Thanks to @iSazonov!) -- Fix a bug in `Import-Module` to avoid incorrect cyclic dependency detection. (#3594) -- Fix `New-ModuleManifest` to correctly check if a URI string is well formed. (#3631) - -### Filesystem-specific updates and fixes - -- Use operating system calls to determine whether two paths refer to the same file in file system operations. (#3441) - - This will fix issues where case-sensitive file paths were being treated as case-insensitive on Unix platforms. -- Fix `New-Item` to allow creating symbolic links to file/directory targets and even a non-existent target. (#3509) -- Change the behavior of `Remove-Item` on a symbolic link to only removing the link itself. (#3637) -- Use better error message when `New-Item` fails to create a symbolic link because the specified link path points to an existing item. (#3703) -- Change `Get-ChildItem` to list the content of a link to a directory on Unix platforms. (#3697) -- Fix `Rename-Item` to allow Unix globbing patterns in paths. (#3661) - -### Interactive fixes - -- Add Hashtable tab completion for `-Property` of `Select-Object`. (#3625) (Thanks to @powercode) -- Fix tab completion with `@{` to avoid crash in PSReadline. (#3626) (Thanks to @powercode) -- Use ` - ` as `ToolTip` and `ListItemText` when tab completing process ID. (#3664) (Thanks to @powercode) - -### Remoting fixes - -- Update PowerShell SSH remoting to handle multi-line error messages from OpenSSH client. (#3612) -- Add `-Port` parameter to `New-PSSession` to create PowerShell SSH remote sessions on non-standard (non-22) ports. (#3499) (Thanks to @Lee303) - -### API Updates - -- Add the public property `ValidRootDrives` to `ValidateDriveAttribute` to make it easy to discover the attribute state via `ParameterMetadata` or `PSVariable` objects. (#3510) (Thanks to @indented-automation!) -- Improve error messages for `ValidateCountAttribute`. (#3656) (Thanks to @iSazonov) -- Update `ValidatePatternAttribute`, `ValidateSetAttribute` and `ValidateScriptAttribute` to allow users to more easily specify customized error messages. (#2728) (Thanks to @powercode) - -## v6.0.0-alpha.18 - 2017-04-05 - -### Progress Bar - -We made a number of fixes to the progress bar rendering and the `ProgressRecord` object that improved cmdlet performance and fixed some rendering bugs on non-Windows platforms. - -- Fix a bug that caused the progress bar to drift on Unix platforms. (#3289) -- Improve the performance of writing progress records. (#2822) (Thanks to @iSazonov!) -- Fix the progress bar rendering on Unix platforms. (#3362) (#3453) -- Reuse `ProgressRecord` in Web Cmdlets to reduce the GC overhead. (#3411) (Thanks to @iSazonov!) - -### Cmdlet updates - -- Use `ShellExecute` with `Start-Process`, `Invoke-Item`, and `Get-Help -Online` so that those cmdlets use standard shell associations to open a file/URI. - This means you `Get-Help -Online` will always use your default browser, and `Start-Process`/`Invoke-Item` can open any file or path with a handler. - (Note: there are still some problems with STA threads.) (#3281, partially fixes #2969) -- Add `-Extension` and `-LeafBase` switches to `Split-Path` so that you can split paths between the filename extension and the rest of the filename. (#2721) (Thanks to @powercode!) -- Implement `Format-Hex` in C# along with some behavioral changes to multiple parameters and the pipeline. (#3320) (Thanks to @MiaRomero!) -- Add `-NoProxy` to web cmdlets so that they ignore the system-wide proxy setting. (#3447) (Thanks to @TheFlyingCorpse!) -- Fix `Out-Default -Transcript` to properly revert out of the `TranscribeOnly` state, so that further output can be displayed on Console. (#3436) (Thanks to @PetSerAl!) -- Fix `Get-Help` to not return multiple instances of the same help file. (#3410) - -### Interactive fixes - -- Enable argument auto-completion for `-ExcludeProperty` and `-ExpandProperty` of `Select-Object`. (#3443) (Thanks to @iSazonov!) -- Fix a tab completion bug that prevented `Import-Module -n` from working. (#1345) - -### Cross-platform fixes - -- Ignore the `-ExecutionPolicy` switch when running PowerShell on non-Windows platforms because script signing is not currently supported. (#3481) -- Standardize the casing of the `PSModulePath` environment variable. (#3255) - -### JEA fixes - -- Fix the JEA transcription to include the endpoint configuration name in the transcript header. (#2890) -- Fix `Get-Help` in a JEA session. (#2988) - -## v6.0.0-alpha.17 - 2017-03-08 - -- Update PSRP client libraries for Linux and Mac. - - We now support customer configurations for Office 365 interaction, as well as NTLM authentication for WSMan based remoting from Linux (more information [here](https://github.com/PowerShell/psl-omi-provider/releases/tag/v1.0.0.18)). (#3271) -- We now support remote step-in debugging for `Invoke-Command -ComputerName`. (#3015) -- Use prettier formatter with `ConvertTo-Json` output. (#2787) (Thanks to @kittholland!) -- Port `*-CmsMessage` and `Get-PfxCertificate` cmdlets to Powershell Core. (#3224) -- `powershell -version` now returns version information for PowerShell Core. (#3115) -- Add the `-TimeOut` parameter to `Test-Connection`. (#2492) -- Add `ShouldProcess` support to `New-FileCatalog` and `Test-FileCatalog` (fixes `-WhatIf` and `-Confirm`). (#3074) (Thanks to @iSazonov!) -- Fix `Test-ModuleManifest` to normalize paths correctly before validating. - - This fixes some problems when using `Publish-Module` on non-Windows platforms. (#3097) -- Remove the `AliasProperty "Count"` defined for `System.Array`. - - This removes the extraneous `Count` property on some `ConvertFrom-Json` output. (#3231) (Thanks to @PetSerAl!) -- Port `Import-PowerShellDatafile` from PowerShell script to C#. (#2750) (Thanks to @powercode!) -- Add `-CustomMethod` parameter to web cmdlets to allow for non-standard method verbs. (#3142) (Thanks to @Lee303!) -- Fix web cmdlets to include the HTTP response in the exception when the response status code is not success. (#3201) -- Expose a process' parent process by adding the `CodeProperty "Parent"` to `System.Diagnostics.Process`. (#2850) (Thanks to @powercode!) -- Fix crash when converting a recursive array to a bool. (#3208) (Thanks to @PetSerAl!) -- Fix casting single element array to a generic collection. (#3170) -- Allow profile directory creation failures for Service Account scenarios. (#3244) -- Allow Windows' reserved device names (e.g. CON, PRN, AUX, etc.) to be used on non-Windows platforms. (#3252) -- Remove duplicate type definitions when reusing an `InitialSessionState` object to create another Runspace. (#3141) -- Fix `PSModuleInfo.CaptureLocals` to not do `ValidateAttribute` check when capturing existing variables from the caller's scope. (#3149) -- Fix a race bug in WSMan command plug-in instance close operation. (#3203) -- Fix a problem where newly mounted volumes aren't available to modules that have already been loaded. (#3034) -- Remove year from PowerShell copyright banner at start-up. (#3204) (Thanks to @kwiknick!) -- Fixed spelling for the property name `BiosSerialNumber` for `Get-ComputerInfo`. (#3167) (Thanks to @iSazonov!) - -## v6.0.0-alpha.16 - 2017-02-15 - -- Add `WindowsUBR` property to `Get-ComputerInfo` result -- Cache padding strings to speed up formatting a little -- Add alias `Path` to the `-FilePath` parameter of `Out-File` -- Fix the `-InFile` parameter of `Invoke-WebRequest` -- Add the default help content to powershell core -- Speed up `Add-Type` by crossgen'ing its dependency assemblies -- Convert `Get-FileHash` from script to C# implementation -- Fix lock contention when compiling the code to run in interpreter -- Avoid going through WinRM remoting stack when using `Get-ComputerInfo` locally -- Fix native parameter auto-completion for tokens that begin with a single "Dash" -- Fix parser error reporting for incomplete input to allow defining class in interactive host -- Add the `RoleCapabilityFiles` keyword for JEA support on Windows - -## v6.0.0-alpha.15 - 2017-01-18 - -- Use parentheses around file length for offline files -- Fix issues with the Windows console mode (terminal emulation) and native executables -- Fix error recovery with `using module` -- Report `PlatformNotSupported` on IoT for Get/Import/Export-Counter -- Add `-Group` parameter to `Get-Verb` -- Use MB instead of KB for memory columns of `Get-Process` -- Add new escape character for ESC: `` `e`` -- Fix a small parsing issue with a here string -- Improve tab completion of types that use type accelerators -- `Invoke-RestMethod` improvements for non-XML non-JSON input -- PSRP remoting now works on CentOS without addition setup - -## v6.0.0-alpha.14 - 2016-12-14 - -- Moved to .NET Core 1.1 -- Add Windows performance counter cmdlets to PowerShell Core -- Fix try/catch to choose the more specific exception handler -- Fix issue reloading modules that define PowerShell classes -- `Add ValidateNotNullOrEmpty` to approximately 15 parameters -- `New-TemporaryFile` and `New-Guid` rewritten in C# -- Enable client side PSRP on non-Windows platforms -- `Split-Path` now works with UNC roots -- Implicitly convert value assigned to XML property to string -- Updates to `Invoke-Command` parameters when using SSH remoting transport -- Fix `Invoke-WebRequest` with non-text responses on non-Windows platforms -- `Write-Progress` performance improvement from `alpha13` reverted because it introduced crash with a race condition - -## v6.0.0-alpha.13 - 2016-11-22 - -- Fix `NullReferenceException` in binder after turning on constrained language mode -- Enable `Invoke-WebRequest` and `Invoke-RestMethod` to not validate the HTTPS certificate of the server if required. -- Enable binder debug logging in PowerShell Core -- Add parameters `-Top` and `-Bottom` to `Sort-Object` for Top/Bottom N sort -- Enable `Update-Help` and `Save-Help` on Unix platforms -- Update the formatter for `System.Diagnostics.Process` to not show the `Handles` column -- Improve `Write-Progress` performance by adding timer to update a progress pane every 100 ms -- Enable correct table width calculations with ANSI escape sequences on Unix -- Fix background jobs for Unix and Windows -- Add `Get-Uptime` to `Microsoft.PowerShell.Utility` -- Make `Out-Null` as fast as `> $null` -- Add DockerFile for 'Windows Server Core' and 'Nano Server' -- Fix WebRequest failure to handle missing ContentType in response header -- Make `Write-Host` fast by delay initializing some properties in InformationRecord -- Ensure PowerShell Core adds an initial `/` rooted drive on Unix platforms -- Enable streaming behavior for native command execution in pipeline, so that `ping | grep` doesn't block -- Make `Write-Information` accept objects from pipeline -- Fixes deprecated syscall issue on macOS 10.12 -- Fix code errors found by the static analysis using PVS-Studio -- Add support to W3C Extended Log File Format in `Import-Csv` -- Guard against `ReflectionTypeLoadException` in type name auto-completion -- Update build scripts to support win7-x86 runtime -- Move PackageManagement code/test to oneget.org - -## v6.0.0-alpha.12 - 2016-11-03 - -- Fix `Get-ChildItem -Recurse -ErrorAction Ignore` to ignore additional errors -- Don't block pipeline when running Windows EXE's -- Fix for PowerShell SSH remoting with recent Win32-OpenSSH change. -- `Select-Object` with `-ExcludeProperty` now implies `-Property *` if -Property is not specified. -- Adding ValidateNotNullOrEmpty to `-Name` parameter of `Get-Alias` -- Enable Implicit remoting commands in PowerShell Core -- Fix GetParentProcess() to replace an expensive WMI query with Win32 API calls -- Fix `Set-Content` failure to create a file in PSDrive under certain conditions. -- Adding ValidateNotNullOrEmpty to `-Name` parameter of `Get-Service` -- Adding support in `Get-WinEvent -FilterHashtable` -- Adding WindowsVersion to `Get-ComputerInfo` -- Remove the unnecessary use of lock in PseudoParameterBinder to avoid deadlock -- Refactor `Get-WinEvent` to use StringBuilder for XPath query construction -- Clean up and fix error handling of libpsl-native -- Exclude Registry and Certificate providers from UNIX PS -- Update PowerShell Core to consume .Net Core preview1-24530-04 - -## v6.0.0-alpha.11 - 2016-10-17 - -- Add '-Title' to 'Get-Credential' and unify the prompt experience -- Update dependency list for PowerShell Core on Linux and OS X -- Fix 'powershell -Command -' to not hang and to not ignore the last command -- Fix binary operator tab completion -- Enable 'ConvertTo-Html' in PowerShell Core -- Remove most Maximum* capacity variables -- Fix 'Get-ChildItem -Hidden' to work on system hidden files on Windows -- Fix 'JsonConfigFileAccessor' to handle corrupted 'PowerShellProperties.json' - and defer creating the user setting directory until a write request comes -- Fix variable assignment to not overwrite read-only variables -- Fix 'Get-WinEvent -FilterHashtable' to work with named fields in UserData of event logs -- Fix 'Get-Help -Online' in PowerShell Core on Windows -- Spelling/grammar fixes - -## v6.0.0-alpha.10 - 2016-09-15 - -- Fix passing escaped double quoted spaces to native executables -- Add DockerFiles to build each Linux distribution -- `~/.config/PowerShell` capitalization bug fixed -- Fix crash on Windows 7 -- Fix remote debugging on Windows client -- Fix multi-line input with redirected stdin -- Add PowerShell to `/etc/shells` on installation -- Fix `Install-Module` version comparison bug -- Spelling fixes - -## v6.0.0-alpha.9 - 2016-08-15 - -- Better man page -- Added third-party and proprietary licenses -- Added license to MSI - -## v6.0.0-alpha.8 - 2016-08-11 - -- PowerShell packages pre-compiled with crossgen -- `Get-Help` content added -- `Get-Help` null reference exception fixed -- Ubuntu 16.04 support added -- Unsupported cmdlets removed from Unix modules -- PSReadline long prompt bug fixed -- PSReadline custom key binding bug on Linux fixed -- Default terminal colors now respected -- Semantic Version support added -- `$env:` fixed for case-sensitive variables -- Added JSON config files to hold some settings -- `cd` with no arguments now behaves as `cd ~` -- `ConvertFrom-Json` fixed for multiple lines -- Windows branding removed -- .NET CoreCLR Runtime patched to version 1.0.4 -- `Write-Host` with unknown hostname bug fixed -- `powershell` man-page added to package -- `Get-PSDrive` ported to report free space -- Desired State Configuration MOF compilation ported to Linux -- Windows 2012 R2 / Windows 8.1 remoting enabled - -## v6.0.0-alpha.7 - 2016-07-26 - -- Invoke-WebRequest and Invoke-RestMethod ported to PowerShell Core -- Set PSReadline default edit mode to Emacs on Linux -- IsCore variable renamed to IsCoreCLR -- Microsoft.PowerShell.LocalAccounts and other Windows-only assemblies excluded on Linux -- PowerShellGet fully ported to Linux -- PackageManagement NuGet provider ported -- Write-Progress ported to Linux -- Get-Process -IncludeUserName ported -- Enumerating symlinks to folders fixed -- Bugs around administrator permissions fixed on Linux -- ConvertFrom-Json multi-line bug fixed -- Execution policies fixed on Windows -- TimeZone cmdlets added back; excluded from Linux -- FileCatalog cmdlets added back for Windows -- Get-ComputerInfo cmdlet added back for Windows - -## v0.6.0 - 2016-07-08 - -- Targets .NET Core 1.0 release -- PowerShellGet enabled -- [system.manage] completion issues fixed -- AssemblyLoadContext intercepts dependencies correctly -- Type catalog issues fixed -- Invoke-Item enabled for Linux and OS X -- Windows ConsoleHost reverted to native interfaces -- Portable ConsoleHost redirection issues fixed -- Bugs with pseudo (and no) TTY's fixed -- Source Depot synced to baseline changeset 717473 -- SecureString stub replaced with .NET Core package - -## v0.5.0 - 2016-06-16 - -- Paths given to cmdlets are now slash-agnostic (both / and \ work as directory separator) -- Lack of cmdlet support for paths with literal \ is a known issue -- .NET Core packages downgraded to build rc2-24027 (Nano's build) -- XDG Base Directory Specification is now respected and used by default -- Linux and OS X profile path is now `~/.config/powershell/profile.ps1` -- Linux and OS X history save path is now `~/.local/share/powershell/PSReadLine/ConsoleHost_history.txt` -- Linux and OS X user module path is now `~/.local/share/powershell/Modules` -- The `~/.powershell` folder is deprecated and should be deleted -- Scripts can be called within PowerShell without the `.ps1` extension -- `Trace-Command` and associated source cmdlets are now available -- `Ctrl-C` now breaks running cmdlets correctly -- Source Depot changesets up to 715912 have been merged -- `Set-PSBreakPoint` debugging works on Linux, but not on Windows -- MSI and APPX packages for Windows are now available -- Microsoft.PowerShell.LocalAccounts is available on Windows -- Microsoft.PowerShell.Archive is available on Windows -- Linux xUnit tests are running again -- Many more Pester tests are running - -## v0.4.0 - 2016-05-17 - -- PSReadline is ported and included by default -- Original Windows ConsoleHost is ported and replaced CoreConsoleHost -- .NET Core packages set to the RC2 release at build 24103 -- OS X 10.11 added to Continuous Integration matrix -- Third-party C# cmdlets can be built with .NET CLI -- Improved symlink support on Linux -- Microsoft.Management.Infrastructure.Native replaced with package -- Many more Pester tests - -## v0.3.0 - 2016-04-11 - -- Supports Windows, Nano, OS X, Ubuntu 14.04, and CentOS 7.1 -- .NET Core packages are build rc3-24011 -- Native Linux commands are not shadowed by aliases -- `Get-Help -Online` works -- `more` function respects the Linux `$PAGER`; defaults to `less` -- `IsWindows`, `IsLinux`, `IsOSX`, `IsCore` built-in PowerShell variables added -- `Microsoft.PowerShell.Platform` removed for the above -- Cross-platform core host is now `CoreConsoleHost` -- Host now catches exceptions in `--command` scripts -- Host's shell ID changed to `Microsoft.PowerShellCore` -- Modules that use C# assemblies can be loaded -- `New-Item -ItemType SymbolicLink` supports arbitrary targets -- PSReadline implementation supports multi-line input -- `Ctrl-R` provides incremental reverse history search -- `$Host.UI.RawUI` now supported -- `Ctrl-K` and `Ctrl-Y` for kill and yank implemented -- `Ctrl-L` to clear screen now works -- Documentation was completely overhauled -- Many more Pester and xUnit tests added - -## v0.2.0 - 2016-03-08 - -- Supports Windows, OS X, Ubuntu 14.04, and CentOS 7.1 -- .NET Core packages are build 23907 -- `System.Console` PSReadline is fully functional -- Tests pass on OS X -- `Microsoft.PowerShell.Platform` module is available -- `New-Item` supports symbolic and hard links -- `Add-Type` now works -- PowerShell code merged with upstream `rs1_srv_ps` - -## v0.1.0 - 2016-02-23 - -- Supports Windows, OS X, and Ubuntu 14.04 +The change logs have been split by version and moved to [CHANGELOG](./CHANGELOG). diff --git a/CHANGELOG/6.0.md b/CHANGELOG/6.0.md new file mode 100644 index 00000000000..52db53afabf --- /dev/null +++ b/CHANGELOG/6.0.md @@ -0,0 +1,1180 @@ +# 6.0 Changelog + +## [6.0.0] - 2018-01-10 + +### Breaking changes + +- Remove `sc` alias which conflicts with `sc.exe` (#5827) +- Separate group policy settings and enable policy controlled logging in PowerShell Core (#5791) + +### Engine updates and fixes + +- Handle `DLLImport` failure of `libpsrpclient` in PowerShell Remoting on Unix platforms (#5622) + +### Test + +- Replace `lee.io` Tests with `WebListener` (#5709) (Thanks @markekraus!) +- Update the docker based release package tests due to the removal of `Pester` module and other issues (#5692) +- Replace Remaining `HttpBin.org` Tests with `WebListener` (#5665) (Thanks @markekraus!) + +### Build and Packaging Improvements + +- Update x86 and x64 `MSI` packages to not overwrite each other (#5812) (Thanks @bergmeister!) +- Update `Restore-PSPester` to include the fix for nested describe errors (#5771) +- Automate the generation of release change log draft (#5712) + +### Documentation and Help Content + +- Updated help Uri to point to latest help content for `Microsoft.PowerShell.Core` module (#5820) +- Update the installation doc for `Raspberry-Pi` about supported devices (#5773) +- Fix a typo and a Markdown linting error in the Pull Request Template (#5807) (Thanks @markekraus!) +- Update submodule documentation for pester removal (#5786) (Thanks @bergmeister!) +- Change `Github` to `GitHub` in `CONTRIBUTING.md` (#5697) (Thanks @stuntguy3000!) +- Fix incorrect release date on the changelog (#5698) (Thanks @SwarfegaGit!) +- Add instructions to deploy `win-arm` build on Windows IoT (#5682) + +## [6.0.0-rc.2] - 2017-12-14 + +### Breaking changes + +- Skip null-element check for collections with a value-type element type (#5432) +- Make `AllSigned` execution policy require modules under `$PSHome` to be signed (#5511) + +### Engine updates and fixes + +- Update PowerShell to use `2.0.4` dotnet core runtime. (#5677) +- Remove references to the old executable `powershell` or `powershell.exe` (#5408) + +### General cmdlet updates and fixes + +- Remove unnecessary check for `Paths.count > 0`, in the `*-FileCatalog` CmdLets (#5596) +- Use explicit `libpsl-native` binary name for `dllimport`. (#5580) + +### Build and Packaging Improvements + +- Fix `Get-EnvironmentInformation` to properly check for CoreCLR (#5592) (Thanks @markekraus!) +- Make Travis CI use `libcurl+openssl+gssapi` (#5629) (Thanks @markekraus!) +- Disambiguate icon for daily builds on Windows (#5467) (Thanks @bergmeister!) +- Fix `Import-CliXml` tests which still use `powershell` instead of `pwsh` and make sure it fails if it regresses (#5521) (Thanks @markekraus!) +- Update port number used for WebCmdlets tests which broke due to a change in AppVeyor (#5520) (Thanks @markekraus!) +- Clean up use of `Runspaceconfiguration` from comments and xUnit test code (#5569) (Thanks @Bhaal22!) +- Replace `HttpListener` Response Tests with WebListener (#5540, #5605) (Thanks @markekraus!) +- Fix the path to `powershell_xxx.inc` in Start-Build (#5538) (Thanks @iSazonov!) +- Remove Pester as a module include with the PowerShell Packages. + You should be able to add it by running `Install-Module Pester`. (#5623, #5631) +- Refactor `New-UnixPackaging` into functions to make the large function more readable. (#5625) +- Make the experience better when `Start-PSPester` doesn't find Pester (#5673) +- Update packaging and release build scripts to produce zip packages for `win-arm` and `win-arm64` (#5664) +- Enable `Install-Debian` to work with VSTS Hosted Linux Preview (#5659) +- Add `linux-arm` tarball package to release build (#5652, #5660) +- Enable building for `win-arm` and `win-arm64` (#5524) +- Make macOS package require 10.12 or newer (#5649, #5654) +- Update signing subjects to something meaningful (#5650) +- Make `New-UnixPackage` more readable (#5625) +- Update `PowerShellGet` tests to validate the new install location of `AllUsers` scope. (#5633) +- Increase reliability of flaky test that fails intermittently in CI (#5641) +- Exclude markdown files from `Pester` folder from the Markdown meta test (#5636) +- Run tests for Windows installer only on Windows (#5619) +- Suppress the expected errors from `Select-Xml` tests (#5591) +- Add retry logic to prerequisite URL and output URL on failure so you can more easily troubleshoot (#5601, #5570) +- Make sure submodule are initialized when running Mac release build (#5496) +- Remove duplicate files in Windows packages in a folder called `signed` (#5527) +- Add PowerShell VSCode style settings (#5529) (Thanks @bergmeister) +- Add Travis CI matrix for improved job tagging (#5547) +- Remove community docker files from official docker image validation (#5508) + +### Documentation and Help Content + +- XML documentation fix for `CompletionResult` (#5550) (Thanks @bergmeister!) +- Change synopsis of `install-powershell.ps1` to reflect that it works cross-platform (#5465) (Thanks @bergmeister!) +- Add more helpful message for `AmbiguousParameterSet` exception (#5537) (Thanks @kvprasoon!) +- Update the contribution guideline to note that updating the changelog is required. (#5586) +- Updated doc to build arm/arm64 versions of `psrp.windows` and `PowerShell.Core.Instrumentation.dll` libraries (#5668) +- Update Contribution guidelines with work in progress guidance (#5655) +- Update code coverage tests to get GitCommitId using the ProductVersion from Assembly (#5651) +- Remove requirement to updating changelog update in PR (#5644, #5586) +- Minor refactoring of the release build scripts (#5632) +- Update PowerShell executable name in `using-vscode.md` (#5593) +- Fix xUnit test for PS (#4780) +- Update install link and instructions for R-Pi (#5495) + +### Compliance Work + +[Compliance](https://github.com/PowerShell/PowerShell/blob/master/docs/maintainers/issue-management.md#miscellaneous-labels) +work is required for Microsoft to continue to sign and release packages from the project as official Microsoft packages. + +- Remove `PerformWSManPluginReportCompletion`, which was not used, from `pwrshplugin.dll` (#5498) (Thanks @bergmeister!) +- Remove exclusion for unresponsive condition and add context exception for remaining instances (#5595) +- Replace `strlen` with `strnlen` in native code (#5510) + +## [6.0.0-rc] - 2017-11-16 + +### Breaking changes + +- Fix `-Verbose` to not override `$ErrorActionPreference`. (#5113) +- Fix `Get-Item -LiteralPath a*b` to return error if `a*b` doesn't actually exist. (#5197) +- Remove `AllScope` from most default aliases to reduce overhead on creating new scopes. (#5268) +- Change `$OutputEncoding` default to be `UTF8` without `BOM` rather than `ASCII`. (#5369) +- Add error on legacy credential over non-HTTPS for Web Cmdlets. (#5402) (Thanks @markekraus!) +- Fix single value JSON `null` in `Invoke-RestMethod`. (#5338) (Thanks @markekraus!) +- Add `PSTypeName` Support for `Import-Csv` and `ConvertFrom-Csv`. (#5389) (Thanks @markekraus!) + +### Engine updates and fixes + +- Add char range overload to the `..` operator, so `'a'..'z'` returns characters from 'a' to 'z'. (#5026) (Thanks @IISResetMe!) +- Remove `CommandFactory` because it serves no real purpose. (#5266) +- Change to not insert line breaks at console window width to output (except for tables). (#5193) +- Use `Ast` for context in parameter binding and fix to glob the native command argument only when it's not quoted. (#5188) +- Fix dynamic class assembly name. (#5292) +- Update PowerShell to use `2.0.4-servicing` dotnet core runtime. (#5295) +- Fix `ExecutionContext.LoadAssembly` to load with name when file cannot be found. (#5161) +- Speed up the check for suspicious content in script texts. (#5302) +- Use native `os_log` APIs on macOS for PowerShell Core logging. (#5310) +- Redirect `ETW` logging to `Syslog` on Linux. (#5144) +- Improve how we pass the array literal to native commands. (#5301) +- Make `SemanticVersion` compatible with `SemVer 2.0`. (#5037) (Thanks @iSazonov!) +- Revert refactoring changes that broke remoting to Windows PowerShell 5.1. (#5321) +- Port some fixes in `Job` for an issue that causes PowerShell to not respond. (#5258) +- Multiple improvements by `CodeRush` static analysis. (#5132) (Thanks @Himura2la!) +- Fix the Runspace cleanup issue that causes PowerShell to not respond on exit. (#5356) +- Update PowerShell to depend on new version of `psrp` and `libmi` nuget packages on Unix platforms. (#5469) + +### General cmdlet updates and fixes + +- Add `-AsHashtable` to `ConvertFrom-Json` to return a `Hashtable` instead. (#5043) (Thanks @bergmeister!) +- Fix `Import-module` to not report a loaded module was not found. (#5238) +- Fix performance issues in `Add-Type`. (#5243) (Thanks @iSazonov!) +- Fix `PSUserAgent` generation for Web Cmdlets on Windows 7. (#5256) (Thanks @markekraus!) +- Remove `DCOM` support from `*-Computer` cmdlets. (#5277) +- Add multiple link header support to Web Cmdlets. (#5265) (Thanks @markekraus!) +- Use wider columns for process id and user. (#5303) +- Add `Remove-Alias` Command. (#5143) (Thanks @PowershellNinja!) +- Update `installpsh-suse.sh` to work with the `tar.gz` package. (#5309) +- Add `Jobject` serialization support to `ConvertTo-Json`. (#5141) +- Display full help with 'help' function. (#5195) (Thanks @rkeithhill!) +- Fix `help` function to not pipe to `more` if objects are returned instead of help text. (#5395) +- Fix `Unblock-File` to not write an error if the file is already unblocked. (#5362) (Thanks @iSazonov!) +- Clean up FullCLR code from Web Cmdlets. (#5376) (Thanks @markekraus!) +- Exclude cmdlets that are not supported on Unix platforms. (#5083) +- Make `Import-Csv` support `CR`, `LF` and `CRLF` as line delimiters. (#5363) (Thanks @iSazonov!) +- Fix spelling in Web Cmdlet errors. (#5427) (Thanks @markekraus!) +- Add `SslProtocol` support to Web Cmdlets. (#5329) (Thanks @markekraus!) + +### Build and Packaging Improvements + +- Use `RCEdit` to embed icon and version information into `pwsh.exe`. (#5178) +- Update Docker file for Nano Server 1709 release. (#5252) +- Change VSCode build task to use `pwsh`. (#5255) +- Refactor building and packaging scripts for signing in release build workflow. (#5300) +- Always build with `-CrossGen` in CI to verify a fix in `CrossGen` tool. (#5315) +- Separate `Install-PowerShellRemoting.ps1` from `psrp.windows` nuget package. (#5330) +- Include symbols folder an embedded zip when packaging symbols. (#5333) +- Add Uniform Type Identifier conforming with Apple standards using a reverse DNS style prefix. (#5323) +- Update `Wix` toolset download link to newer version 3.11 (#5339) (Thanks @bergmeister!) +- Re-enable macOS launcher after fixing an issue that blocked macOS package generation. (#5291) (Thanks @thezim!) +- Set expected binaries and variable name for folder for symbols build. (#5357) +- Rename and update PowerShell `ETW` manifest to remove the Windows PowerShell dependency. (#5360) +- Add ability to produce `tar.gz` package for Raspbian. (#5387) +- Update `Find-Dotnet` to find dotnet with the compatible SDK. (#5341) (Thanks @rkeithhill!) +- Add signing manifest and script to update it with production values. (#5397) +- Add `install-powershell.ps1` to install PowerShell Core on windows. (#5383) +- Make `-Name` a dynamic parameter in `Start-PSPackage`. (#5415) +- Support `[package]` tag in PR CI and fix nightly build on macOS. (#5410) +- Enhance `install-powershell.ps1` to work on Linux and macOS. (#5411) +- Move the `RCEdit` step to the build phase rather than the packaging phase. (#5404) +- Allow packaging from a zip package to allow for signing. (#5418) +- Add automation to validate PowerShell Core packages using Docker containers. (#5401) +- Fix the `brew update` issue in bootstrap script. (#5400) +- Enable `install-powershell.ps1` to update the current running PowerShell Core. (#5429) +- Add standard set of VSCode workspace setting files. (#5457) (Thanks @rkeithhill!) +- Add support for installing PowerShell Core on Amazon Linux via `install-powershell.sh`. (#5461) (Thanks @DarwinJS!) +- Get `PowerShellGet` and `PackageManagement` from the PowerShell Gallery. (#5452) +- Fix `Start-PSBuild` on `WSL` if repository was already built on Windows. (#5346) (Thanks @bergmeister!) +- Fix build in VSCode and use an improved version of `tasks.json` from @rkeithhill. (#5453) +- Add scripts for signing packages in the release build workflow. (#5463) + +### Documentation and Help Content + +- Fix the codebase to use the consistent copyright string. (#5210) +- Add documentation about how to create `libpsl` and `psrp.windows` nuget packages. (#5278) +- Add help strings in PowerShell banner. (#5275) (Thanks @iSazonov!) +- Change all links in `README.md` to absolute as they are being used in other places outside of GitHub. (#5354) +- Update instructions to build on VSCode based on `pwsh`. (#5368) +- Update `FAQ.md` about how to use PowerShell Core nuget packages. (#5366) +- Correct the Fedora documentation (#5384) (Thanks @offthewoll!) +- Add instructions about how to create the `PowerShell.Core.Instrumentation` nuget package. (#5396) +- Updated PowerShell to use the latest help package. (#5454) + +### Compliance Work + +[Compliance](https://github.com/PowerShell/PowerShell/blob/master/docs/maintainers/issue-management.md#miscellaneous-labels) +work is required for Microsoft to continue to sign and release packages from the project as official Microsoft packages. + +- Replace the word `hang` with something more appropriate and add rules about other terms. (#5213, #5297, #5358) +- Use simplified names for compliance folders (#5388) +- Add compliance label description (#5355) +- Set `requestedExecutionLevel` to `asInvoker` for `pwsh.exe` on Windows. (#5285) +- Add `HighEntropyVA` to building pwsh. (#5455) + +## [6.0.0-beta.9] - 2017-10-24 + +### Breaking changes + +- Fix `ValueFromRemainingArguments` to have consistent behavior between script and C# cmdlets. (#2038) (Thanks @dlwyatt) +- Remove parameters `-importsystemmodules` and `-psconsoleFile` from `powershell.exe`. (#4995) +- Removed code to show a GUI prompt for credentials as PowerShell Core prompts in console. (#4995) +- Remove `-ComputerName` from `Get/Set/Remove-Service`. (#5094) +- Rename the executable name from `powershell` to `pwsh`. (#5101) +- Remove `RunspaceConfiguration` support. (#4942) +- Remove `-ComputerName` support since .NET Core `Process.GetProcesses(computer)` returns local processes. (#4960) +- Make `-NoTypeInformation` the default on `Export-Csv` and `ConvertTo-Csv`. (#5164) (Thanks @markekraus) +- Unify cmdlets with parameter `-Encoding` to be of type `System.Text.Encoding`. (#5080) + +### Engine updates and fixes + +- Fix PowerShell to update the `PATH` environment variable only if `PATH` exists. (#5021) +- Enable support of folders and files with colon in name on Unix. (#4959) +- Fix detection of whether `-LiteralPath` was used to suppress wildcard expansion for navigation cmdlets. (#5038) +- Enable using filesystem from a UNC location. (#4998) +- Escape trailing backslash when dealing with native command arguments. (#4965) +- Change location of `ModuleAnalysisCache` so it isn't shared with Windows PowerShell. (#5133) +- Put command discovery before scripts for Unix. (#5116) + +### General cmdlet updates and fixes + +- Correct comma position in `SecureStringCommands.resx`. (#5033) (Thanks @markekraus) +- User Agent of Web Cmdlets now reports the OS platform (#4937) (Thanks @LDSpits) +- Add the positional parameter attribute to `-InputObject` for `Set-Service`. (#5017) (Thanks @travisty-) +- Add `ValidateNotNullOrEmpty` attribute to `-UFormat` for `Get-Date`. (#5055) (Thanks @DdWr) +- Add `-NoNewLine` switch for `Out-String`. (#5056) (Thanks @raghav710) +- Improve progress messages written by Web Cmdlets. (#5078) (Thanks @markekraus) +- Add verb descriptions and alias prefixes for `Get-Verb`. (#4746) (Thanks @Tadas) +- Fix `Get-Content -Raw` to not miss the last line feed character. (#5076) +- Add authentication parameters to Web Cmdlets. (#5052) (Thanks @markekraus) + - Add `-Authentication` that provides three options: Basic, OAuth, and Bearer. + - Add `-Token` to get the bearer token for OAuth and Bearer options. + - Add `-AllowUnencryptedAuthentication` to bypass authentication that is provided for any transport scheme other than HTTPS. +- Fix `MatchInfoContext` clone implementation (#5121) (Thanks @dee-see) +- Exclude `PSHostProcess` cmdlets from Unix platforms. (#5105) +- Fix `Add-Member` to fetch resource string correctly. (#5114) +- Enable `Import-Module` to be case insensitive. (#5097) +- Add exports for `syslog` APIs in `libpsl-native`. (#5149) +- Fix `Get-ChildItem` to not ignore `-Depth` parameter when using with `-Include` or `-Exclude`. (#4985) (Thanks @Windos) +- Added properties `UserName`, `Description`, `DelayedAutoStart`, `BinaryPathName` and `StartupType` to the `ServiceController` objects returned by `Get-Service`. (#4907) (Thanks @joandrsn) + +### Build and Packaging Improvements + +- Treat `.rtf` files as binary so EOL don't get changed. (#5020) +- Improve the output of `tools/installpsh-osx.sh` and update Travis-CI to use Ruby 2.3.3. (#5065) +- Improve `Start-PSBootstrap` to locate dotnet SDK before installing it. (#5059) (Thanks @PetSerAl) +- Fix the prerequisite check of the MSI package. (#5070) +- Support creating `tar.gz` package for Linux and macOS. (#5085) +- Add release builds that produce symbols for compliance scans. (#5086) +- Update existing Docker files for the Linux package changes. (#5102) +- Add compiler switches and replace dangerous function with safer ones. (#5089) +- Add macOS launcher. (#5138) (Thanks @thezim) +- Replace `httpbin.org/response-headers` Tests with WebListener. (#5058) (Thanks @markekraus) +- Update `appimage.sh` to reflect the new name `pwsh`. (#5172) +- Update the man help file used in packaging. (#5173) +- Update to use `pwsh` in macOS launcher. (#5174) (Thanks @thezim) +- Add code to send web hook for Travis-CI daily build. (#5183) +- Add `global.json` to pick correct SDK version. (#5118) (Thanks @rkeithhill) +- Update packaging to only package PowerShell binaries when packaging symbols. (#5145) +- Update Docker files and related due to the name change. (#5156) + +### Code Cleanup + +- Clean up Json cmdlets. (#5001) (Thanks @iSazonov) +- Remove code guarded by `RELATIONSHIP_SUPPORTED` and `SUPPORTS_IMULTIVALUEPROPERTYCMDLETPROVIDER`, which has never been used. (#5066) +- Remove PSMI code that has never been used. (#5075) +- Remove unreachable code for `Stop-Job`. (#5091) (Thanks @travisty-) +- Removed font and codepage handling code that is only applicable to Windows PowerShell. (#4995) + +### Test + +- Fix a race condition between `WebListener` and Web Cmdlets tests. (#5035) (Thanks @markekraus) +- Add warning to `Start-PSPester` if Pester module is not found (#5069) (Thanks @DdWr) +- Add tests for DSC configuration compilation on Windows. (#5011) +- Test fixes and code coverage automation fixes. (#5046) + +### Documentation and Help Content + +- Update Pi demo instructions about installing libunwind8. (#4974) +- Add links on best practice guidelines in coding guideline. (#4983) (Thanks @iSazonov) +- Reformat command line help for `powershell -help` (#4989) (Thanks @iSazonov) +- Change logo in readme to current black icon. (#5030) +- Fix RPM package name in `README.md`. (#5044) +- Update `docs/building/linux.md` to reflect the current status of powershell build. (#5068) (Thanks @dee-see) +- Add black version of `.icns` file for macOS. (#5073) (Thanks @thezim) +- Update Arch Linux installation instructions. (#5048) (Thanks @kylesferrazza) +- Add submodule reminder to `testing-guidelines.md`. (#5061) (Thanks @DdWr) +- Update instructions in `docs/building/internals.md` for building from source. (#5072) (Thanks @kylesferrazza) +- Add UserVoice link to Issue Template. (#5100) (Thanks @markekraus) +- Add `Get-WebListenerUrl` Based Examples to WebListener `README.md`. (#4981) (Thanks @markekraus) +- Add document about how to create cmdlet with dotnet CLI. (#5117) (Thanks @rkeithhill) +- Update the help text for PowerShell executable with the new name `pwsh`. (#5182) +- Add new forward links for PowerShell 6.0.0 help content. (#4978) +- Fix VSCode `launch.json` to point to `pwsh`. (#5189) +- Add example of how to create .NET Core cmdlet with Visual Studio. (#5096) + +## [6.0.0-beta.8] - 2017-10-05 + +### Breaking changes + +* Changed `New-Service` to return error when given unsupported `-StartupType` and fixed `Set-Service` icon failing test. (#4802) +* Allow `*` to be used in registry path for `Remove-Item`. (#4866) +* Remove unsupported `-ShowWindow` switch from `Get-Help`. (#4903) +* Fix incorrect position of a parameter which resulted in the args passed as input instead of as args for `InvokeScript()`. (#4963) + +### Engine updates and fixes + +* Make calls to `void CodeMethod` work. (#4850) (Thanks @powercode) +* Get `PSVersion` and `GitCommitId` from the `ProductVersion` attribute of assembly (#4863) (Thanks @iSazonov) +* Fix `powershell -version` and built-in help for `powershell.exe` to align with other native tools. (#4958 & #4931) (Thanks @iSazonov) +* Load assemblies with `Assembly.LoadFrom` before `Assembly.Load` when the file path is given. (#4196) +* Add a generic file watcher function in `HelpersCommon.psm1`. (#4775) +* Update old links and fix broken links in `docs/host-powershell/README.md`. (#4877) +* Fix when importing remote modules using version filters (and added tests). (#4900) +* Enable transcription of native commands on non-Windows platforms. (#4871) +* Add a new line to `CommandNotFoundException` error string. (#4934 & #4991) +* Fix bug where PowerShell would exit with an error within an SSH remoting connection on Linux. (#4993) +* Fix issues with expression redirected to file. (#4847) + +### General cmdlet updates and fixes + +* Added `Remove-Service` to Management module. (#4858) (Thanks @joandrsn) +* Added functionality to set credentials on `Set-Service` command. (#4844) (Thanks @joandrsn) +* Fix `Select-String` to exclude directories (as opposed to individual files) discovered from `-Path`. (#4829) (Thanks @iSazonov) +* `Get-Date` now supports more argument completion scenarios by adding `ArgumentCompletionsAttribute`. (#4835) (Thanks @iSazonov) +* Exclude `-ComObject` parameter of `New-Object` on unsupported (currently non-Windows) platforms. (#4922) (Thanks @iSazonov) +* Updated default `ModuleVersion` in `New-ModuleManifest` to `0.0.1` to align with SemVer. (#4842) (Thanks @LDSpits) +* Add Multipart support to web cmdlets. (#4782) (Thanks @markekraus) +* Add `-ResponseHeadersVariable` to `Invoke-RestMethod` to enable the capture of response headers. (#4888) (Thanks @markekraus) +* Initialize web cmdlets headers dictionary only once. (#4853) (Thanks @markekraus) +* Change web cmdlets `UserAgent` from `WindowsPowerShell` to `PowerShell`. (#4914) (Thanks @markekraus) + +### Build and Packaging Improvements + +* Make the build output the WiX compilation log if it failed. (#4831) (Thanks @bergmeister) +* Use a simple file based check in the MSI for the VC++ 2015 redistributables. (#4745) (Thanks @bergmeister) +* New icon for PowerShell Core. (#4848) +* Build Powershell Core using the generic RID `linux-x64`. (#4841) +* Create generic Linux-x64 packages that are portable to all supported RPM Linux distros (and more similar for Debian based distros). (#4902 & #4994) +* Suppress the output of building test tools in `Compress-TestContent`. (#4957) +* Remove unnecessary error messages from output. (#4954) +* Update Travis CI script so that PRs can fail due to Pester tests. (#4830) +* Move release build definition into PowerShell. (#4884) +* Fix credential scan issues. (#4927 & #4935) +* Enable security flags in native compiler. (#4933) +* Add VS 2017 solution file for `powershell-win-core`. (#4748) + +### Code Cleanup + +* Remove remainder of `Utility.Activities` (Workflow code). (#4880) +* Remove `Microsoft.PowerShell.CoreCLR.AssemblyLoadContext.dll`. (#4868) +* Enable auto EOL on Git repo side, fix some character encoding issues. (#4912) +* Updated EOL for all files to be LF in the repository. (#4943 & #4956) +* Removed leading whitespace. (#4991) + +### DSC Language + +* Update version of `PSDesiredStateConfiguration` in project files to fix complication of MOF files with the `Configuration` keyword. (#4979) + +### Test + +* Replace httpbin.org tests with `WebListener`. (Thanks @markekraus) + * headers (#4799) + * user-agent (#4798) + * redirect (#4852) + * encoding (#4869) + * delay (#4905) + * gzip & enable deflate (#4948) + * related changes and fixes (#4920) +* Port tests for constrained language mode. (#4816) +* Enable `Select-String` test from a network path. (#4921) (Thanks @iSazonov) +* Reformat `Measure-Object` test. (#4972) (Thanks @iSazonov) +* Mitigate intermittent failures in access denied tests. (#4788) +* Fix tests that incorrectly use `ShouldBeErrorId`. (#4793) +* Fix a test issue that causes tests to be skipped in Travis CI run (#4891) +* Skip web cmdlet certificate authentication tests on CentOS and Mac. (#4822) +* Validate product resource strings against resx files. (#4811 & #4861) +* Add source files for coverage run. (#4925) +* Add the UTC offset correctly in tests for CDXML cmdlets. (#4867) +* Be sure to change `PSDefaultParameterValue` in the global scope. (#4977 & #4892) +* Reduce output of Pester for CI. (#4855) +* Add tests for + * `Get-Content` (#4723) (Thanks @sarithsutha) + * Remoting and Jobs (#4928) + * `Get-Help` (#4895) + * `Get-Command -ShowCommandInfo` (#4906) + * `Get-Content -Tail` (#4790) + * `Get-Module` over remoting (#4787) + * `Start/Stop/Suspend/Resume/Restart-Service` cmdlets (#4774) + * WSMan Config provider tests (#4756) + * CDXML CIM `DateTime` test (#4796) + +### Documentation and Graphics + +* Sort `.spelling` (Thanks @markekraus) +* Improve the guideline for performance consideration. (#4824) +* Add setup steps for MacOS to use PSRP over SSH. (#4872) +* Instructions to demo PowerShell Core on Raspbian. (#4882) +* Added instructions to get permission to use PowerShell image assets. (#4938) +* Added demo for using Windows PowerShell modules. (#4886) + +## [6.0.0-beta.7] - 2017-09-13 + +### Breaking change + +* Fix `Get-Content -Delimiter` to not include the delimiter in the array elements returned (#3706) (Thanks @mklement0) +* Rename `$IsOSX` to `$IsMacOS` (#4757) + +### Engine updates and fixes + +* Use stricter rules when unwrapping a PSObject that wraps a COM object (#4614) +* Remove appended Windows PowerShell `PSModulePath` on Windows. (#4656) +* Ensure `GetNetworkCredential()` returns null if PSCredential has null or empty user name (#4697) +* Push locals of automatic variables to 'DottedScopes' when dotting script cmdlets (#4709) +* Fix `using module` when module has non-terminating errors handled with `SilentlyContinue` (#4711) (Thanks @iSazonov) +* Enable use of 'Singleline,Multiline' option in split operator (#4721) (Thanks @iSazonov) +* Fix error message in `ValidateSetAttribute.ValidateElement()` (#4722) (Thanks @iSazonov) + +### General cmdlet updates and fixes + +* Add Meta, Charset, and Transitional parameters to `ConvertTo-HTML` (#4184) (Thanks @ergo3114) +* Prevent `Test-ModuleManifest` from loading unnecessary modules (#4541) +* Remove AlternateStream code and `-Stream` from provider cmdlets on non-Windows (#4567) +* Add explicit ContentType detection to `Invoke-RestMethod` (#4692) +* Fix an error on `Enter-PSSession` exit (#4693) +* Add `-WhatIf` switch to `Start-Process` cmdlet (#4735) (Thanks @sarithsutha) +* Remove double spaces in .cs, .ps1, and .resx files (#4741 & #4743) (Thanks @korygill) +* Replace 'Windows PowerShell' with 'PowerShell' in resx files (#4758) (Thanks @iSazonov) + +### Build and Packaging Improvements + +* Refactor MSBuild project files to get PowerShell version from git tag (#4182) (Thanks @iSazonov) +* Create a single package for each Windows supported architecture (x86 and amd64) (#4540) +* Set the default windows RID to win7- (#4701) +* Enable cross-compiling for Raspberry-PI arm32 (#4742) +* Fix macOS brew reinstall command (#4627) (Thanks @TheNewStellW) +* Improvements to the Travis-CI script (#4689, #4731, #4807) +* Update OpenSUSE docker image to 42.2 (#4737) +* Confirm `Start-PSPackage` produces a package (#4795) + +### Code Cleanup + +* Remove Workflow code (#4777) +* Clean up CORECLR preprocessor directives in TraceSource (#4684) + +### Test + +* Add test WebListener module and tests for Web Cmdlet Certificate Authentication (#4622) (Thanks @markekraus) +* Move WebCmdlets HTTPS tests to WebListener (#4733) (Thanks @markekraus) +* Replace httpbin.org/get tests With WebListener (#4738) (Thanks @markekraus) +* Use `-PassThru` on Pester tests to reliably catch failures (#4644) +* Display the same number of tests regardless of platform (#4728) +* Improve comparison of code coverage values for a file (#4764) +* Silence PSSessionConfiguration test warning messages in the log (#4794) +* Add tests for + * `Get-Service` (#4773) + * `Set-Service` and `New-Service` (#4785) + * `Trace-Command` (#4288) + * `StaticParameter` (#4779) + * `Test-Wsman` (#4771) + * `New-Object -ComObject` (#4776) + * ProxyCommand APIs (#4791) +* Disable tests + * 'VC++ Redistributable'(#4673 & #4729) + * "Test 01. Standard Property test - all properties ()" due to missing CsPhysicallyInstalledMemory (#4763) + * `New-Service` failing test (#4806) + +### Documentation + +* Update WritingPesterTests.md to recommend ShouldBeErrorId (#4637) +* Clarify the Pull Request process, roles, and responsibilities (#4710) +* Add absolute URLs in the issue template and pull request template (#4718) (Thanks @chucklu) +* Add new approved Build and Deploy verbs (#4725) +* Update using-vscode.md to use the new exe path (#4736) +* Update coding guidelines to make it more concrete and useful in a review process (#4754) + +## [6.0.0-beta.6] - 2017-08-24 + +### Breaking change + +* Make invalid argument error messages for `-File` and `-Command` consistent and make exit codes consistent with Unix standards (#4573) + +### Engine updates and fixes + +* Make resource loading to work with PowerShell SxS installation (#4139) +* Add missing assemblies to TPA list to make Pwrshplughin.dll work (#4502) +* Make sure running `powershell` starts instance of the current version of PowerShell. (#4481) +* Make sure we only use Unicode output by default on Nano and IoT systems (#4074) +* Enable `powershell -WindowStyle` to work on Windows. (#4573) +* Enable enumeration of COM collections. (#4553) + +### General cmdlet updates and fixes + +* Fix Web CmdLets `-SkipHeaderValidation` to work with non-standard User-Agent headers. (#4479 & #4512) (Thanks @markekraus) +* Add Certificate authentication support for Web CmdLets. (#4646) (Thanks @markekraus) +* Add support for content headers to Web CmdLets. (#4494 & #4640) (Thanks @markekraus) +* Add support for converting enums to string (#4318) (Thanks @KirkMunro) +* Ignore casing when binding PSReadline KeyHandler functions (#4300) (Thanks @oising) +* Fix `Unblock-File` for the case of a read-only file. (#4395) (Thanks @iSazonov) +* Use supported API to set Central Access Policy ID (CAPID) in SACL. (#4496) +* Make `Start-Trace` support paths that require escaping in the underlying APIs (#3863) +* Removing `#if CORECLR` enabled, `Enable-PSRemoting` and `Disable-PSRemoting` (#2671) +* Enable WSManCredSSP cmdlets and add tests. (#4336) +* Use .NET Core's implementation for ShellExecute. (#4523) +* Fix SSH Remoting handling of KeyFileParameter when the path must be quoted. (#4529) +* Make Web CmdLets use HTML meta charset attribute value, if present (#4338) +* Move to .NET Core 2.0 final (#4603) + +### Build/test and code cleanup + +* Add Amazon Linux Docker image and enable related tests. (#4393) (Thanks @DarwinJS) +* Make MSI verify pre-requisites are installed. (#4602) (Thank @bergmeister) +* Fixed formatting issues in build files. (#4630) (Thanks @iSazonov) +* Make sure `install-powershell.sh` installs latest powershell on macOS, even if an old version is cached in brew. (#4509) (Thanks @richardszalay for reporting.) +* Fixes install scripts issue for macOS. (#4631) (Thanks @DarwinJS) +* Many stability improvements to our nightly code coverage automation. (#4313 & #4550) +* Remove hash validation from nanoserver-insider Docker file, due to frequent changes. (#4498) +* Update to make Travis-CI daily build badge more reliable. (#4522) +* Remove unused build files, build code, and product code. (#4532, #4580, #4590, #4589, #4588, #4587, #4586, #4583, #4582, #4581) +* Add additional acceptance tests for PowerShellGet. (#4531) +* Only publish a NuGet of the full PowerShell core package on daily builds and not merge. (#4517) +* Update nanoserver-insider Docker file due to breaking changes in the base image. (#4555) +* Cleanup engine tests (#4551) +* Fix intermittent failures in filesystem tests (#4566) +* Add tests for + * `New-WinEvent`. (#4384) + * tab completion. (#4560) + * various types. (#4503) + * CDXML CmdLets. (#4537) +* Only allow packaging of powershell, if it was built from a repo at the root of the file system named powershell. (#4569 & #4600) +* Update `Format-Hex` test cases to use -TestCase instead of foreach loops. (#3800) +* Added functionality to get code coverage for a single file locally. (#4556) + +### Documentation + +* Added Ilya (@iSazonov) as a Maintainer. (#4365) +* Grammar fix to the Pull Request Guide. (#4322) +* Add homebrew for macOS to install documentation. (#3838) +* Added a CodeOwner file. (#4565 & #4597) + +### Cleanup `#if CORECLR` code + +PowerShell 6.0 will be exclusively built on top of CoreCLR, +so we are removing a large amount of code that's built only for FullCLR. +To read more about this, check out [this blog post](https://devblogs.microsoft.com/powershell/powershell-6-0-roadmap-coreclr-backwards-compatibility-and-more/). + +## [6.0.0-beta.5] - 2017-08-02 + +### Breaking changes + +* Remove the `*-Counter` cmdlets in `Microsoft.PowerShell.Diagnostics` due to the use of unsupported APIs until a better solution is found. (#4303) +* Remove the `Microsoft.PowerShell.LocalAccounts` due to the use of unsupported APIs until a better solution is found. (#4302) + +### Engine updates and fixes + +* Fix the issue where PowerShell Core wasn't working on Windows 7 or Windows Server 2008 R2/2012 (non-R2). (#4463) +* `ValidateSetAttribute` enhancement: support set values to be dynamically generated from a custom `ValidateSetValueGenerator`. (#3784) (Thanks to @iSazonov!) +* Disable breaking into debugger on Ctrl+Break when running non-interactively. (#4283) (Thanks to @mwrock!) +* Give error instead of crashing if WSMan client library is not available. (#4387) +* Allow passing `$true`/`$false` as a parameter to scripts using `powershell.exe -File`. (#4178) +* Enable `DataRow`/`DataRowView` adapters in PowerShell Core to fix an issue with `DataTable` usage. (#4258) +* Fix an issue where PowerShell class static methods were being shared across `Runspace`s/`SessionState`s. (#4209) +* Fix array expression to not return null or throw error. (#4296) +* Fixes a CIM deserialization bug where corrupted CIM classes were instantiating non-CIM types. (#4234) +* Improve error message when `HelpMessage` property of `ParameterAttribute` is set to empty string. (#4334) +* Make `ShellExecuteEx` run in a STA thread. (#4362) + +### General cmdlet updates and fixes + +* Add `-SkipHeaderValidation` switch to `Invoke-WebRequest` and `Invoke-RestMethod` to support adding headers without validating the header value. (#4085) +* Add support for `Invoke-Item -Path `. (#4262) +* Fix `ConvertTo-Html` output when using a single column header. (#4276) +* Fix output of `Length` for `FileInfo` when using `Format-List`. (#4437) +* Fix an issue in implicit remoting where restricted sessions couldn't use `Get-FormatData �PowerShellVersion`. (#4222) +* Fix an issue where `Register-PSSessionConfiguration` fails if `SessionConfig` folder doesn't exist. (#4271) + +### Installer updates + +* Create script to install latest PowerShell from Microsoft package repositories (or Homebrew) on non-Windows platforms. (#3608) (Thanks to @DarwinJS!) +* Enable MSI upgrades rather than a side-by-side install. (#4259) +* Add a checkbox to open PowerShell after the Windows MSI installer has finished. (#4203) (Thanks to @bergmeister!) +* Add Amazon Linux compatibility to `install-powershell.sh`. (#4360) (Thanks to @DarwinJS!) +* Add ability to package PowerShell Core as a NuGet package. (#4363) + +### Build/test and code cleanup + +* Add build check for MFC for Visual C++ during Windows builds. + This fixes a long-standing (and very frustrating!) issue with missing build dependencies! (#4185) (Thanks to @KirkMunro!) +* Move building Windows PSRP binary out of `Start-PSBuild`. + Now `Start-PSBuild` doesn't build PSRP binary on windows. Instead, we consume the PSRP binary from a NuGet package. (#4335) +* Add tests for built-in type accelerators. (#4230) (Thanks to @dchristian3188!) +* Increase code coverage of `Get-ChildItem` on file system. (#4342) (Thanks to @jeffbi!) +* Increase test coverage for `Rename-Item` and `Move-Item`. (#4329) (Thanks to @jeffbi!) +* Add test coverage for Registry provider. (#4354) (Thanks to @jeffbi!) +* Fix warnings and errors thrown by PSScriptAnalyzer. (#4261) (Thanks to @bergmeister!) +* Fix regressions that cause implicit remoting tests to fail. (#4326) +* Disable legacy UTC and SQM Windows telemetry by enclosing the code in '#if LEGACYTELEMETRY'. (#4190) + +### Cleanup `#if CORECLR` code + +PowerShell 6.0 will be exclusively built on top of CoreCLR, +so we are removing a large amount of code that's built only for FullCLR. +To read more about this, check out [this blog post](https://devblogs.microsoft.com/powershell/powershell-6-0-roadmap-coreclr-backwards-compatibility-and-more/). + +## [6.0.0-beta.4] - 2017-07-12 + +## Windows PowerShell backwards compatibility + +In the `beta.4` release, we've introduced a change to add the Windows PowerShell `PSModulePath` to the default `PSModulePath` in PowerShell Core on Windows. (#4132) + +Along with the introduction of .NET Standard 2.0 in `6.0.0-beta.1` and a GAC probing fix in `6.0.0-beta.3`, +**this change will enable a large number of your existing Windows PowerShell modules/scripts to "just work" inside of PowerShell Core on Windows**. +(Note: We have also fixed the CDXML modules on Windows that were regressed in `6.0.0-beta.2` as part of #4144). + +So that we can further enable this backwards compatibility, +we ask that you tell us more about what modules or scripts do and don't work in Issue #4062. +This feedback will also help us determine if `PSModulePath` should include the Windows PowerShell values by default in the long run. + +For more information on this, we invite you to read [this blog post explaining PowerShell Core and .NET Standard in more detail](https://blogs.msdn.microsoft.com/powershell/?p=13355). + +### Engine updates and fixes + +- Add Windows PowerShell `PSModulePath` by default on Windows. (#4132) +- Move PowerShell to `2.0.0-preview3-25426-01` and using the .NET CLI version `2.0.0-preview2-006502`. (#4144) +- Performance improvement in PSReadline by minimizing writing ANSI escape sequences. (#4110) +- Implement Unicode escape parsing so that users can use Unicode characters as arguments, strings or variable names. (#3958) (Thanks to @rkeithhill!) +- Script names or full paths can have commas. (#4136) (Thanks to @TimCurwick!) +- Added `semver` as a type accelerator for `System.Management.Automation.SemanticVersion`. (#4142) (Thanks to @oising!) +- Close `eventLogSession` and `EventLogReader` to unlock an ETL log. (#4034) (Thanks to @iSazonov!) + +### General cmdlet updates and fixes + +- `Move-Item` cmdlet honors `-Include`, `-Exclude`, and `-Filter` parameters. (#3878) +- Add a parameter to `Get-ChildItem` called `-FollowSymlink` that traverses symlinks on demand, with checks for link loops. (#4020) +- Change `New-ModuleManifest` encoding to UTF8NoBOM on non-Windows platforms. (#3940) +- `Get-AuthenticodeSignature` cmdlets can now get file signature timestamp. (#4061) +- Add tab completion for `Export-Counter` `-FileFormat` parameter. (#3856) +- Fixed `Import-Module` on non-Windows platforms so that users can import modules with `NestedModules` and `RootModules`. (#4010) +- Close `FileStream` opened by `Get-FileHash`. (#4175) (Thanks to @rkeithhill!) + +### Remoting + +- Fixed PowerShell not responding when the SSH client abruptly terminates. (#4123) + +### Documentation + +- Added recommended settings for VS Code. (#4054) (Thanks to @iSazonov!) + +## [6.0.0-beta.3] - 2017-06-20 + +### Breaking changes + +- Remove the `BuildVersion` property from `$PSVersionTable`. + This property was strongly tied to the Windows build version. + Instead, we recommend that you use `GitCommitId` to retrieve the exact build version of PowerShell Core. + (#3877) (Thanks to @iSazonov!) +- Change positional parameter for `powershell.exe` from `-Command` to `-File`. + This fixes the usage of `#!` (aka as a shebang) in PowerShell scripts that are being executed from non-PowerShell shells on non-Windows platforms. + This also means that you can now do things like `powershell foo.ps1` or `powershell fooScript` without specifying `-File`. + However, this change now requires that you explicitly specify `-c` or `-Command` when trying to do things like `powershell.exe Get-Command`. + (#4019) +- Remove `ClrVersion` property from `$PSVersionTable`. + (This property is largely irrelevant for .NET Core, + and was only preserved in .NET Core for specific legacy purposes that are inapplicable to PowerShell.) + (#4027) + +### Engine updates and fixes + +- Add support to probe and load assemblies from GAC on Windows platform. + This means that you can now load Windows PowerShell modules with assembly dependencies which reside in the GAC. + If you're interested in running your traditional Windows PowerShell scripts and cmdlets using the power of .NET Standard 2.0, + try adding your Windows PowerShell module directories to your PowerShell Core `$PSModulePath`. + (E.g. `$env:PSModulePath += ';C:\Program Files\WindowsPowerShell\Modules;C:\WINDOWS\system32\WindowsPowerShell\v1.0\Modules'`) + Even if the module isn't owned by the PowerShell Team, please tell us what works and what doesn't by leaving a comment in [issue #4062][issue-4062]! (#3981) +- Enhance type inference in tab completion based on runtime variable values. (#2744) (Thanks to @powercode!) + This enables tab completion in situations like: + + ```powershell + $p = Get-Process + $p | Foreach-Object Prio + ``` + +- Add `GitCommitId` to PowerShell Core banner. + Now you don't have to run `$PSVersionTable` as soon as you start PowerShell to get the version! (#3916) (Thanks to @iSazonov!) +- Fix a bug in tab completion to make `native.exe --` call into native completer. (#3633) (Thanks to @powercode!) +- Fix PowerShell Core to allow use of long paths that are more than 260 characters. (#3960) +- Fix ConsoleHost to honour `NoEcho` on Unix platforms. (#3801) +- Fix transcription to not stop when a Runspace is closed during the transcription. (#3896) + +[issue-4062]: https://github.com/PowerShell/PowerShell/issues/4062 + +### General cmdlet updates and fixes + +- Enable `Send-MailMessage` in PowerShell Core. (#3869) +- Fix `Get-Help` to support case insensitive pattern matching on Unix platforms. (#3852) +- Fix tab completion on `Get-Help` for `about_*` topics. (#4014) +- Fix PSReadline to work in Windows Server Core container image. (#3937) +- Fix `Import-Module` to honour `ScriptsToProcess` when `-Version` is specified. (#3897) +- Strip authorization header on redirects with web cmdlets. (#3885) +- `Start-Sleep`: add the alias `ms` to the parameter `-Milliseconds`. (#4039) (Thanks to @Tadas!) + +### Developer experience + +- Make hosting PowerShell Core in your own .NET applications much easier by refactoring PowerShell Core to use the default CoreCLR loader. (#3903) +- Update `Add-Type` to support `CSharpVersion7`. (#3933) (Thanks to @iSazonov) + +## [6.0.0-beta.2] - 2017-06-01 + +### Support backgrounding of pipelines with ampersand (`&`) (#3360) + +- Putting `&` at the end of a pipeline will cause the pipeline to be run as a PowerShell job. +- When a pipeline is backgrounded, a job object is returned. +- Once the pipeline is running as a job, all of the standard `*-Job` cmdlets can be used to manage the job. +- Variables (ignoring process-specific variables) used in the pipeline are automatically copied to the job so `Copy-Item $foo $bar &` just works. +- The job is also run in the current directory instead of the user's home directory. +- For more information about PowerShell jobs, see [about_Jobs](https://docs.microsoft.com/powershell/module/microsoft.powershell.core/about/about_jobs). + +### Engine updates and fixes + +- Crossgen more of the .NET Core assemblies to improve PowerShell Core startup time. (#3787) +- Enable comparison between a `SemanticVersion` instance and a `Version` instance that is constructed only with `Major` and `Minor` version values. + This will fix some cases where PowerShell Core was failing to import older Windows PowerShell modules. (#3793) (Thanks to @mklement0!) + +### General cmdlet updates and fixes + +- Support Link header pagination in web cmdlets (#3828) + - For `Invoke-WebRequest`, when the response includes a Link header we create a RelationLink property as a Dictionary representing the URLs and `rel` attributes and ensure the URLs are absolute to make it easier for the developer to use. + - For `Invoke-RestMethod`, when the response includes a Link header we expose a `-FollowRelLink` switch to automatically follow `next` `rel` links until they no longer exist or once we hit the optional `-MaximumFollowRelLink` parameter value. +- Update `Get-ChildItem` to be more in line with the way that the *nix `ls -R` and the Windows `DIR /S` native commands handle symbolic links to directories during a recursive search. + Now, `Get-ChildItem` returns the symbolic links it encountered during the search, but it won't search the directories those links target. (#3780) +- Fix `Get-ChildItem` to continue enumeration after throwing an error in the middle of a set of items. + This fixes some issues where inaccessible directories or files would halt execution of `Get-ChildItem`. (#3806) +- Fix `ConvertFrom-Json` to deserialize an array of strings from the pipeline that together construct a complete JSON string. + This fixes some cases where newlines would break JSON parsing. (#3823) +- Enable `Get-TimeZone` for macOS/Linux. (#3735) +- Change to not expose unsupported aliases and cmdlets on macOS/Linux. (#3595) (Thanks to @iSazonov!) +- Fix `Invoke-Item` to accept a file path that includes spaces on macOS/Linux. (#3850) +- Fix an issue where PSReadline was not rendering multi-line prompts correctly on macOS/Linux. (#3867) +- Fix an issue where PSReadline was not working on Nano Server. (#3815) + +## [6.0.0-beta.1] - 2017-05-08 + +### Move to .NET Core 2.0 (.NET Standard 2.0 support) + +PowerShell Core has moved to using .NET Core 2.0 so that we can leverage all the benefits of .NET Standard 2.0. (#3556) +To learn more about .NET Standard 2.0, there's some great starter content [on Youtube](https://www.youtube.com/playlist?list=PLRAdsfhKI4OWx321A_pr-7HhRNk7wOLLY) +and on [the .NET blog](https://devblogs.microsoft.com/dotnet/introducing-net-standard/). +We'll also have more content soon in our [repository documentation](https://github.com/PowerShell/PowerShell/tree/master/docs) (which will eventually make its way to [official documentation](https://github.com/powershell/powershell-docs)). +In a nutshell, .NET Standard 2.0 allows us to have universal, portable modules between Windows PowerShell (which uses the full .NET Framework) and PowerShell Core (which uses .NET Core). +Many modules and cmdlets that didn't work in the past may now work on .NET Core, so import your favorite modules and tell us what does and doesn't work in our GitHub Issues! + +### Telemetry + +- For the first beta of PowerShell Core 6.0, telemetry has been to the console host to report two values (#3620): + - the OS platform (`$PSVersionTable.OSDescription`) + - the exact version of PowerShell (`$PSVersionTable.GitCommitId`) + +If you want to opt-out of this telemetry, simply delete `$PSHome\DELETE_ME_TO_DISABLE_CONSOLEHOST_TELEMETRY`. +Even before the first run of Powershell, deleting this file will bypass all telemetry. +In the future, we plan on also enabling a configuration value for whatever is approved as part of [RFC0015](https://github.com/PowerShell/PowerShell-RFC/blob/master/Archive/Rejected/RFC0015-PowerShell-StartupConfig.md). +We also plan on exposing this telemetry data (as well as whatever insights we leverage from the telemetry) in [our community dashboard](https://devblogs.microsoft.com/powershell/powershell-open-source-community-dashboard/). + +If you have any questions or comments about our telemetry, please file an issue. + +### Engine updates and fixes + +- Add support for native command globbing on Unix platforms. (#3643) + - This means you can now use wildcards with native binaries/commands (e.g. `ls *.txt`). +- Fix PowerShell Core to find help content from `$PSHome` instead of the Windows PowerShell base directory. (#3528) + - This should fix issues where about_* topics couldn't be found on Unix platforms. +- Add the `OS` entry to `$PSVersionTable`. (#3654) +- Arrange the display of `$PSVersionTable` entries in the following way: (#3562) (Thanks to @iSazonov!) + - `PSVersion` + - `PSEdition` + - alphabetical order for rest entries based on the keys +- Make PowerShell Core more resilient when being used with an account that doesn't have some key environment variables. (#3437) +- Update PowerShell Core to accept the `-i` switch to indicate an interactive shell. (#3558) + - This will help when using PowerShell as a default shell on Unix platforms. +- Relax the PowerShell `SemanticVersion` constructors to not require 'minor' and 'patch' portions of a semantic version name. (#3696) +- Improve performance to security checks when group policies are in effect for ExecutionPolicy. (#2588) (Thanks to @powercode) +- Fix code in PowerShell to use `IntPtr(-1)` for `INVALID_HANDLE_VALUE` instead of `IntPtr.Zero`. (#3544) (Thanks to @0xfeeddeadbeef) + +### General cmdlet updates and fixes + +- Change the default encoding and OEM encoding used in PowerShell Core to be compatible with Windows PowerShell. (#3467) (Thanks to @iSazonov!) +- Fix a bug in `Import-Module` to avoid incorrect cyclic dependency detection. (#3594) +- Fix `New-ModuleManifest` to correctly check if a URI string is well formed. (#3631) + +### Filesystem-specific updates and fixes + +- Use operating system calls to determine whether two paths refer to the same file in file system operations. (#3441) + - This will fix issues where case-sensitive file paths were being treated as case-insensitive on Unix platforms. +- Fix `New-Item` to allow creating symbolic links to file/directory targets and even a non-existent target. (#3509) +- Change the behavior of `Remove-Item` on a symbolic link to only removing the link itself. (#3637) +- Use better error message when `New-Item` fails to create a symbolic link because the specified link path points to an existing item. (#3703) +- Change `Get-ChildItem` to list the content of a link to a directory on Unix platforms. (#3697) +- Fix `Rename-Item` to allow Unix globbing patterns in paths. (#3661) + +### Interactive fixes + +- Add Hashtable tab completion for `-Property` of `Select-Object`. (#3625) (Thanks to @powercode) +- Fix tab completion with `@{` to avoid crash in PSReadline. (#3626) (Thanks to @powercode) +- Use ` - ` as `ToolTip` and `ListItemText` when tab completing process ID. (#3664) (Thanks to @powercode) + +### Remoting fixes + +- Update PowerShell SSH remoting to handle multi-line error messages from OpenSSH client. (#3612) +- Add `-Port` parameter to `New-PSSession` to create PowerShell SSH remote sessions on non-standard (non-22) ports. (#3499) (Thanks to @Lee303) + +### API Updates + +- Add the public property `ValidRootDrives` to `ValidateDriveAttribute` to make it easy to discover the attribute state via `ParameterMetadata` or `PSVariable` objects. (#3510) (Thanks to @indented-automation!) +- Improve error messages for `ValidateCountAttribute`. (#3656) (Thanks to @iSazonov) +- Update `ValidatePatternAttribute`, `ValidateSetAttribute` and `ValidateScriptAttribute` to allow users to more easily specify customized error messages. (#2728) (Thanks to @powercode) + +## [6.0.0-alpha.18] - 2017-04-05 + +### Progress Bar + +We made a number of fixes to the progress bar rendering and the `ProgressRecord` object that improved cmdlet performance and fixed some rendering bugs on non-Windows platforms. + +- Fix a bug that caused the progress bar to drift on Unix platforms. (#3289) +- Improve the performance of writing progress records. (#2822) (Thanks to @iSazonov!) +- Fix the progress bar rendering on Unix platforms. (#3362) (#3453) +- Reuse `ProgressRecord` in Web Cmdlets to reduce the GC overhead. (#3411) (Thanks to @iSazonov!) + +### Cmdlet updates + +- Use `ShellExecute` with `Start-Process`, `Invoke-Item`, and `Get-Help -Online` so that those cmdlets use standard shell associations to open a file/URI. + This means you `Get-Help -Online` will always use your default browser, and `Start-Process`/`Invoke-Item` can open any file or path with a handler. + (Note: there are still some problems with STA threads.) (#3281, partially fixes #2969) +- Add `-Extension` and `-LeafBase` switches to `Split-Path` so that you can split paths between the filename extension and the rest of the filename. (#2721) (Thanks to @powercode!) +- Implement `Format-Hex` in C# along with some behavioral changes to multiple parameters and the pipeline. (#3320) (Thanks to @MiaRomero!) +- Add `-NoProxy` to web cmdlets so that they ignore the system-wide proxy setting. (#3447) (Thanks to @TheFlyingCorpse!) +- Fix `Out-Default -Transcript` to properly revert out of the `TranscribeOnly` state, so that further output can be displayed on Console. (#3436) (Thanks to @PetSerAl!) +- Fix `Get-Help` to not return multiple instances of the same help file. (#3410) + +### Interactive fixes + +- Enable argument auto-completion for `-ExcludeProperty` and `-ExpandProperty` of `Select-Object`. (#3443) (Thanks to @iSazonov!) +- Fix a tab completion bug that prevented `Import-Module -n` from working. (#1345) + +### Cross-platform fixes + +- Ignore the `-ExecutionPolicy` switch when running PowerShell on non-Windows platforms because script signing is not currently supported. (#3481) +- Standardize the casing of the `PSModulePath` environment variable. (#3255) + +### JEA fixes + +- Fix the JEA transcription to include the endpoint configuration name in the transcript header. (#2890) +- Fix `Get-Help` in a JEA session. (#2988) + +## [6.0.0-alpha.17] - 2017-03-08 + +- Update PSRP client libraries for Linux and Mac. + - We now support customer configurations for Office 365 interaction, as well as NTLM authentication for WSMan based remoting from Linux (more information [here](https://github.com/PowerShell/psl-omi-provider/releases/tag/v1.0.0.18)). (#3271) +- We now support remote step-in debugging for `Invoke-Command -ComputerName`. (#3015) +- Use prettier formatter with `ConvertTo-Json` output. (#2787) (Thanks to @kittholland!) +- Port `*-CmsMessage` and `Get-PfxCertificate` cmdlets to Powershell Core. (#3224) +- `powershell -version` now returns version information for PowerShell Core. (#3115) +- Add the `-TimeOut` parameter to `Test-Connection`. (#2492) +- Add `ShouldProcess` support to `New-FileCatalog` and `Test-FileCatalog` (fixes `-WhatIf` and `-Confirm`). (#3074) (Thanks to @iSazonov!) +- Fix `Test-ModuleManifest` to normalize paths correctly before validating. + - This fixes some problems when using `Publish-Module` on non-Windows platforms. (#3097) +- Remove the `AliasProperty "Count"` defined for `System.Array`. + - This removes the extraneous `Count` property on some `ConvertFrom-Json` output. (#3231) (Thanks to @PetSerAl!) +- Port `Import-PowerShellDatafile` from PowerShell script to C#. (#2750) (Thanks to @powercode!) +- Add `-CustomMethod` parameter to web cmdlets to allow for non-standard method verbs. (#3142) (Thanks to @Lee303!) +- Fix web cmdlets to include the HTTP response in the exception when the response status code is not success. (#3201) +- Expose a process' parent process by adding the `CodeProperty "Parent"` to `System.Diagnostics.Process`. (#2850) (Thanks to @powercode!) +- Fix crash when converting a recursive array to a bool. (#3208) (Thanks to @PetSerAl!) +- Fix casting single element array to a generic collection. (#3170) +- Allow profile directory creation failures for Service Account scenarios. (#3244) +- Allow Windows' reserved device names (e.g. CON, PRN, AUX, etc.) to be used on non-Windows platforms. (#3252) +- Remove duplicate type definitions when reusing an `InitialSessionState` object to create another Runspace. (#3141) +- Fix `PSModuleInfo.CaptureLocals` to not do `ValidateAttribute` check when capturing existing variables from the caller's scope. (#3149) +- Fix a race bug in WSMan command plug-in instance close operation. (#3203) +- Fix a problem where newly mounted volumes aren't available to modules that have already been loaded. (#3034) +- Remove year from PowerShell copyright banner at start-up. (#3204) (Thanks to @kwiknick!) +- Fixed spelling for the property name `BiosSerialNumber` for `Get-ComputerInfo`. (#3167) (Thanks to @iSazonov!) + +## [6.0.0-alpha.16] - 2017-02-15 + +- Add `WindowsUBR` property to `Get-ComputerInfo` result +- Cache padding strings to speed up formatting a little +- Add alias `Path` to the `-FilePath` parameter of `Out-File` +- Fix the `-InFile` parameter of `Invoke-WebRequest` +- Add the default help content to powershell core +- Speed up `Add-Type` by crossgen'ing its dependency assemblies +- Convert `Get-FileHash` from script to C# implementation +- Fix lock contention when compiling the code to run in interpreter +- Avoid going through WinRM remoting stack when using `Get-ComputerInfo` locally +- Fix native parameter auto-completion for tokens that begin with a single "Dash" +- Fix parser error reporting for incomplete input to allow defining class in interactive host +- Add the `RoleCapabilityFiles` keyword for JEA support on Windows + +## [6.0.0-alpha.15] - 2017-01-18 + +- Use parentheses around file length for offline files +- Fix issues with the Windows console mode (terminal emulation) and native executables +- Fix error recovery with `using module` +- Report `PlatformNotSupported` on IoT for Get/Import/Export-Counter +- Add `-Group` parameter to `Get-Verb` +- Use MB instead of KB for memory columns of `Get-Process` +- Add new escape character for ESC: `` `e`` +- Fix a small parsing issue with a here string +- Improve tab completion of types that use type accelerators +- `Invoke-RestMethod` improvements for non-XML non-JSON input +- PSRP remoting now works on CentOS without addition setup + +## [6.0.0-alpha.14] - 2016-12-14 + +- Moved to .NET Core 1.1 +- Add Windows performance counter cmdlets to PowerShell Core +- Fix try/catch to choose the more specific exception handler +- Fix issue reloading modules that define PowerShell classes +- `Add ValidateNotNullOrEmpty` to approximately 15 parameters +- `New-TemporaryFile` and `New-Guid` rewritten in C# +- Enable client side PSRP on non-Windows platforms +- `Split-Path` now works with UNC roots +- Implicitly convert value assigned to XML property to string +- Updates to `Invoke-Command` parameters when using SSH remoting transport +- Fix `Invoke-WebRequest` with non-text responses on non-Windows platforms +- `Write-Progress` performance improvement from `alpha13` reverted because it introduced crash with a race condition + +## [6.0.0-alpha.13] - 2016-11-22 + +- Fix `NullReferenceException` in binder after turning on constrained language mode +- Enable `Invoke-WebRequest` and `Invoke-RestMethod` to not validate the HTTPS certificate of the server if required. +- Enable binder debug logging in PowerShell Core +- Add parameters `-Top` and `-Bottom` to `Sort-Object` for Top/Bottom N sort +- Enable `Update-Help` and `Save-Help` on Unix platforms +- Update the formatter for `System.Diagnostics.Process` to not show the `Handles` column +- Improve `Write-Progress` performance by adding timer to update a progress pane every 100 ms +- Enable correct table width calculations with ANSI escape sequences on Unix +- Fix background jobs for Unix and Windows +- Add `Get-Uptime` to `Microsoft.PowerShell.Utility` +- Make `Out-Null` as fast as `> $null` +- Add DockerFile for 'Windows Server Core' and 'Nano Server' +- Fix WebRequest failure to handle missing ContentType in response header +- Make `Write-Host` fast by delay initializing some properties in InformationRecord +- Ensure PowerShell Core adds an initial `/` rooted drive on Unix platforms +- Enable streaming behavior for native command execution in pipeline, so that `ping | grep` doesn't block +- Make `Write-Information` accept objects from pipeline +- Fixes deprecated syscall issue on macOS 10.12 +- Fix code errors found by the static analysis using PVS-Studio +- Add support to W3C Extended Log File Format in `Import-Csv` +- Guard against `ReflectionTypeLoadException` in type name auto-completion +- Update build scripts to support win7-x86 runtime +- Move PackageManagement code/test to oneget.org + +## [6.0.0-alpha.12] - 2016-11-03 + +- Fix `Get-ChildItem -Recurse -ErrorAction Ignore` to ignore additional errors +- Don't block pipeline when running Windows EXE's +- Fix for PowerShell SSH remoting with recent Win32-OpenSSH change. +- `Select-Object` with `-ExcludeProperty` now implies `-Property *` if -Property is not specified. +- Adding ValidateNotNullOrEmpty to `-Name` parameter of `Get-Alias` +- Enable Implicit remoting commands in PowerShell Core +- Fix GetParentProcess() to replace an expensive WMI query with Win32 API calls +- Fix `Set-Content` failure to create a file in PSDrive under certain conditions. +- Adding ValidateNotNullOrEmpty to `-Name` parameter of `Get-Service` +- Adding support in `Get-WinEvent -FilterHashtable` +- Adding WindowsVersion to `Get-ComputerInfo` +- Remove the unnecessary use of lock in PseudoParameterBinder to avoid deadlock +- Refactor `Get-WinEvent` to use StringBuilder for XPath query construction +- Clean up and fix error handling of libpsl-native +- Exclude Registry and Certificate providers from UNIX PS +- Update PowerShell Core to consume .Net Core preview1-24530-04 + +## [6.0.0-alpha.11] - 2016-10-17 + +- Add '-Title' to 'Get-Credential' and unify the prompt experience +- Update dependency list for PowerShell Core on Linux and OS X +- Fix 'powershell -Command -' to not stop responding and to not ignore the last command +- Fix binary operator tab completion +- Enable 'ConvertTo-Html' in PowerShell Core +- Remove most Maximum* capacity variables +- Fix 'Get-ChildItem -Hidden' to work on system hidden files on Windows +- Fix 'JsonConfigFileAccessor' to handle corrupted 'PowerShellProperties.json' + and defer creating the user setting directory until a write request comes +- Fix variable assignment to not overwrite read-only variables +- Fix 'Get-WinEvent -FilterHashtable' to work with named fields in UserData of event logs +- Fix 'Get-Help -Online' in PowerShell Core on Windows +- Spelling/grammar fixes + +## [6.0.0-alpha.10] - 2016-09-15 + +- Fix passing escaped double quoted spaces to native executables +- Add DockerFiles to build each Linux distribution +- `~/.config/PowerShell` capitalization bug fixed +- Fix crash on Windows 7 +- Fix remote debugging on Windows client +- Fix multi-line input with redirected stdin +- Add PowerShell to `/etc/shells` on installation +- Fix `Install-Module` version comparison bug +- Spelling fixes + +## [6.0.0-alpha.9] - 2016-08-15 + +- Better man page +- Added third-party and proprietary licenses +- Added license to MSI + +## [6.0.0-alpha.8] - 2016-08-11 + +- PowerShell packages pre-compiled with crossgen +- `Get-Help` content added +- `Get-Help` null reference exception fixed +- Ubuntu 16.04 support added +- Unsupported cmdlets removed from Unix modules +- PSReadline long prompt bug fixed +- PSReadline custom key binding bug on Linux fixed +- Default terminal colors now respected +- Semantic Version support added +- `$env:` fixed for case-sensitive variables +- Added JSON config files to hold some settings +- `cd` with no arguments now behaves as `cd ~` +- `ConvertFrom-Json` fixed for multiple lines +- Windows branding removed +- .NET CoreCLR Runtime patched to version 1.0.4 +- `Write-Host` with unknown hostname bug fixed +- `powershell` man-page added to package +- `Get-PSDrive` ported to report free space +- Desired State Configuration MOF compilation ported to Linux +- Windows 2012 R2 / Windows 8.1 remoting enabled + +## [6.0.0-alpha.7] - 2016-07-26 + +- Invoke-WebRequest and Invoke-RestMethod ported to PowerShell Core +- Set PSReadline default edit mode to Emacs on Linux +- IsCore variable renamed to IsCoreCLR +- Microsoft.PowerShell.LocalAccounts and other Windows-only assemblies excluded on Linux +- PowerShellGet fully ported to Linux +- PackageManagement NuGet provider ported +- Write-Progress ported to Linux +- Get-Process -IncludeUserName ported +- Enumerating symlinks to folders fixed +- Bugs around administrator permissions fixed on Linux +- ConvertFrom-Json multi-line bug fixed +- Execution policies fixed on Windows +- TimeZone cmdlets added back; excluded from Linux +- FileCatalog cmdlets added back for Windows +- Get-ComputerInfo cmdlet added back for Windows + +## [0.6.0] - 2016-07-08 + +- Targets .NET Core 1.0 release +- PowerShellGet enabled +- [system.manage] completion issues fixed +- AssemblyLoadContext intercepts dependencies correctly +- Type catalog issues fixed +- Invoke-Item enabled for Linux and OS X +- Windows ConsoleHost reverted to native interfaces +- Portable ConsoleHost redirection issues fixed +- Bugs with pseudo (and no) TTY's fixed +- Source Depot synced to baseline changeset 717473 +- SecureString stub replaced with .NET Core package + +## [0.5.0] - 2016-06-16 + +- Paths given to cmdlets are now slash-agnostic (both / and \ work as directory separator) +- Lack of cmdlet support for paths with literal \ is a known issue +- .NET Core packages downgraded to build rc2-24027 (Nano's build) +- XDG Base Directory Specification is now respected and used by default +- Linux and OS X profile path is now `~/.config/powershell/profile.ps1` +- Linux and OS X history save path is now `~/.local/share/powershell/PSReadLine/ConsoleHost_history.txt` +- Linux and OS X user module path is now `~/.local/share/powershell/Modules` +- The `~/.powershell` folder is deprecated and should be deleted +- Scripts can be called within PowerShell without the `.ps1` extension +- `Trace-Command` and associated source cmdlets are now available +- `Ctrl-C` now breaks running cmdlets correctly +- Source Depot changesets up to 715912 have been merged +- `Set-PSBreakPoint` debugging works on Linux, but not on Windows +- MSI and APPX packages for Windows are now available +- Microsoft.PowerShell.LocalAccounts is available on Windows +- Microsoft.PowerShell.Archive is available on Windows +- Linux xUnit tests are running again +- Many more Pester tests are running + +## [0.4.0] - 2016-05-17 + +- PSReadline is ported and included by default +- Original Windows ConsoleHost is ported and replaced CoreConsoleHost +- .NET Core packages set to the RC2 release at build 24103 +- OS X 10.11 added to Continuous Integration matrix +- Third-party C# cmdlets can be built with .NET CLI +- Improved symlink support on Linux +- Microsoft.Management.Infrastructure.Native replaced with package +- Many more Pester tests + +## [0.3.0] - 2016-04-11 + +- Supports Windows, Nano, OS X, Ubuntu 14.04, and CentOS 7.1 +- .NET Core packages are build rc3-24011 +- Native Linux commands are not shadowed by aliases +- `Get-Help -Online` works +- `more` function respects the Linux `$PAGER`; defaults to `less` +- `IsWindows`, `IsLinux`, `IsOSX`, `IsCore` built-in PowerShell variables added +- `Microsoft.PowerShell.Platform` removed for the above +- Cross-platform core host is now `CoreConsoleHost` +- Host now catches exceptions in `--command` scripts +- Host's shell ID changed to `Microsoft.PowerShellCore` +- Modules that use C# assemblies can be loaded +- `New-Item -ItemType SymbolicLink` supports arbitrary targets +- PSReadline implementation supports multi-line input +- `Ctrl-R` provides incremental reverse history search +- `$Host.UI.RawUI` now supported +- `Ctrl-K` and `Ctrl-Y` for kill and yank implemented +- `Ctrl-L` to clear screen now works +- Documentation was completely overhauled +- Many more Pester and xUnit tests added + +## [0.2.0] - 2016-03-08 + +- Supports Windows, OS X, Ubuntu 14.04, and CentOS 7.1 +- .NET Core packages are build 23907 +- `System.Console` PSReadline is fully functional +- Tests pass on OS X +- `Microsoft.PowerShell.Platform` module is available +- `New-Item` supports symbolic and hard links +- `Add-Type` now works +- PowerShell code merged with upstream `rs1_srv_ps` + +## 0.1.0 - 2016-02-23 + +- Supports Windows, OS X, and Ubuntu 14.04 + +[6.0.0]: https://github.com/PowerShell/PowerShell/compare/v6.0.0-rc.2...v6.0.0 +[6.0.0-rc.2]: https://github.com/PowerShell/PowerShell/compare/v6.0.0-rc...v6.0.0-rc.2 +[6.0.0-rc]: https://github.com/PowerShell/PowerShell/compare/v6.0.0-beta.9...v6.0.0-rc +[6.0.0-beta.9]: https://github.com/PowerShell/PowerShell/compare/v6.0.0-beta.8...v6.0.0-beta.9 +[6.0.0-beta.8]: https://github.com/PowerShell/PowerShell/compare/v6.0.0-beta.7...v6.0.0-beta.8 +[6.0.0-beta.7]: https://github.com/PowerShell/PowerShell/compare/v6.0.0-beta.6...v6.0.0-beta.7 +[6.0.0-beta.6]: https://github.com/PowerShell/PowerShell/compare/v6.0.0-beta.5...v6.0.0-beta.6 +[6.0.0-beta.5]: https://github.com/PowerShell/PowerShell/compare/v6.0.0-beta.4...v6.0.0-beta.5 +[6.0.0-beta.4]: https://github.com/PowerShell/PowerShell/compare/v6.0.0-beta.3...v6.0.0-beta.4 +[6.0.0-beta.3]: https://github.com/PowerShell/PowerShell/compare/v6.0.0-beta.2...v6.0.0-beta.3 +[6.0.0-beta.2]: https://github.com/PowerShell/PowerShell/compare/v6.0.0-beta.1...v6.0.0-beta.2 +[6.0.0-beta.1]: https://github.com/PowerShell/PowerShell/compare/v6.0.0-alpha.18...v6.0.0-beta.1 +[6.0.0-alpha.18]: https://github.com/PowerShell/PowerShell/compare/v6.0.0-alpha.17...v6.0.0-alpha.18 +[6.0.0-alpha.17]: https://github.com/PowerShell/PowerShell/compare/v6.0.0-alpha.16...v6.0.0-alpha.17 +[6.0.0-alpha.16]: https://github.com/PowerShell/PowerShell/compare/v6.0.0-alpha.15...v6.0.0-alpha.16 +[6.0.0-alpha.15]: https://github.com/PowerShell/PowerShell/compare/v6.0.0-alpha.14...v6.0.0-alpha.15 +[6.0.0-alpha.14]: https://github.com/PowerShell/PowerShell/compare/v6.0.0-alpha.13...v6.0.0-alpha.14 +[6.0.0-alpha.13]: https://github.com/PowerShell/PowerShell/compare/v6.0.0-alpha.12...v6.0.0-alpha.13 +[6.0.0-alpha.12]: https://github.com/PowerShell/PowerShell/compare/v6.0.0-alpha.11...v6.0.0-alpha.12 +[6.0.0-alpha.11]: https://github.com/PowerShell/PowerShell/compare/v6.0.0-alpha.10...v6.0.0-alpha.11 +[6.0.0-alpha.10]: https://github.com/PowerShell/PowerShell/compare/v6.0.0-alpha.9...v6.0.0-alpha.10 +[6.0.0-alpha.9]: https://github.com/PowerShell/PowerShell/compare/v6.0.0-alpha.8...v6.0.0-alpha.9 +[6.0.0-alpha.8]: https://github.com/PowerShell/PowerShell/compare/v6.0.0-alpha.7...v6.0.0-alpha.8 +[6.0.0-alpha.7]: https://github.com/PowerShell/PowerShell/compare/v0.6.0...v6.0.0-alpha.7 +[0.6.0]: https://github.com/PowerShell/PowerShell/compare/v0.5.0...v0.6.0 +[0.5.0]: https://github.com/PowerShell/PowerShell/compare/v0.4.0...v0.5.0 +[0.4.0]: https://github.com/PowerShell/PowerShell/compare/v0.3.0...v0.4.0 +[0.3.0]: https://github.com/PowerShell/PowerShell/compare/v0.2.0...v0.3.0 +[0.2.0]: https://github.com/PowerShell/PowerShell/compare/v0.1.0...v0.2.0 diff --git a/CHANGELOG/6.1.md b/CHANGELOG/6.1.md new file mode 100644 index 00000000000..59cf2842d78 --- /dev/null +++ b/CHANGELOG/6.1.md @@ -0,0 +1,797 @@ +# 6.1 Changelog + +## [6.1.6] - 2019-09-12 + +### Build and Packaging Improvements + +- Update DotNet SDK and runtime framework version (Internal 9945) + +## [6.1.5] - 2019-07-16 + +### Breaking changes + +- Disable `Enter-PSHostProcess` cmdlet when system in lock down mode (Internal 8968) + +### Build and Packaging Improvements + +- Update DotNet SDK and runtime framework version (Internal 9087) +- Add automated RPM signing to release build (#10013) +- Update copyright symbol for NuGet packages (#9936) +- Bump `System.Net.Http.WinHttpHandler` from `4.5.3` to `4.5.4` (#9790) +- Integrate building NuGet package in the coordinated build (#8947) (#9708) +- Bump `Newtonsoft.Json` (#9662) + +## [6.1.4] - 2019-05-21 + +### Build and Packaging Improvements + +- Disable debugger in System Lock down mode (Internal 8430) +- Port changes for release automation to `6.1` (Internal 8402) +- Fix `MSI` `WIX` generation (#9013) (Internal 8385) +- Update `Microsoft.PowerShell.Archive` version (Internal 8380) +- Update package version in hosting test (Internal 8374) +- Bump to `dotnet` `2.1.11` release +- Remove update build table logic from release build (Internal 8364) +- Add `AccessToken` variable to jobs that perform signing (#9351) +- Support release branches based on the forward slash separator (#8903) + +## [6.1.3] - 2019-02-14 + +### Engine Updates and Fixes + +- Add security mitigation for 6.1.3 release (Internal 6561) + +### Tools + +- Change the feed URL to feed name due to changes in Azure DevOps (#8664) + +### Tests + +- Updating test gallery URL in PackageManagement tests (#7879) + +### Build and Packaging Improvements + +- Get PowerShellGet tests working (#7831) +- Start tracking release build information in an azure storage table (#8850) +- Remove `PDBs` from `fxdependent` package (#8006) +- Make every `csproj` files have its own folder (#8750) +- Update packaging script to build reference assembly targeting `netcoreapp2.1` and use actual `.csproj` files (#8729) +- Move Final artifacts from coordinated build to `finalResults` folder (#8806) +- Refactor Unified Release Build (#8804) +- Add compliance to Coordinated build (#8798) +- Switch to 1.11 of FPM to fix FPM install issue (#8797) +- Update the coordinated build with framework dependent package for dotnet SDK (#8773) +- Add Windows build to coordinated release build YAML (#8695) +- Build package build using Ubuntu 18.04 image (#8666) +- Adding `yml` for Windows Release builds (#8374) +- Update `SignType` in `signing.xml` (#8223) +- Update DotNet SDK and Runtime version (Internal 7004) +- Add `binskim` to coordinated build and increase timeout (#8834) + +## [6.1.2] - 2019-01-15 + +### Tests + +- Fix test failures (Internal 6310) + +### Build and Packaging Improvements + +- Moved the cleanup logic to `Restore-PSModuleToBuild` (Internal 6442) +- Update dependency versions (Internal 6421) +- Create unified release build for macOS and Linux packages (#8399) +- Build Alpine `tar.gz` package in release builds (Internal 6027) + +### Documentation and Help Content + +- Update version for README, Alpine docker file and hosting tests (Internal 6438) + +## [6.1.1] - 2018-11-13 + +### Engine Updates and Fixes + +- Fix issue with logging the null character in `ScriptBlock` logging (Internal 5607) +- Consolidation of all Windows PowerShell work ported to 6.1 (Internal 5233) + +### General Cmdlet Updates and Fixes + +- Use `ZipFile` and `ExtractToDirectory` APIs to extract zip file (Internal 5608) + +## [6.0.5] - 2018-11-13 + +### Engine updates and fixes + +- Fix issue with logging the null character in `ScriptBlock` logging (Internal 5605) + +### General cmdlet updates and fixes + +- Use `ZipFile` and `ExtractToDirectory` APIs to extract zip file (Internal 4802) + +### Build and Packaging Improvements + +- Update `SignType` in `signing.xml` (Internal 5721) +- Port changes to pull PowerShell Gallery modules from Modules `csproj` (Internal 5713) +- Port macOS Release build changes changes from GitHub (#8189, #8188, #8185) +- Fix script path for `PowerShellPackageVsts.ps1` (#8189) +- Workaround for accessing `AzDevOps` Artifacts (#8188) +- Bump various packages to latest patch version (Internal 5675) +- Update PowerShell SDK NuGet various metadata description (Internal 4527, 4510, 4505) + +## [6.0.4] - 2018-08-10 + +### Build and Packaging Improvements + +- Update the Archive module version (Internal 5671) +- Update to .NET Core `2.1.5` with SDK `2.1.403` (#7936) (Thanks @iSazonov!) +- Disable package major upgrade tests for release branch (Internal 5209) +- Bump versions for dependencies (Internal 5612) +- Port changes to allow `AzDevOps` NuGet feeds for macOS build (Internal 5716) +- Port macOS changes from GitHub (#8189, #8188, #8185) +- Add function to create a new `nuget.config` file (#8170) +- Updated `wxs` file to match published packages (Internal 5660) + +### Tests + +- Change API to match cmdlet which is more reliable in `AzDevOps` Pipelines Windows (#8003) +- Fix conflict with `Get-AdlStoreChildItem` from `az` module in tab completion tests (#8167) + +## [6.1.0] - 2018-09-13 + +### Engine Updates and Fixes + +- Enable indexing operations on `System.Tuple` and `System.ValueTuple` (#7633) (Thanks @SeeminglyScience!) +- Use non-virtual call to invoke 'family or assembly' methods on base class from PowerShell class (#7624) (Thanks @yurko7!) +- Handle operations with `ByRef-like` types gracefully in PowerShell (#7533) +- Make the `-settingfile` flag on `pwsh` work for `ScriptBlock` logging on windows (#7631) +- Ensure the `SSHClientSessionTransportManager` stream writer and reader fields are cleared after disposing (#7746) +- Add `LocationChangedAction` handler to support the Windows Compatibility module (#7552) + +### General Cmdlet Updates and Fixes + +- Fix `Set-Service -Status Stopped` to stop services with dependencies (#5525) (Thanks @zhenggu!) +- Add the `Duration` property to `HistoryInfo` (#5208) (Thanks @powercode!) +- Fix null reference in `ConvertFrom-Markdown` when the markdown content is empty (#7463) +- Fix file blocking issue with WebCmdlets (#7676) (Thanks @Claustn!) +- Fix performance issue in `WSMan` provider by using `Refresh()` to update the status rather than instantiating `ServiceController` (#7680) + +### Code Cleanup + +- Remove `Suspend-Job` and `Resume-Job` cmdlets from compilation on Unix platforms (#7650) +- Remove extra spaces in error messages in `Modules.resx` (#7662) (Thanks @sethvs!) +- Cleanup the platform runtime checks from `FileSystemProvider` (#7655) (Thanks @iSazonov!) +- Improve code style of `Send-MailMessage` cmdlet (#7723) (Thanks @ThreeFive-O!) + +### Tools + +- Add tools for PowerShell performance analysis (#7595) (Thanks @lzybkr!) +- Update code coverage module to download zip files based on job ID (#7653) + +### Tests + +- Update test which assumes all previews have the name preview in the version (#7625) +- Update Pester syntax in `Set-Location` test (#7615) (Thanks @iSazonov!) +- Add `ScriptBlock` logging test for Linux and macOS (#7599) (#7586) +- Add tests to report when package references are out of date (#7661) +- Fix `ModuleSpecification.Tests.ps1` (#7663) (Thanks @sethvs!) +- Updates Docker package tests (#7667) + +### Build and Packaging Improvements + +- Update to the latest package references, dotnet core SDK and framework (#7646) (Thanks @iSazonov!) +- Make the artifact upload only occur for non-PR builds (#7657) +- Change to not upload artifacts during pull request due to missing VSTS feature (#7588) +- Remove workaround on VSTS that is no longer needed (#7666) +- Update docker files to use MCR (#7656) +- Add symbolic links for `libssl` and `libcrypto` to Debian 9 build to make remoting work (#7609) +- Simplify the `StartupInfo` type used in Jumplist creation for faster `P/Invoke` (#7580) (Thanks @powercode!) +- Add VSTS CI for Windows (#7536) +- Update the version of `PowerShellGet` module to `1.6.7` (#7564) +- update the version of `PSReadLine` module to `2.0.0-beta3` (#7711) +- Make sure MSI build works for non-preview builds (#7752) +- Build and package framework dependent package (#7729) +- Change locale of `mdspell` to `en-US` (#7671) +- Add daily build on non-windows platforms (#7683) +- Fix Windows MSI to remove the `Uninstall` shortcut during an uninstall when more than one version is installed (#7701) (Thanks @bergmeister!) +- Fix docker image names for release build (#7726) + +### Documentation and Help Content + +- Update the version of .NET Core in docs (#7467) (Thanks @bergmeister!) +- Fix links in `README.md` (#7619) (Thanks @iSazonov!) +- Add VSTS CI build badges for master branch to `README.md` (#7691) (Thanks @bergmeister!) +- Add a paragraph in `CONTRIBUTING.md` about updating `files.wxs` (#7695) (Thanks @iSazonov!) + +# [6.1.0-rc.1]- 2018-08-22 + +### Engine Updates and Fixes + +- Fix to not duplicate the `System32` module path when starting `pwsh` from `pwsh` (#7414) +- Fix sequence point update for `switch/if/for/while/do-while/do-until` statements (#7305) +- Set the cursor to the place where a user hits tab key (#7299) +- Adding `LanguagePrimitives.TryCompare` to provide faster comparisons (#7438) (Thanks @powercode!) +- Improving performance of `LanguagePrimitives.TryConvertTo` (#7418) (Thanks @powercode!) +- Set `PowerShellVersion` to `3.0` for built-in modules to make Windows PowerShell work when starting from PowerShell Core (#7365) +- Avoid extra unnecessary allocations in `PSMemberInfoInternalCollection` (#7435) (Thanks @iSazonov!) +- Enforce the `CompatiblePSEditions` check for modules from the legacy `System32` module path (#7183) +- Make sure that `SettingFile` argument is parsed before we load the settings (#7449) +- Default to `DefaultConsoleWidth` when DotNet says `WindowWidth` is 0 (#7465) + +### General Cmdlet Updates and Fixes + +- Fix parameter name in the `Get-Variable` cmdlet error message (#7384) (Thanks @sethvs!) +- Fix `Move-Item -Path` with wildcard character (#7397) (Thanks @kwkam!) +- Ignore `Newtonsoft.Json` metadata properties in `ConvertFrom-Json` (#7308) (Thanks @louistio!) +- Fix several issues in Markdown cmdlets (#7329) +- Add support for parsing Link Header with variable whitespace (#7322) +- Change parameter order in `Get-Help` and help in order to get first `-Full` and + then `-Functionality` when using Get-Help `-Fu` followed by pressing tab and help `-Fu` followed by pressing tab (#7370) (Thanks @sethvs!) +- Add support for passing files and Markdown directly to `Show-Markdown` (#7354) +- Add `-SkipIndex` parameter to `Select-Object` (#7483) (Thanks @powercode!) +- Improve performance of `Import-CSV` up to 10 times (#7413) (Thanks @powercode!) +- Update `Enable-PSRemoting` so configuration name is unique for Preview releases (#7202) +- Improve performance on JSON to PSObject conversion (#7482) (Thanks @powercode!) +- Fix error message for `Add-Type` when `-AssemblyName` with wildcard is not found (#7444) +- Make native globbing on Unix return an absolute path when it is given an absolute path (#7106) +- Improve the performance of `Group-Object` (#7410) (Thanks @powercode!) +- Remove one unneeded verbose output from `ConvertTo-Json` (#7487) (Thanks @devblackops!) +- Enable `Get-ChildItem` to produce `Mode` property even if cannot determine if hard link (#7355) + +### Code Cleanup + +- Remove empty XML comment lines (#7401) (Thanks @iSazonov!) +- Cleanup Docker files (#7328) +- Correct the comment for `WSManReceiveDataResult.Unmarshal` (#7364) +- Format Utility `csproj` with updated `codeformatter` (#7263) (Thanks @iSazonov!) +- Bulk update format for files in Management folder with `codeformatter` (#7346) (Thanks @iSazonov!) +- Cleanup: replace `Utils.FileExists()/DirectoryExists()/ItemExists()` with DotNet methods (#7129) (Thanks @iSazonov!) +- Update `Utils.IsComObject` to use `Marshal.IsComObject` since CAS is no longer supported in DotNet Core (#7344) +- Fix some style issues in engine code (#7246) (Thanks @iSazonov!) + +### Test + +- Use `-BeExactly` and `-HaveCount` instead of `-Be` in `BugFix.Tests.ps1` (#7386) (Thanks @sethvs!) +- Use `-BeExactly` and `-HaveCount` instead of `-Be` in `TabCompletion.Tests.ps1` (#7380) (Thanks @sethvs!) +- Update CI scripts to support running tests for experimental features (#7419) +- Use `-HaveCount` instead of `-Be` in `Where-Object.Tests.ps1` (#7379) (Thanks @sethvs!) +- Fix ThreadJob tests so that they will run more reliably (#7360) +- Make logging tests for macOS pending (#7433) + +### Build and Packaging Improvements + +- Update Build script owners (#7321) +- Make `MUSL` NuGet package optional (#7316) +- Enable `pwsh-preview` to work on Windows (#7345) +- Fix SDK dependencies +- Add back the `powershell-core` NuGet source for hosting tests +- Fix typo in environment checker (#7547 & #7549) +- Only remove the revision if it is `0` from module version when restoring modules (#7538) +- Update `WCF` and `NJsonSchema` NuGet packages to latest released patch version (#7411) (Thanks @bergmeister!) +- Add Linux and macOS VSTS CI (#7490, #7527, #7535, #7515 & #7516) +- Updated ThreadJob to version `1.1.2` (#7522) +- Add xUnit project to `PowerShell.sln` and make it runnable from within VisualStudio (#7254) (Thanks @bergmeister!) +- Update NuGet packaging code for the new markdown assembly (#7431) +- Update version of modules shipped with PowerShell (#7531) +- Retry restore on failure (#7544 & #7550) +- Update `PowerShellGet` version +- Update NuGet package metadata (#7517) +- Update reference to use packages from `NuGet.org` (#7525) +- `Start-DevPowerShell`: add `-Configuration` and handle `-ArgumentList` more properly (#7300) (Thanks @jazzdelightsme!) +- Add preview icon to macOS launcher (#7448) (Thanks @thezim!) +- Add `Microsoft.PowerShell.MarkdownRender` to `signing.xml` (#7472) +- Fix building on RedHat Enterprise Linux (#7489) +- Build: Also search PATH for `rcedit` (#7503) (Thanks @kwkam!) +- Save modules to un-versioned folder to enable servicing (#7518 & #7523) +- Fix macOS launcher app to allow release and preview versions (#7306) (Thanks @thezim!) + +### Documentation and Help Content + +- Fix docs comments in utility folder (#7192) (Thanks @iSazonov!) +- Fix a typo in `issue-management.md` (#7393) (Thanks @alexandair!) +- Fix casing of `GitHub` in `best-practice.md` (#7392) (Thanks @alexandair!) +- Fix typos in `docs/maintainers/README.md` (#7390) (Thanks @alexandair!) +- Add maintainer's best practice document and update maintainer list (#7311) +- Update Docker link to `PowerShell-Docker` (#7351) (Thanks @JoshuaCooper!) +- Add `Snapcraft` to spelling dictionary (#7318) +- Update `README.md` and `metadata.json` for release `v6.0.4` (#7497) +- Add `Former Repository Maintainers` section in `maintainers/README.md` (#7475) +- Update the `HelpUri` for `Get-ExperimentalFeature` (#7466) + +# [6.1.0-preview.4]- 2018-07-19 + +### Breaking Changes + +- Remove the `VisualBasic` support from Add-Type (#7284) +- Update PowerShell Direct to try `pwsh` then fallback to `powershell` (#7241) +- Make pwsh able to start in a directory with wildcards in the name (#7240) +- Update `Enable-PSRemoting` so configuration name is unique for Preview releases (#7202) +- Enforce the `CompatiblePSEditions` check for modules from the legacy `System32` module path (#7183) + +### Engine Updates and Fixes + +- Add support to experimental features (#7242) +- Fix error when using `Get-ChildItem c:` (#7033) (Thanks @sethvs!) +- Add location history for `Set-Location` to enable `cd -` scenario (issue #2188) (#5051) (Thanks @bergmeister!) +- Fix padding for right aligned column in table formatting (#7136) +- Fix a performance regression to the `-replace` operator after adding `ScriptBlock` support (#7135) +- Fix tab expansion for `Get-Process` on macOS (#7176) +- When using PSRP, if we receive text instead of XML, output it as error to help troubleshoot (#7168) +- Fix trimming of whitespace when table is wrapped (#7184) +- Modified the `Group-Object -AsHashTable` to use the base object of `PSObject` as the key for the `Hashtable` (#7123) +- Add back ADSI and WMI type accelerators (#7085) +- Add `CompatiblePSEditions` to PowerShell Core built-in modules (#7083) +- Make `Start-Process -ArgumentList` to accept `@()` or `$null` (#6597) +- Avoid calling native APIs to check for existence of FileSystem items (#6929) (Thanks @iSazonov!) +- Add copy environment variables from `ProcessStartInfo` to key/pair array used in creating SSH process (#7070) +- Add markdown rendering feature assemblies to the trusted assembly list (#7280) +- Don't fail if `SaferPolicy` API is not available on Windows 10 IoT or NanoServer (#7075) +- Fix conditions for transcription of `Write-Information` command. (#6917) (Thanks @hubuk!) +- Fix a parsing error when `break` and `continue` are used in a switch statement in a finally block (#7273) +- Fix prompt string to be platform agnostic and keep its trailing spaces (#7255) +- Make progress panel display correctly on UNIX when the user is typing. (#6972) +- Revert change to have `SetLocation()` treat wildcarded path as literal if it exists (#7101) +- Make `Select-Object`/`ForEach-Object`/`Where-Object` see dynamic properties (#6898) (Thanks @jazzdelightsme!) +- Fix class searcher to ignore hidden properties (#7188) +- Update remote prompt when using SSH to show username if different (#7191) +- Remove `SemanticVersion` from `knowntypes` list in serialization code to enable interop between Windows PowerShell and PowerShell Core (#7016) +- Add more information to job process failure error (#7251) +- Use .Net Core `File.Delete()` method to remove symbolic links and alternate streams (#7017) (Thanks @iSazonov!) +- Enable `UseShellExecute` on all platforms (#7198) +- Methods with return type `[object]` should return `null` for an empty result (#7138) + +### General Cmdlet Updates and Fixes + +- Add Markdown rendering cmdlets (#6926) +- `Send-MailMessage`: Update all parameters to support `ValueFromPipelineByPropertyName`. (#6911) (Thanks @sethvs!) +- Allow Basic Auth over HTTPS (#6890) +- Add `ThreadJob` module package and tests (#7169) +- Fix Windows Event Log channel isolation semantics (#6956) (Thanks @Robo210!) +- Make `Measure-Object` handle `scriptblock` properties. (#6934) +- Added functionality to retry in `Invoke-RestMethod` and `Invoke-WebRequest`. (#5760) +- Add type inference for `Select-Object` command (#7171) (Thanks @powercode!) +- Add `-AllStats` Switch parameter for `Measure-Object` cmdlet (#7220) (Thanks @kvprasoon!) + +### Code Cleanup + +- Remove unneeded code that forces ARM platforms to run PowerShell in CL mode (#7046) +- Bulk update code base to put `null` on the right-hand-side of a comparison expression (#6949) (Thanks @iSazonov!) +- Remove `MapSecurityZoneWithUrlmon` method and related code (#7103) +- Cleanup: remove the unneeded type `RemotingCommandUtils` (#7029) +- Remove unneeded "Windows-Full" modules (#7030) +- CodeFactor code style cleanup: replace literal empty strings with `string.Empty` (#6950) (Thanks @iSazonov!) +- Remove dummy comments in Utility module files (#7224) (Thanks @iSazonov!) +- Use empty array for Functions/Cmdlets/`AliasesToExport` to follow the best practice (#7108) +- Refactor module code related to `Get-Module -ListAvailable` (#7145) +- Refactor module specification logic (#7126) + +### Test + +- Add tests for module specifications (#7140) +- Update test string for better clarity in `Send-MailMessage.Tests.ps1` (#7195) (Thanks @sethvs!) +- Add test to verify filesystem provider isn't used when accessing root path in `PSDrive` (#7173) +- Fix to address `ThreadJob` tests reliability and speed (#7270) +- Add additional checks for test that passes inconsistently (#7051) + +### Build and Packaging Improvements + +- `install-powershell.sh` filter pre-releases (when available), `params` documentation (#6849) (Thanks @DarwinJS!) +- Fedora 28 was released, Fedora 26 and 25 went end of life. (#7079) (Thanks @adelton!) +- Disambiguate icon on Windows for preview builds/installers to use `Powershell_av_colors` and + make daily build use `Powershell_avatar` instead (#7086) (Thanks @bergmeister!) +- Update to build for Alpine (#7139) +- Update build and packaging modules for Alpine (#7149) +- Add ability to install previews side-by-side with production releases (#7194) (Thanks @DarwinJS!) +- Enable NuGet Package Registration for compliance (#7053) +- Fix the preview macOS package link (#7061) +- Remove PSReadLine from then `PowerShell.sln` file (#7137) +- Fix the file `PowerShell.sln` that was corrupted by accident (#7288) +- Fix the encoding of `PowerShell.sln` to be `utf-8` (#7289) +- Make sure all references to the Package ID for previews packages is powershell-preview (#7066) +- Update `internals.md` with the latest build changes (#7058) +- When installing using MSI, set the working directory of the shortcut to the user home directory (#7072) +- Move to dotnet core 2.1.1 (#7161) (Thanks @iSazonov!) +- Update to latest package references, runtime framework, and SDK (#7272) +- AppVeyor build matrix: more efficient build job split to reduce total time by another 5 minutes (#7021) (Thanks @bergmeister!) +- Build: Fix the source location of `PowerShell.Core.Instrumentation.dll` (#7226) +- Add Andrew to the default reviewers of the build related files (#7019) +- Build: Fix a check to avoid null argument in case `vcvarsall.bat` is absent (#7218) (Thanks @PetSerAl!) +- Update `releaseTag` in `tools/metadata.json` (#7214) +- Update `Start-PSPester` to make it more user friendly (#7210) (Thanks @bergmeister!) +- Make `Start-PSBuild -Clean` not prompt due to locked files when Visual Studio is open by excluding `sqlite3` folder and use `-x` instead of `-X` option on `git clean` (#7235) (Thanks @bergmeister!) + +### Documentation and Help Content + +- Fix typos in `DOCSMIGRATION.md` (#7094) (Thanks @alexandair!) +- Add instructions to update Homebrew formula for the preview version PowerShell (#7067) (Thanks @vors!) +- Merge Third Party Notices and License updates (#7203) +- Update third party notices (#7042) +- Fix Markdown and spelling errors in `CHANGELOG.md` (#7064) +- Fix `New-TemporaryFile` online help URI (#6608) +- Fix links to PowerShell install docs (#7001) (Thanks @jokajak!) +- Update links that contain `en-us` culture (#7013) (Thanks @bergmeister!) +- Update docs for `ArgumentCompleterAttribute` class (#7227) (Thanks @Meir017!) +- Fix the name of a `Register-EngineEvent` test (#7222) (Thanks @alexjordan6!) +- Update README files for native code for migration (#7248) +- Comment about dynamic members for the `DotNetAdapter`, `GetMember` and `GetMembers` (#7087) +- Update the PowerShell executable location in building guide docs (#7205) (Thanks @louistio!) + +# [6.1.0-preview.3]- 2018-06-07 + +### Breaking Changes + +- Clean up uses of `CommandTypes.Workflow` and `WorkflowInfo` (#6708) +- Disallow Basic Auth over HTTP in PowerShell Remoting on Unix (#6787) +- Change packaging to differentiate only between major versions and previews (#6968) +- Enhance and refactor `Add-Type` cmdlet (#6141) (Thanks @iSazonov!) + - A few error strings were removed and thus the corresponding fully qualified error ids are no longer in use. + +### Engine Updates and Fixes + +- Fix crash when terminal is reset (#6777) +- Fix a module-loading regression that caused an infinite loop (#6843) +- Further improve `PSMethod` to `Delegate` conversion (#6851) +- Block list `System.Windows.Forms` from loading to prevent a crash (#6822) +- Fix `Format-Table` where rows were being trimmed unnecessarily if there's only one row of headers (#6772) +- Fix `SetDate` function in `libpsl-native` to avoid corrupting memory during `P/Invoke` (#6881) +- Fix tab completions for hash table (#6839) (Thanks @iSazonov!) +- Fix parser to continue parsing key-value pairs after an `If-Statement` value in a `HashExpression` (#7002) +- Add error handling for `#requires` in an interactive session (#6469) + +### General Cmdlet Updates and Fixes + +- Improve parameter validation in `ExportCsvHelper` (#6816) (Thanks @sethvs!) +- Quote `Multipart` form-data field names (#6782) (Thanks @markekraus!) +- Fix Web Cmdlets for .NET Core 2.1 (#6806) (Thanks @markekraus!) +- Fix `Set-Location DriveName:` to restore current working directory in the drive (#6774) (Thanks @mcbobke!) +- Add the alias `-lp` for `-LiteralPath` parameters #6732 (#6770) (Thanks @kvprasoon!) +- Remove `more` function and move the `$env:PAGER` capability into the `help` function (#6059) (Thanks @iSazonov!) +- Add line break to the error message for `Set-ExecutionPolicy` (#6803) (Thanks @wesholton84!) + +### Code Cleanup + +- Clean up `#if SILVERLIGHT` (#6907) (Thanks @iSazonov!) +- Clean up the unused method `NonWindowsGetDomainName()` (#6948) (Thanks @iSazonov!) +- Clean up FileSystem provider (#6909) (Thanks @iSazonov!) + +### Test + +- Add tests for PowerShell hosting API to verify MyGet packages (#6737) +- Remove Web Cmdlets tests using proxy environment variables (#6808) (Thanks @iSazonov!) +- Enable Web Cmdlets tests for greater platform support (#6836) (Thanks @markekraus!) +- Convert `ShouldBeErrorId` to `Should -Throw -ErrorId` in PowerShell tests (#6682) +- Fix CIM cmdlets tests (#6755) (Thanks @sethvs!) +- Add tests for PowerShell classes inheriting from abstract .NET classes (#6752) +- Fix `Select-Object.Tests.ps1` which previously failed intermittently on Unix platforms. (#6747) +- Update docker package tests to fix error on OpenSUSE 42 (#6783) +- Fix test and infrastructure that block code coverage runs (#6790) +- Update Tests `Isfile` to correct response for `"/"` (#6754) (Thanks @Patochun!) +- Improve code coverage in `Export-Csv.Tests.ps1` (#6795) (Thanks @sethvs!) +- Change `-Quiet` parameter of `Invoke-Pester` to `-Show None` in `OpenCover.psm1` (#6798) (Thanks @sethvs!) +- Replace `Dbg.Assert` with `if () throw` in `CSVCommands.cs` (#6910) (Thanks @sethvs!) +- Fix xUnit test `GetTempFileName` (#6943) (Thanks @iSazonov!) + +### Build and Packaging Improvements + +- Add Windows Compatibility Pack 2.0.0 to PowerShell Core and adopt the official .NET Core 2.1 (#6958) +- Add Jumplist 'Run as Administrator' to Taskbar on Windows (#6913, #6985) (Thanks @bergmeister!) +- Use AppVeyor matrix for faster Pull Request builds (#6945) (Thanks @bergmeister!) +- Fix `build.psm1` to not add tool path to $PATH twice (#6834) +- Add script to create a container manifest (#6735) +- Fix docker manifest creation script to work with more complex tags and with repeated use (#6852) +- Add functions to merge Pester and xUnit logs (#6854) +- Enable generating full symbols for the Windows debug build (#6853) +- Add functions into `build.psm1` to save and restore `PSOptions` between different sessions. (#6884) +- Update signing XML based on new signing guidelines (#6893) +- Update the release docker files to allow specifying the version of to-be-installed PowerShell and the version of image to use (#6835) +- Updates docker files for Fedora 27 and Kali Linux (#6819) +- Change packaging to support Ubuntu 17.10 and 18.04 (#6769) +- Update `Get-ChangeLog` to make it more accurate (#6764) +- Fix comparison to see if sudo test is needed in `install-*.sh` (#6771) (Thanks @bjh7242!) +- Packaging: Add registry keys to support library folder background for explorer context menu (#6784) (Thanks @bergmeister!) +- Skip `dotnet-cli` initialization and stop caching the `dotnet` folder for Travis CI (#7007) +- Skip compiling the non-supported cmdlets on Unix in `System.Management.Automation.dll` to fix the crash in Unix debug build (#6939) +- Use `PSReadLine` 2.0.0-beta2 from PSGallery (#6998) +- Update `PSRP` Linux NuGet package version to 1.4.2-* (#6711) +- Add path cleanup utility `Reset-PWSHSystemPath.ps1` (#6892) (Thanks @DarwinJS!) +- Add logic to create signing XML for NuGet packages (#6921) +- Add and config the `Settings.StyleCop` file (#6930, #6986) (Thanks @iSazonov!) +- Fix the double curly bracket typo in a docker file (#6960) (Thanks @adelton!) +- Remove dependencies on `libcurl` and `libunwind` in packaging to match the .NET Core behavior (#6964) (Thanks @qmfrederik!) +- Make the docker build fail when the curl operation fails. (#6961) (Thanks @adelton!) + +### Documentation and Help Content + +- Update installation doc about Raspbian (#6859) +- Add code coverage report generation instructions (#6515) +- Migrate docs from PowerShell repository to Docs repository (#6899) +- Fix broken links due to migrating GitHub docs on Installation, Known Issues and Breaking Changes to `docs.microsoft.com` (#6981) (Thanks @bergmeister!) +- Update documentation on how to write tests verifying errors conditions (#6687) +- Fix preview download links in `README.md` (#6762) + +# [6.1.0-preview.2]- 2018-04-27 + +### Breaking Changes + +- Remove support for file to opt-out of telemetry, only support environment variable (#6601) +- Simplify the installation paths the MSI uses (#6442) + +### Engine Updates and Fixes + +- Fix running `pwsh` produced from `dotnet build` (#6549) +- Remove the `FullCLR-only` symbol-info related code from `EventManager.cs` (#6563) +- Improve `PSMethod-to-Delegate` conversion (#6570) +- Fix `PsUtils.GetManModule()` to avoid infinite loop when there was no main module (#6358) +- Fix error in windows environment provider when the environment variable has duplicates that differ only by case (#6489) (Thanks @mklement0!) +- Make sure that the width of the header is at least the size of the label (or property name) (#6487) +- Enable `[Environment]::OSVersion` to return current OS rather than compatible version (#6457) +- Change the `SaveError` method in Parser to use `nameof` for error ids (#6498) +- Fix error when `Format-Wide -AutoSize | Out-String` is called (#6491) (Thanks @stknohg!) +- Make `LanguagePrimitive.GetEnumerable` treat `DataTable` as Enumerable (#6511) +- Fix formatting of tables where headers span multiple rows (#6504) +- Improve performance of parsing `RegexOption` for `-split` by using `if` branches (#6605) (Thanks @iSazonov!) +- Enable specifying `sshd` subsystem to use via `-Subsystem` (#6603) +- Add some optimizations in formatting subsystem (#6678) (Thanks @iSazonov!) +- Throw better parsing error when statements should be put in named block (#6434) +- Use `Unregister-Event` to remove an event subscriber when removing `PSEdit` function (#6449) +- Make the `PSISERemoteSessionOpenFile` a support event (#6582) +- Add `-WorkingDirectory` parameter to `pwsh` (#6612) +- Support importing module paths that end in trailing directory separator (#6602) +- Formatting: Use cache for dash padding strings for tables (#6625) (Thanks @iSazonov!) +- Port Windows PowerShell AppLocker and DeviceGuard `UMCI` application white listing support (#6133) +- Reduce allocations in `TableWriter` (#6648) (Thanks @iSazonov!) + +### General Cmdlet Updates and Fixes + +- Add `-Resume` Feature to WebCmdlets (#6447) (Thanks @markekraus!) +- Support `user@host:port` syntax for `SSH` transport (#6558) +- Add ported `Test-Connection` cmdlet (#5328) (Thanks @iSazonov!) +- Added line break to Access-Denied error message (#6607) +- Some fixes in `Get-Date -UFormat` (#6542) (Thanks @iSazonov!) +- Added check for existence of Location HTTP header before using it (#6560) (Thanks @ffeldhaus!) +- Enable `Update-Help` to save help content in user scope by default (#6352) +- Update `Enable-PSRemoting` to create PowerShell.6 endpoint and version specific endpoint (#6519, #6630) +- Update error message that `Disconnect-PSSession` is only supported with `WSMan` (#6689) +- Make `Export-FormatData` print pretty XML output (#6691) (Thanks @iSazonov!) +- Add `-AsArray` parameter to `ConvertoTo-Json` command (#6438) +- Add `Test-Json` cmdlet (`NJsonSchema`) (#5229) (Thanks @iSazonov!) +- Correct a typo in comment for `Invoke-WebRequest` (#6700) (Thanks @gabrielsroka!) +- Re-order `UFormat` options in `Get-Date` (#6627) (Thanks @iSazonov!) +- Add the parameter `-Not` to `Where-Object` (#6464) (Thanks @SimonWahlin!) + +### Code Cleanup + +- Engine: Fix several code cleanup issues (#6552, #6609) +- Clean up workflow logic in the module loading component (#6523) +- Engine: Clean up unneeded `GetTypeInfo()` calls (#6613, #6636, #6633, #6635, #6634) + +### Test + +- Fix line ending in `DefaultCommands.Tests.ps1` from `CRLF` to `LF` (#6553) +- Use new Pester parameter syntax in tests (#6490, #6574, #6535, #6536, #6488, #6366, #6351, #6349, #6256, #6250) (Thanks @KevinMarquette, @sethvs, @bergmeister!) +- Fix `Copy.Item.Tests.ps1` (#6596) (Thanks @sethvs!) +- Fix typos or formatting in some test files (#6595, #6593, #6594, #6592, #6591) (Thanks @sethvs!) +- Add missing `Start-WebListener` to WebCmdlets tests (#6604) (Thanks @markekraus!) +- Update Dockerfile test to use Ubuntu 17.10 as the base image (#6503) +- Add PowerShell logging tests for macOS and Linux (#6025) +- Add tests for `Format-Table -Wrap` (#6670) (Thanks @iSazonov!) +- Reformat `Format-Table` tests (#6657) (Thanks @iSazonov!) +- Add new reliable tests for `Get-Date -UFormat` (#6614) (Thanks @iSazonov!) + +### Build and Packaging Improvements + +- Use C# latest language in `.csproj` files (#6559) (Thanks @iSazonov!) +- Update `installpsh-.sh` installers to handle "preview" in version number (#6573) (Thanks @DarwinJS!) +- Enable `PowerShell.sln` to work in VisualStudio (#6546) +- Remove duplicate `Restore-PSPackage` (#6544) +- Use `-WorkingDirectory` parameter to handle context menu when path contains single quotes (#6660) (Thanks @bergmeister!) +- Make `-CI` not depend on `-PSModuleRestore` in `Start-PSBuild` (#6450) +- Restore for official Linux arm builds (#6455) +- Fix error about setting readonly variable in `install-powershell.sh` (#6617) +- Make release macOS build work better (#6619, #6610) +- MSI: add function to generate a `MSP` package (#6445) + +### Documentation and Help Content + +- Doc: Update Ubuntu source creation commands to use `curl -o` (#6510) (Thanks @M-D-M!) +- Update stale bot message (#6462) (Thanks @iSazonov!) +- Remove extraneous SSH and install docs from the 'demos' folder (#6628) + +# [6.1.0-preview.1]- 2018-03-23 + +### Breaking Changes + +- Throw terminating error in `New-TemporaryFile` and make it not rely on the presence of the `TEMP` environment variable (#6182) (Thanks @bergmeister!) +- Remove the unnecessary `AddTypeCommandBase` class from `Add-Type` (#5407) (Thanks @iSazonov!) +- Remove unsupported members from the enum `Language` in `Add-Type` (#5829) (Thanks @iSazonov!) +- Fix range operator to work better with character ranges (#5732) (Thanks @iSazonov!) + +### Engine Updates and Fixes + +- Fix `ValidateSet` with generator in a module (#5702) +- Update `SAL` annotation and fix warnings (#5617) +- Add `ForEach` and `Where` methods to `PSCustomobject` (#5756) (Thanks @iSazonov!) +- Add `Count` and `Length` properties to `PSCustomobject` (#5745) (Thanks @iSazonov!) +- Make minor fixes in compiler to properly handle void type expression (#5764) +- Logging: Fix the escaped characters when generating `.resx` file from PowerShell `ETW` manifest. (#5892) +- Remove `PSv2` only code from `Types_Ps1Xml.cs` and `HostUtilities.cs` (#5907) (Thanks @iSazonov!) +- Enable passing arrays to `pwsh -EncodedArguments` on debug builds. (#5836) +- Logging: Handle path that contains spaces in `RegisterManifest.ps1` (#5859) (Thanks @tandasat!) +- Add `-settingsfile` to `pwsh` to support loading a custom powershell config file. (#5920) +- Return better error for `pwsh -WindowStyle` on unsupported platforms. (#5975) (Thanks @thezim!) +- Enable conversions from `PSMethod` to `Delegate` (#5287) (Thanks @powercode!) +- Minor code clean-up changes in tab completion code (#5737) (Thanks @kwkam!) +- Add lambda support to `-replace` operator (#6029) (Thanks @IISResetMe!) +- Fix retrieval of environment variables on Windows in cases where variable names differ only by case. (#6320) +- Fix the `NullRefException` when using `-PipelineVariable` with `DynamicParam` block (#6433) +- Add `NullReference` checks to two code paths related to `PseudoParameterBinder` (#5738) (Thanks @kwkam!) +- Fix `PropertyOnlyAdapter` to allow calling base methods (#6394) +- Improve table view for `Certs` and `Signatures` by adding `EnhancedKeyUsageList` and `StatusMessage` (#6123) +- Fix the filtering of analytic events on Unix platforms. (#6086) +- Update copyright and license headers (#6134) +- Set pipeline thread stack size to 10MB (#6224) (Thanks @iSazonov!) + +### General Cmdlet Updates and Fixes + +- Fix the `NullRefException` in `Enter-PSHostProcess` (#5995) +- Merge and Sort `BasicHtmlWebResponseObject` and `ContentHelper` in Web Cmdlets (#5720) (Thanks @markekraus!) +- Encoding for `New-ModuleManifest` on all platforms should be `UTF-8 NoBOM` (#5923) +- Make `Set-Location` use path with wildcard characters as literal if it exists (#5839) +- Combine Web Cmdlets partial class files (#5612) (Thanks @markekraus!) +- Change `Microsoft.PowerShell.Commands.SetDateCommand.SystemTime` to `struct`. (#6006) (Thanks @stknohg!) +- Add Simplified `multipart/form-data` support to Web Cmdlets through `-Form` parameter (#5972) (Thanks @markekraus!) +- Make a relative redirect URI absolute when `Authorization` header present (#6325) (Thanks @markekraus!) +- Make relation-link handling in Web Cmdlets case-insensitive (#6338) +- Make `Get-ChildItem -LiteralPath` accept `Include` or `Exclude` filter (#5462) +- Stop `ConvertTo-Json` when `Ctrl+c` is hit (#6392) +- Make `Resolve-Path -Relative` return useful path when `$PWD` and `-Path` is on different drive (#5740) (Thanks @kwkam!) +- Correct the `%c`, `%l`, `%k`, `%s` and `%j` formats in `Get-Date -UFormat` (#4805) (Thanks @iSazonov!) +- Add standard deviation implementation on `Measure-Object` (#6238) (Thanks @CloudyDino!) +- Make `Get-ChildItem /* -file` include `` as search directory (#5431) +- Enable setting `PSSession` Name when using `SSHTransport` and add `Transport` property (#5954) +- Add `Path` alias to `-FilePath` parameters and others for several commands (#5817) (Thanks @KevinMarquette!) +- Add the parameter `-Password` to `Get-PfxCertificate` (#6113) (Thanks @maybe-hello-world!) +- Don't add trailing spaces to last column when using `Format-Table` (#5568) +- Fix table alignment and padding. (#6230) +- Add `-SkipHeaderValidation` Support to `ContentType` on Web Cmdlets (#6018) (Thanks @markekraus!) +- Add common aliases for all `write-*` commands default message parameter (#5816) (Thanks @KevinMarquette!) +- Make `UTF-8` the default encoding for `application/json` (#6109) (Thanks @markekraus!) +- Enable `$env:PAGER` to work correctly if arguments are used (#6144) + +### Test + +- Convert Web Cmdlets test to `one-true-brace-style` formatting (#5716) (Thanks @markekraus!) +- Add a test for `IValidateSetValuesGenerator` in a module (#5830) (Thanks @iSazonov!) +- Fix function to test for docker OS due to change to use `linuxkit` for macOS (#5843) +- Replace `HttpListener` tests with `WebListener` (#5806, #5840, #5872) (Thanks @markekraus!) +- Stop `HttpListener` from running in Web Cmdlets tests (#5921) (Thanks @markekraus!) +- Fix `PSVersion` in `PSSessionConfiguration` tests (#5554) (Thanks @iSazonov!) +- Update test framework to support Pester v4 (#6064) +- Update tests to use Pester v4 Syntax. (#6294, #6257, #6306, #6304, #6298) +- Add negative tests for `Copy-Item` over remote sessions (#6231) +- Markdown test: Use strict in JavaScript (#6328) +- Add tests for `Get-Process` about the `-Module` and `-FileVersion` parameters (#6272) +- Add test for the `OsLocalDateTime` property of `Get-ComputerInfo`. (#6253) +- Change `Get-FileHash` tests to use raw bytes (#6430) +- Remove `runas.exe` from tests as we have tags to control this behavior (#6432) +- Refactor the `Get-Content` tests to use `-TestCases`. (#6082) +- Use `RequireAdminOnWindows` tag in `Set-Date` tests (#6034) (Thanks @stknohg!) +- Remove `-TimeOutSec` from non timeout related tests (#6055) (Thanks @markekraus!) +- Add verbosity and more accurate timeout implementation for `Start-WebListener` (#6013) (Thanks @markekraus!) +- Skip tests that use `ExecutionPolicy` cmdlets on Unix (#6021) +- Change Web Cmdlet tests to use `127.0.0.1` instead of `Localhost` (#6069) (Thanks @markekraus!) +- Fix `Start-PSPester` to include or exclude `RequireSudoOnUnix` tag smartly on Unix (#6241) +- Fix the terse output on Windows for test runs without admin privilege (#6252) +- Add `RequireSudoOnUnix` tag for `Get-Help` tests. (#6223) +- Add tests for `*-Item` Cmdlets in function provider (#6172) +- Support running tests in root privilege on Linux. (#6145) + +### Build and Packaging Improvements + +- Add option to add explorer shell context menu in Windows installer (#5774) (Thanks @bergmeister!) +- Make the explorer shell context menu registry entries platform specific to allow side by side of `x86` and `x64`. (#5824) (Thanks @bergmeister!) +- Fix start menu folder clash of shortcut when `x86` and `x64` are both installed by appending ` (x86)` for `x86` installation. (#5826) (Thanks @bergmeister!) +- Reduce image file sizes using lossless compression with `imgbot` (#5808) (Thanks @bergmeister!) +- Windows installer: Allow `Launch PowerShell` checkbox to be toggled using the space bar. (#5792) (Thanks @bergmeister!) +- Fix release packaging build (#6459) +- Fail `AppVeyor` Build if `MSI` does not build (#5755) (Thanks @bergmeister!) +- Cleanup temporarily created `WiX` files after compilation to be able to have a clean re-build (#5757) (Thanks @bergmeister!) +- Fix `install-powershell.ps1` for running during window setup (#5727) +- Start using `Travis-CI` cache (#6003) +- Fix build, packaging and installation scripts for `SLES` (#5918) (Thanks @tomconte!) +- Update recommended `WiX` toolset link to be generic to `WiX 3.x` but mention that latest version of 3.11 has to be taken (#5926) (Thanks @bergmeister!) +- Add service point manager call in `Install-PowerShell.ps1` to force `TLS1.2`. (#6310) (Thanks @DarqueWarrior!) +- Add `-Restore` when build `win-arm` and `win-arm64` (#6353) +- Make sure package verification failure fails the `AppVeyor` build (#6337) +- Specify the runtime when running `dotnet restore` in `Start-PSBuild` (#6345) +- Rename `log` and `logerror` to `Write-Log [$message] [-error]` (#6333) +- Make Linux packages use correct version scheme for preview releases (#6318) +- Add support for Debian in `installpsh-debian.sh` (#6314) (Thanks @Pawamoy!) +- MSI: Make preview builds to install Side by side with release builds (#6301) +- Add `TLS1.2` workaround for code coverage script (#6299) +- Cleanup after Powershell install for `CentOS` and `Fedora` Docker images (#6264) (Thanks @strawgate!) +- MSI: Update the environment variable PATH with proper value (#6441) +- MSI: Remove the version from the product name (#6415) +- Support non-GitHub commits in the change log generation script (#6389) +- Fix secret and JavaScript compliance issues (#6408) +- Remove `AppVeyor` specific cmdlet from `Start-NativeExecution` (#6263) +- Restore modules from the `NuGet` package cache by using `dotnet restore` (#6111) +- CI Build: Use `TRAVIS_PULL_REQUEST_SHA` to accurately get the commit message (#6024) +- Use `TLS1.2` on Windows during `Start-PSBootstrap` (#6235) (Thanks @CallmeJoeBob!) +- Use `TLS1.2` in `Start-PSBootStrap` without breaking `HTTPS` (#6236) (Thanks @markekraus!) +- Add options to enable `PSRemoting` and register Windows Event Logging Manifest to MSI installer (#5999) (Thanks @bergmeister!) + +### Documentation and Help Content + +- Separate macOS from Linux install instructions. (#5823) (Thanks @thezim!) +- Show usage (short) help if command line parameter is wrong (#5780) (Thanks @iSazonov!) +- Add the breaking changes doc for 6.0.0 release. (#5620) (Thanks @maertendMSFT!) +- Remove DockerFile for Fedora 25 and add DockerFile for Fedora 27 (#5984) (Thanks @seemethere!) +- Add a missing step to prepare the build environment on Mac. (#5901) (Thanks @zackJKnight!) +- Update `BREAKINGCHANGES.md` to include WebCmdlets breaking changes (#5852) (Thanks @markekraus!) +- Fix typos in `BREAKINGCHANGES.md` (#5913) (Thanks @brianbunke!) +- Update `macos.md` to use `brew cask upgrade` for upgrading powershell (#5875) (Thanks @timothywlewis!) +- Add verification step to macOS install docs (#5860) (Thanks @rpalo!) +- Fix links in macOS install docs (#5861) (Thanks @kanjibates!) +- Update docs with test guidelines with the `RequireSudoOnUnix` tag. (#6274) +- Add `Alpine` Linux support (#6367) (Thanks @kasper3!) +- Update to Governance doc to reflect current working model (#6323) +- Add guidance on adding copyright and license header to new source files (#6140) +- Fix the command to build type catalog in `internals.md` (#6084) (Thanks @ppadmavilasom!) +- Fix `Pull Request Process` dead link (#6066) (Thanks @IISResetMe!) +- Update processes to allow for coordinated vulnerability disclosure (#6042) +- Rework Windows Start menu folder name (#5891) (Thanks @Stanzilla!) +- Update `Raspbian` installation instructions to create `symlink` for `pwsh` (#6122) +- Fix various places that still refer to old versions of `pwsh` (#6179) (Thanks @bergmeister!) +- Correct a Linux installation typo (#6219) (Thanks @mababio!) +- Change synopsis of `install-powershell.ps1` to reflect that it works cross-platform (#5465) (Thanks @bergmeister!) + +## [6.0.2] - 2018-03-15 + +### Engine updates and fixes + +- Update PowerShell to use `2.0.6` dotnet core runtime and packages (#6403) + - This change addresses this vulnerability: [Microsoft Security Advisory `CVE-2018-0875`: Hash Collision can cause Denial of Service](https://github.com/PowerShell/Announcements/issues/4) + +### Build and Packaging Improvements + +- Add Ubuntu build without `AppImage` (#6380) +- Add scripts to set and or update the release tag in `VSTS` (#6107) +- Fix `DSC` Configuration compilation (#6225) +- Fix errors in `Start-PSBootStrap` during release builds (#6159) +- Fix spelling failures in `CI` (#6191) +- Use PowerShell `windowsservercore` Docker image for release builds (#6226) +- Use `ADD` instead of `Invoke-WebRequest` in `nanoserver` Docker file (#6255) +- When doing daily/test build in a non-release branch use the branch name as the preview name (#6355) +- Add Environment Variable override of telemetry (#6063) (Thanks @diddledan!) +- Build: Remove two unneeded lines from `Invoke-AppveyorFinish` (#6344) +- MSI: Refactor `New-MsiPackage` into `packaging.psm1` + and various fixes to enable patching + (#5871, #6221, #6254, #6303, #6356, #6208, #6334, #6379, #6094, #6192) +- MSI: Use `HKLM` instead of `HKCU` registry keys since the current installation scope is per-machine. (#5915) (Thanks @bergmeister!) + +## [6.0.1] - 2018-01-25 + +### Engine updates and fixes + +- Update PowerShell to use `2.0.5` dotnet core runtime and packages. (#5903, #5961) (Thanks @iSazonov!) + +### Build and Packaging Improvements + +- Re-release of `v6.0.0` as `v6.0.1` due to issues upgrading from pre-release versions + +### Test + +- Update regular expression to validate `GitCommitId` in `$PSVersionTable` to not require a pre-release tag (#5893) + +[6.1.6]: https://github.com/PowerShell/PowerShell/compare/v6.1.5...v6.1.6 +[6.1.5]: https://github.com/PowerShell/PowerShell/compare/v6.1.4...v6.1.5 +[6.1.4]: https://github.com/PowerShell/PowerShell/compare/v6.1.3...v6.1.4 +[6.1.3]: https://github.com/PowerShell/PowerShell/compare/v6.1.2...v6.1.3 +[6.1.2]: https://github.com/PowerShell/PowerShell/compare/v6.1.1...v6.1.2 +[6.1.1]: https://github.com/PowerShell/PowerShell/compare/v6.1.0...v6.1.1 +[6.1.0]: https://github.com/PowerShell/PowerShell/compare/v6.1.0-rc.1...v6.1.0 +[6.1.0-rc.1]: https://github.com/PowerShell/PowerShell/compare/v6.1.0-preview.4...v6.1.0-rc.1 +[6.1.0-preview.4]: https://github.com/PowerShell/PowerShell/compare/v6.1.0-preview.3...v6.1.0-preview.4 +[6.1.0-preview.3]: https://github.com/PowerShell/PowerShell/compare/v6.1.0-preview.2...v6.1.0-preview.3 +[6.1.0-preview.2]: https://github.com/PowerShell/PowerShell/compare/v6.1.0-preview.1...v6.1.0-preview.2 +[6.1.0-preview.1]: https://github.com/PowerShell/PowerShell/compare/v6.0.2...v6.1.0-preview.1 +[6.0.2]: https://github.com/PowerShell/PowerShell/compare/v6.0.1...v6.0.2 +[6.0.1]: https://github.com/PowerShell/PowerShell/compare/v6.0.0...v6.0.1 diff --git a/CHANGELOG/6.2.md b/CHANGELOG/6.2.md new file mode 100644 index 00000000000..bf54f978eba --- /dev/null +++ b/CHANGELOG/6.2.md @@ -0,0 +1,875 @@ +# 6.2 Changelog + +## [6.2.7] - 2020-07-16 + +### Build and Packaging Improvements + +
+ +
    +
  • Fix Azure file copy issues in release build by fixing the path to upload directory content (#13182)
  • +
  • Update .NET Core to version 2.1.808 (Internal 12003)
  • +
  • Fix Azure file copy break in AzDevOps by updating task version to latest (#13173)
  • +
+ +
+ +## [6.2.6] - 2020-06-11 + +### Engine Updates and Fixes + +- Restrict loading of `amsi.dll` to `system32` folder (#12730) + +### Tools + +- Update the PowerShell team list to correct changelog generation (#12927) + +### Tests + +- Pin major Pester version to 4 to prevent breaking changes caused by upcoming release of `v5` (#12262) (Thanks @bergmeister!) + +### Build and Packaging Improvements + +
+ + + +

Update to `.NET Core 2.1.807`

+ +
+ +
    +
  • update to dotnet 2.1.807 (Internal 11697)
  • +
  • update hosting tests
  • +
  • Check if Azure Blob exists before overwriting (#12921)
  • +
  • Upgrade APIScan version (#12876)
  • +
  • Fix break in package build by pinning ffi version to 1.12 (#12889)
  • +
  • Update the build to sign any unsigned files as 3rd party Dlls (#12581)
  • +
+ +
+ +## [6.2.5] - 2020-05-14 + +### Build and Packaging Improvements + +
+ +
    +
  • Port back the code for new changelog format.
  • +
  • Work around FPM issue with a specific version on macOS
  • +
  • Update the combined package build to release the daily builds (#10449)
  • +
  • Refactor packaging pipeline (#11852)
  • +
  • Bump .NET SDK version to the version 2.1.18
  • +
  • Move to standard internal pool for building (#12119)
  • +
+ +
+ +## [6.2.4] - 2020-01-27 + +### General Cmdlet Updates and Fixes + +- Enable `Start-Process` to work on Windows 7 (#10417) (Thanks @iSazonov!) +- Fix global tool issues around exit code, command line parameters, and paths with spaces (#10461) +- Make `Add-Type` usable in applications that host PowerShell (#10587) + +### Build and Packaging Improvements + +- Update to use `TSAv2` (#9914) +- Update the dotnet SDK install script URL in `build.psm1` (#10927) +- Update dependencies needed by Azure PowerShell and patch for `Newtonsoft.Json` (Internal 10798) +- Fix path for getting reference assemblies (Internal 10792) + +## [6.2.3] - 2019-09-12 + +### Engine Updates and Fixes + +- Fix debugger performance regression in system lock down mode (#10269) + +### Tests + +- Remove `markdownlint` tests due to security issues (#10163) + +### Build and Packaging Improvements + +- Update DotNet SDK and runtime framework version (Internal 9946) +- Fix macOS build break (#10207) + +## [6.2.2] - 2019-07-16 + +### Breaking Changes + +- Disable `Enter-PSHostProcess` cmdlet when system in lock down mode (Internal 8969) + +### Engine Updates and Fixes + +- Create `JumpList` in STA thread as some COM APIs are strictly STA only to avoid sporadic CLR crashes (#10057, #9928) (Thanks @bergmeister!) + +### Build and Packaging Improvements + +- Update DotNet SDK and runtime framework version (Internal 9082, 9088, 9092) +- Make `Hashtable` case insensitivity test use current culture rather than shell to set culture (Internal 8529) +- Add automated RPM signing to release build (#10013) +- Update copyright symbol for NuGet packages (#9936) +- Bump `Microsoft.ApplicationInsights` from `2.9.1` to `2.10.0` (#9757) +- Switch from BMP to PNG for graphical MSI installer assets (#9606) +- Bump `System.Net.Http.WinHttpHandler` from `4.5.3` to `4.5.4` (#9789) +- Enable building of MSIX package (#9289, #9715) + +## [6.2.1] - 2019-05-21 + +### Engine Updates and Fixes + +- Re-enable tab completion for functions (#9383) +- Disable debugger in System Lock down mode (Internal 8428) + +### Code Cleanup + +- Update repo for Ubuntu 14.04 EOL (#9324) + +### Tests + +- Fix skipping of tests in `RemoteSession.Basic.Tests.ps1` (#9304) +- Update tests to account for when `$PSHOME` is read only (#9279) +- Mark tests in macOS CI which use `applescript` as pending/inconclusive (#9352) +- Make sure non-Windows CI fails when a test fails (#9303) + +### Build and Packaging Improvements + +- Partially revert "Fix the failed test and update `Publish-TestResults` to make `AzDO` fail the task when any tests failed (#9457)" +- Bump `Markdig.Signed` from `0.16.0` to `0.17.0` (#9595) +- Bump `Microsoft.PowerShell.Archive` from `1.2.2.0` to `1.2.3.0` in `/src/Modules` (#9594) +- Enable building on Kali Linux (#9471) +- Fix the failed test and update `Publish-TestResults` to make `AzDO` fail the task when any tests failed (#9457) +- Add Preview assets for `msix` (#9375) +- Create code coverage and test packages for non-windows (#9373) +- Fix publishing daily `nupkg` to MyGet (#9269) +- Bump `PackageManagement` from `1.3.1` to `1.3.2` in `/src/Modules` (#9568) +- Bump `NJsonSchema` from `9.13.27` to `9.13.37` (#9524) +- Bump `gulp` from `4.0.0` to `4.0.2` in `/test/common/markdown` (#9443) +- Bump `Newtonsoft.Json` from `12.0.1` to `12.0.2` (#9433) +- Bump `System.Net.Http.WinHttpHandler` from `4.5.2` to `4.5.3` (#9367) +- Add `AccessToken` variable to jobs that perform signing (#9351) +- Create test package for macOS on release builds (#9344) +- Add component detection to all jobs (#8964) +- Move artifacts to artifact staging directory before uploading (#9273) + +## [6.2.0] - 2019-03-28 + +### Breaking Changes + +- Fix `-NoEnumerate` behavior in `Write-Output` to be consistent with Windows PowerShell (#9069) (Thanks @vexx32!) + +### Engine Updates and Fixes + +- Add PowerShell remoting enable/disable cmdlet warning messages (#9203) +- Fix for `FormatTable` remote deserialization regression (#9116) +- Update the task-based `async` APIs added to PowerShell to return a Task object directly (#9079) +- Add 5 `InvokeAsync` overloads and `StopAsync` to the `PowerShell` type (#8056) (Thanks @KirkMunro!) + +### General Cmdlet Updates and Fixes + +- Enable `SecureString` cmdlets for non-Windows by storing the plain text (#9199) +- Add Obsolete message to `Send-MailMessage` (#9178) +- Fix `Restart-Computer` to work on `localhost` when WinRM is not present (#9160) +- Make `Start-Job` throw terminating error when PowerShell is being hosted (#9128) +- Update version for `PowerShell.Native` and hosting tests (#8983) + +### Tools + +- Adding `CmdletsToExport` and `AliasesToExport` to test module manifests. (#9108) (Thanks @powercode!) +- Comment cleanup in `releaseTools.psm1` (#9064) (Thanks @RDIL!) + +### Tests + +- Fix `Enter-PSHostProcess` tests flakiness (#9007) +- Add tests for command globbing (#9180) +- Add source for `Install-package` to install `netDumbster` (#9081) (Thanks @Geweldig!) +- Fix tab completion test to handle multiple matches (#8891) +- Refactor macOS and Linux CI so that tests run in parallel (#9056, #9209) +- Added `RequireSudoOnUnix` tags to `PowerShellGet` tests and remove `-pending` parameter (#8954) (Thanks @RDIL!) +- Pending `NamedPipeConnectionInfo` test (#9003) (Thanks @iSazonov!) +- Add test for `-WhatIf` for `New-FileCatalog` (#8966) (Thanks @mjanko5!) + +### Build and Packaging Improvements + +- Performance improvements for release build (#9179) +- Add `tsaVersion` property as `TsaV1` for compliance build phase (#9176) +- Publish global tool packages to `pwshtool` blob and bug fixes (#9163) +- Translate Skipped test results into something Azure DevOps does **not** understand (#9124) +- Disable Homebrew analytics in macOS VSTS builds (#9130) (Thanks @RDIL!) +- Remove AppVeyor references from packaging tools (#9117) (Thanks @RDIL!) +- Fixed Dockerfile syntax highlighting (#8991) (Thanks @RDIL!) +- Fix dependencies of NuGet build to wait on DEB uploads to finish (#9118) +- Fix artifact download issue in release build (#9095) +- Publish test package on release builds (#9063) +- Bump `Microsoft.PowerShell.Native` from `6.2.0-rc.1` to `6.2.0` (#9200) +- Bump `NJsonSchema` from `9.13.19` to `9.13.27` (#9044, #9136, #9166, #9172, #9184 #9196) +- Bump `PowerShellGet` from `2.0.4` to `2.1.2` in /src/Modules (#9110, #9145) +- Bump `SelfSignedCertificate` in `/test/tools/Modules` (#9055) + +### Documentation and Help Content + +- Update docs for `6.2.0-rc.1` release (#9022) + +## [6.2.0-rc.1] - 2019-03-05 + +### Breaking Changes + +- Make `Join-String -InputObject 1,2,3` result equal to `1,2,3 | Join-String` result (#8611) (Thanks @sethvs!) + +### Engine Updates and Fixes + +- Improve check for developer mode by checking minimum required build number (#8749) +- Simplify the declaration of new experimental features (#8726) +- Remove AMSI uninitialized assert and replace with call to uninitialized (#8713) +- Port Security bypass fixes from 6.1.3 (#8915) +- Enable discovering modules that have names same as a culture (e.g. `Az`) (#8777) +- Flatten interface hierarchy when generating properties that implement interface properties (#8382) (Thanks @IISResetMe!) +- Don't use Win32 native APIs on non-Windows for cryptography of secure string over remoting (#8746) +- Allow `.exe` files to be used as IL binary modules (#7281) +- Remove unused cached types (#9015) + +### Experimental Features + +- Add the experimental feature for creating `Temp:\` drive when `FileSystemProvider` initializes (#8696) +- Move `CommandNotFoundException` suggestion to an experimental feature (#8805) + +### General Cmdlet Updates and Fixes + +- Correctly Report impact level when `SupportsShouldProcess` is not set to 'true' (#8209) (Thanks @vexx32!) +- Fix Request Charset Issues in Web Cmdlets (#8742) (Thanks @markekraus!) +- Refactor `ConvertTo-Json` to expose `JsonObject.ConvertToJson` as a public API (#8682) +- Add `-CustomPipeName` to `pwsh` and `Enter-PSHostProcess` (#8889) +- Add configurable maximum depth in `ConvertFrom-Json` with `-Depth` (#8199) (Thanks @louistio!) +- Enable creating relative symbolic links on Windows with `New-Item` (#8783) +- Parse numeric strings as numbers again during conversions (#8681) (Thanks @vexx32!) +- Expose file attributes of `OneDrive` placeholders (#8745) (Thanks @sba923!) +- Enable `Write-Information` to accept `$null` (#8774) +- Adding parameter `ReplyTo` to `Send-MailMessage` (#8727) (Thanks @replicaJunction!) +- Fix `Get-Help` `PSTypeName` issue with `-Parameter` when only one parameter is declared (#8754) (Thanks @pougetat!) + +### Code Cleanup + +- Use HTTPS in URLs where available (#8622) (Thanks @xtqqczze!) +- Update code to use single method to check if path is UNC (#8680) +- Fix typo: `aganist` ➜ `against` (#8943) (Thanks @lupino3!) +- Use the `OperationCancellationException` to replace the `StoppingException` in `ConvertToJson` (#8920) +- Fix style issues in CSV cmdlets (#8894) (Thanks @iSazonov!) +- Fix `LGTM` issues (#8843) (Thanks @iSazonov!) +- Fix length check in `PSSnapinQualifiedName.GetInstance()` (#8837) (Thanks @hvitved!) +- Reduce string allocations when formatting file system objects. (#8831) (Thanks @powercode!) +- Fix many instances of CodeFactor style issue `A single-line comment must not be followed by a blank line` (#8825) (Thanks @RDIL!) +- Refactor `appveyor.psm1` to `ci.psm1` (#8733, #8854, #8709, #8756, #8867) (Thanks @RDIL!) +- Refactor `travis.ps1` into `ci.psm1` (#8822, #8888) (Thanks @RDIL!) +- Fix Markdown lint issues (#8929) +- Fix code-of-conduct linting (#8896) (Thanks @RDIL!) + +### Tools + +- Fix broken reference (#8753) (Thanks @RDIL!) +- Remove `GitKracken` files from `.gitignore` (#8743) (Thanks @RDIL!) +- Update path of `test\xUnit\xUnit.tests.csproj` in `PowerShell.sln` (#8730) (Thanks @markekraus!) +- Ignore files added by `SelfSignedCertificate` (#8728) (Thanks @markekraus!) +- Build Global tool for PowerShell and SDK container (#8984) +- Add Experimental Features to change log creation (#8827) +- Remove unneeded `Invoke-Expression` on unvalidated input (#8826) +- Update CLA pull request labeling info (#8820) (Thanks @RDIL!) +- Update some info in `md-link-checks` (#8757) (Thanks @RDIL!) + +### Tests + +- Fix `Enter-PSHostProcess` test to wait until runspace is ready before attempting to enter (#8725) +- Package validation tests updates (#8714) +- Make xUnit tests run sequentially to avoid race conditions caused by manipulating `powershell.config.json` in tests (#8945) +- Use verbatim string literals for paths (#8937) (Thanks @iSazonov!) +- Parallelize the Windows CI to enable us to run all tests all the time (#8868) +- Fixes for Scheduled release build (#8887) +- Remove references to uninitialized variable (#8849) +- Remove directory causing static analysis failure (#8812) +- Update Pester version to 4.4.4 (#8739) +- Change xUnit Runspace tests to run sequentially (#8796) +- Fix cleanup config files for the csharp xUnit tests (#8761) (Thanks @iSazonov!) +- Moved `fxdependent-dotnetsdk-latest/Dockerfile` (#8738) + +### Build and Packaging Improvements + +- Make every `csproj` files have its own folder (#8750) +- Update packaging script to build reference assembly targeting `netcoreapp2.1` and use actual `.csproj` files (#8729) +- Generate and deploy reference assembly for `Microsoft.PowerShell.Commands.Utility.dll` (#8716) +- Make test file result names unique (#8979) +- Add variable to control the version of the signing task we use (#8982) +- Publish test and code coverage artifacts for daily builds (#8955) +- Integrate building NuGet package in the coordinated build (#8947) +- Support release branches based on the forward slash separator (#8903) +- Port DotNet fixes from 6.1.3 (#8914) +- Start tracking release build information in an azure storage table (#8850) +- Make license a link in the MSI (#8846) +- Use `-ErrorAction Ignore` instead of `SilentlyContinue` with `Get-Command` in build.psm1 (#8832) +- Add `binskim` to coordinated build and increase timeout (#8834) +- Fix daily CI builds to publish tar package as artifacts (#8775) +- Add instrumentation for `Start-PSPackage` (#8811) +- Fix passing credential to the `SyncGalleryToAzArtifacts.psm1` script (#8808) +- Move Final artifacts from coordinated build to `finalResults` folder (#8806) +- Refactor coordinated release build (#8804) +- Add compliance to Coordinated build (#8798) +- Switch to 1.11 of FPM to fix FPM install issue (#8797) +- Update the coordinated build with framework dependent package for dotnet SDK (#8773) +- Fix MSI upgrade failure for preview builds (#9013) +- Build(deps): Bump `Microsoft.ApplicationInsights` from `2.8.1` to `2.9.1` (#8807,#8848) +- Build(deps): Bump `Microsoft.PowerShell.Native` (#8712) +- Build(deps): Bump `NJsonSchema` from `9.13.15` to `9.13.19` (#8732, #8747, #8881, #8952) +- Build(deps): Bump `PackageManagement` from `1.2.4` to `1.3.1` (#8800) +- Build(deps): Bump `XunitXml.TestLogger` from `2.0.0` to `2.1.26` (#8731) +- Build(deps): Bump `Markdig.Signed` from `0.15.7` to `0.16.0` (#8981) + +### Documentation and Help Content + +- Updating README.md for supported openSUSE version and updating link to OS versions supported by CoreFx (#8701) (Thanks @stknohg!) +- Add complete XML docs for `ConvertToJsonContext` constructors (#8737) +- Update README.md for ARM to include both 32-bit and 64-bit PS package links (#8677) (Thanks @slide!) +- Update issue templates with new supported values (#8718) (Thanks @RDIL!) +- Update maintainer docs about the CLA PR labels (#8734) (Thanks @RDIL!) +- Add Andrew to the maintainer list (#8722) +- Update release process template (#8711) +- Change label in doc issue template (#8895) (Thanks @iSazonov!) +- Update the `dir -recurse` example (#8939) (Thanks @vmsilvamolina!) +- Update CHANGELOG for release `6.1.3` (#8918) +- Update stable version to `6.1.3` (#8902) +- Fix broken link (#8905) +- Update Coding Guidelines (#8844) (Thanks @iSazonov!) +- Update governance documentation (#8776) (Thanks @RDIL!) +- Fix broken python method (#8821) (Thanks @RDIL!) +- Changing docs issue template to new docs repo location (#8818) +- Fix spelling in `releaseTool/README.md` (#8810) +- Update GitHub templates (#8792) (Thanks @iSazonov!) +- Fix broken link in `FAQs.md` (#8803) +- Updated `basics.md` to add a link for showing example for installing git on all package managers (#8735) (Thanks @RDIL!) +- Update `README.md` for `preview.4` (#8772) + +## [6.2.0-preview.4] - 2019-01-28 + +### Breaking Changes + +- Add `-Stable` to `Sort-Object` and related tests (#7862) (Thanks @KirkMunro!) +- Improve `Start-Sleep` cmdlet to accept fractional seconds (#8537) (Thanks @Prototyyppi!) +- Change hashtable to use `OrdinalIgnoreCase` to be case-insensitive in all Cultures (#8566) +- Fix `LiteralPath` in `Import-Csv` to bind to `Get-ChildItem` output (#8277) (Thanks @iSazonov!) + +### Engine Updates and Fixes + +- Allow user-specified underlying type for enums (#8329) (Thanks @IISResetMe!) +- Handle case where AppLocker test script fails to delete (#8627) +- Update `CommandNotFound` fuzzy suggestion to only return unique results (#8640) +- Add support to show suggestions on `CommandNotFound` exception (#8458) +- Make `S.M.A.PowerShell.GetSteppablePipeline` method public (#8055) (Thanks @KirkMunro!) +- Add `S.M.A.PowerShell.Create` method overload with Runspace argument (#8057) (Thanks @KirkMunro!) +- Fix mistake on deserialization (#8502) +- Fix formatting of header of table when center aligned (#8497) +- Add `-RepeatHeader` to `Format-Table` to enable repeating header for each screen full (#8481) +- Fix `Debug-Runspace` for Unix platforms and properly enable Windows identity impersonation code (#8451) +- Reset output attributes if column had `ESC` char when using `Format-Table`; Replace `...` with unicode ellipsis (#8326) + +### Experimental Features + +- Add the experimental feature `PSUseAbbreviationExpansion` to support tab completion on abbreviated command names (#8109) + +### General Cmdlet Updates and Fixes + +- Fix code page parsing issue in `Invoke-RestMethod` (#8694) (Thanks @markekraus!) +- Fix `Expect 100-continue` issue with Web Cmdlets (#8679) (Thanks @markekraus!) +- Allow 'name' as an alias key for 'label' in `ConvertTo-Html`, allow the 'width' entry to be an integer (#8426) (Thanks @mklement0!) +- Resolve `:PAGER` if its path contains spaces (#8571) (Thanks @pougetat!) +- Add support enum and char types in `Format-Hex` cmdlet (#8191) (Thanks @iSazonov!) +- Change `Get-Help` cmdlet `-Parameter` parameter so it accepts string arrays (#8454) (Thanks @sethvs!) +- Fix `FixupFileName` to not load resolved assembly during module discovery (#8634) +- Change `Clear-Host` back to using `$RAWUI` and `clear` to work over remoting (#8609) +- Fix `LiteralPath` in `Import-Csv` to bind to `Get-ChildItem` output (#8277) (Thanks @iSazonov!) +- Make scriptblock based calculated properties work again in `ConvertTo-Html` (#8427) (Thanks @mklement0!) +- Fix `Join-String` cmdlet `FormatString` parameter logic (#8449) (Thanks @sethvs!) +- Allow Windows users in developer mode to create symlinks without elevation (#8534) +- `Help` function should only pass content to pager if content was found (#8528) +- Change `Clear-Host` to simply called `[console]::clear` and remove `clear` alias from Unix (#8603) +- `help` function shouldn't use pager for `AliasHelpInfo` (#8552) +- Fix XML nesting bug in `CustomSerializer.WriteMemberInfoCollection()` (#8476) (Thanks @IISResetMe!) +- Add `-UseMinimalHeader` to `Start-Transcript` to minimize transcript header (#8402) (Thanks @lukexjeremy!) + +### Code Cleanup + +- Remove the no longer used `RunspaceConfigurationEntry` types (#8424) +- Remove unneeded catch/throw from `mkdir` and `oss` functions (#8425) +- Remove comments after closing brackets (#8344) (Thanks @Meir017!) +- Cleanup `Format-Hex` (#8683) (Thanks @vexx32!) +- Delete `appveyor.yml` (#8639) (Thanks @RDIL!) +- Revise use of `Start-Sleep` cmdlet (#8633) (Thanks @xtqqczze!) +- Style: Change first char to upper in summary comments (#8597) (Thanks @iSazonov!) +- Style: Use the type aliases `char` and `bool` instead of `Char` and `Boolean` (#8572) (Thanks @iSazonov!) +- Style: Use the type alias `string` instead of `String` in places that are appropriate (#8573) (Thanks @iSazonov!) +- Correctly capitalize the `ForEach` operator in `*.ps1` (#8583) (Thanks @xtqqczze!) +- Remove unnecessary trim of passed-in command line in interactive debugging (#8594) +- Style: Add a space after "//" in comments and remove unneeded comments after "}" (#8576) (Thanks @iSazonov!) +- Style: Add the ending period to the XML document texts (#8577) (Thanks @iSazonov!) +- Avoid use of `mkdir` alias in `*.ps1` and `*.psm1` (#8582) (Thanks @xtqqczze!) +- Regularize redirection operator spacing in `*.ps1` and `*.psm1` (#8581) (Thanks @xtqqczze!) +- Style: Change 'String.' to 'string.' (#8568) (Thanks @iSazonov!) +- Style: Replace `String.IsNullOrEmpty` with `string.IsNullOrEmpty` (#8557) (Thanks @iSazonov!) +- Fix typo in AMSI test (#8561) (Thanks @iSazonov!) +- Style: Convert to upper first char in `` and `` doc tags (#8556) (Thanks @iSazonov!) +- Style: Add period before `` and `` doc tags (#8553) (Thanks @iSazonov!) +- Remove use of cmdlet aliases from `.\test\powershell` (#8546) (Thanks @xtqqczze!) +- Style: Remove extra spaces after `` and before `` docs tags (#8547) (Thanks @iSazonov!) +- Style: Remove preceding spaces from C# `preprocessor-type` keywords (#8540) (Thanks @xtqqczze!) +- Style: remove ` ` (#8538) (Thanks @iSazonov!) +- Style: Add period before returns doc tag (#8535) (Thanks @iSazonov!) +- Style: Change `Object[]` to `object[]` (#8526) (Thanks @iSazonov!) +- Style: Change `Object` to `object` (#8522) (Thanks @iSazonov!) +- Style: Change `UInt64?` to `ulong?` (#8527) (Thanks @iSazonov!) +- Style: Change `Byte{}` to `byte[]` (#8525) (Thanks @iSazonov!) +- Code cleanup: Add space after closing brace where needed (#8530) +- Style: Change `System.Boolean` to `bool` (#8521) (Thanks @iSazonov!) +- Change `String` to `string` for simple references (#8519) +- Change `Int32` to `int` for simple references in variable declaration (#8518) +- Style: Member access symbols should be followed with member name (#8517) +- Style: Remove extra space before colon in named parameters (#8504) +- Style: Use the shorthand of the `nullable` type (#8501) +- Remove empty lines; correct space on closing square brackets, negative signs, and generic brackets (#8508) +- Remove space after new keyword in implicitly typed array allocation (#8505) +- The static keyword should be right after access modifier (#8506) +- Remove comments after closing bracket (#8503) +- Remove space character after `'!'` (#8507) +- Style: Remove extra space before colon in named parameters (#8504) + +### Tools + +- Recommend Azure DevOps extension inside VS-Code for better `YAML` editing. (#8403) (Thanks @bergmeister!) +- `-AddToPath` re-implementation in `install-powershell.ps1` (#8081) (Thanks @glachancecmaisonneuve!) +- Change the feed `URL` to feed name due to changes in `AzDevOps` (#8664) +- Batch merge builds together while a merge build is running (#8668) +- Fix grammar in stale bot message (#8660) (Thanks @RDIL!) +- Add macOS files to `.gitignore` (#8456) (Thanks @RDIL!) +- Name the spelling yaml something more appropriate (#8601) (Thanks @RDIL!) +- Add script to create `icns` files. (#7456) (Thanks @thezim!) +- Pass `nugetkey` as parameter (#8461) +- Add `gitkracken` files to `gitignore` (#8434) (Thanks @RDIL!) +- Create release process issue template (#8417) +- Support for `linuxmint` in `installpsh-debian.sh` (#8440) (Thanks @DarwinJS!) +- Enable `install-powershell.ps1` to use `MSI` (#8418) + +### Tests + +- Remove broken `HelpUri` from `CimTest` (#8688) (Thanks @xtqqczze!) +- Remove appveyor environment checks (#8669) (Thanks @RDIL!) +- Adding tests for `PSDiagnostics Module` (#8431) (Thanks @kvprasoon!) +- Increase diagnose-ability of Link Checker failures (#8667) +- Fix broken urls (#8653) +- Update fuzzy test to fix daily build (#8629) +- Create link check task (#8471) (Thanks @RDIL!) +- Add Tests for `ConfirmImpact` Ratings (#8214) (Thanks @vexx32!) +- Fix style issues in xUnit tests (#8465) (Thanks @iSazonov!) +- Move `xUnit` tests in new folder (#8356) (Thanks @iSazonov!) +- Fix environment variable test and add missing null check in `CommandHelpProvider` (#8408) +- Remove `dotnet` dependency to start WebListener (#8390) + +### Build and Packaging Improvements + +- Update Third Party Notices (#8415) +- Adding yaml for Windows Release builds (#8374) +- Bump `NJsonSchema` from `9.13.1` to `9.13.2` (#8422) +- Do not ship fullclr binaries of `PackageManagement` (#8700) (Thanks @bergmeister!) +- Fix the build for `fxdependent` build for `dotnet sdk` (#8670) +- Add Windows build to universal release build YAML (#8695) +- Remove `Debian 8` references as it is EOL (#8678) +- Build(deps): Bump `NJsonSchema` from `9.13.14` to `9.13.15` (#8671) +- Build package build using ubuntu 18.04 image (#8666) +- Fix a typo in `packaging.psm1` (#8647) (Thanks @sethvs!) +- Add function to create a framework dependent package `dotnet-sdk` containers (#8644) +- Build(deps): Bump `NJsonSchema` from `9.13.13` to `9.13.14` (#8648) +- Build(deps): Bump `PowerShellGet` from `2.0.3` to `2.0.4` (#8649) +- Fix installing `fpm` and `ronn` in macOS CI by avoid installing docs for them (#8656) +- Build(deps): Bump `Markdig.Signed` from `0.15.6` to `0.15.7` (#8637) +- Build(deps): Bump `System.Security.Cryptography.Pkcs` from `4.5.1` to `4.5.2` (#8614) +- Build(deps): Bump `System.Net.Http.WinHttpHandler` from `4.5.1` to `4.5.2` (#8615) +- Build(deps): Bump `NJsonSchema` from `9.13.11` to `9.13.13` (#8616) +- Build(deps): Bump `System.Text.Encoding.CodePages` from `4.5.0` to `4.5.1` (#8613) +- Enable install of Preview MSI release side-by-side with Stable release (#8513) +- Get macOS to publish daily build to nugetfeed (#8464) +- Build(deps): Bump `Markdig.Signed` from `0.15.5` to `0.15.6` (#8558) +- Build(deps): Bump `NJsonSchema` from `9.13.10` to `9.13.11` (#8569) +- Remove duplicate `Open Here` context menu item upgrading to newer Preview release (#8496) +- Bump `NJsonSchema` from `9.13.9` to `9.13.10` (#8511) +- Bump `NJsonSchema` from `9.13.7` to `9.13.9` (#8498) +- Bump `NJsonSchema` from `9.13.4` to `9.13.7` (#8493) +- Bump `NJsonSchema` from `9.13.3` to `9.13.4` (#8462) +- Fix daily NuGet publishing (#8460) +- Bump `NJsonSchema` from `9.13.2` to `9.13.3` (#8457) +- Bump `Markdig.Signed` from `0.15.4` to `0.15.5` (#8444) + +### Documentation and Help Content + +- Remove unused `AppVeyor` links from `README.md` (#8685) (Thanks @RDIL!) +- Update `README.md` (#8684) +- Update Package Management license to MIT (#8676) (Thanks @RDIL!) +- Create Support File (#8618) (Thanks @RDIL!) +- Update git clone URL (#8673) (Thanks @RDIL!) +- docs(contributing): add link check information (#8659) (Thanks @RDIL!) +- Update License and Third Party Notice (#8646) +- Update README, `metadata.json` and changelog for release `6.1.2` (#8658) +- Fix typo in `README.md` (#8642) (Thanks @MarkTiedemann!) +- Fix some typos in the README (#8623) (Thanks @RDIL!) +- Remove `en-us` from `docs.microsoft.com` URL (#8628) (Thanks @xtqqczze!) +- Update examples for hosting PSCore and remove old outdated examples (#8472) (Thanks @bergmeister!) +- Update the pull request template (#8624) (Thanks @RDIL!) +- Contributing guidelines: Remove references to Travis CI and AppVeyor (#8617) (Thanks @RDIL!) +- Update code coverage analysis document (#8543) (Thanks @xtqqczze!) +- Remove `en-us` from our doc links (#8602) +- Document `First-time-issue` and `Hackathon`/`Hacktoberfest` labels (#8575) +- Updated linux build link (#8579) (Thanks @jwmoss!) +- Update contributing guidelines doc to run spellchecking in English (#8473) (Thanks @RDIL!) +- Updating links to point to new VS Code docs (#8468) + +## [6.2.0-preview.3] - 2018-12-10 + +### Breaking Changes + +- `Get-ExperimentalFeature` no longer has `-ListAvailable` switch (#8318) +- `Debug` parameter now sets `DebugPreference` to `Continue` instead of `Inquire` (#8195) (Thanks @KirkMunro!) + +### Engine Updates and Fixes + +- Improve PowerShell startup time by 24% (#8341) (#8396) +- Remove extra newlines from formatting which resulted in unnecessary double newlines (#8247) +- Add `Enable-ExperimentalFeature` and `Disable-ExperimentalFeature` cmdlets (#8318) +- Fix `Export-ModuleMember` bug for a `ScriptBlock` having no context (#8363) +- Fix race condition to access `powershell.config.json` (#8249) (Thanks @iSazonov!) +- Add `SkipCA` and `SkipCN` check requirement to WinRM/OMI HTTPS connection (#8279) +- Add fix for `Start-Job` initialization script which should not be executed as trusted in system lockdown (#8284) + +### General Cmdlet Updates and Fixes + +- Add `Enable-ExperimentalFeature` and `Disable-ExperimentalFeature` cmdlets (#8318) +- Add cmdlet `Join-String` for creating text from pipeline input (#7660) (Thanks @powercode!) +- Expose all cmdlets from `PSDiagnostics` if `logman.exe` is available (#8366) +- Fix `Get-Help` for advanced functions with MAML help content (#8353) +- Conditionally mark getter/setter implementations as virtual in generated classes (#8303) (Thanks @IISResetMe!) +- Fix for `PSDrive` creation with a UNC path with a trailing backslash or forward slash when combined with `-Persist` (#8305) (Thanks @kvprasoon!) +- Remove `Persist` parameter from `New-PSDrive` on non-Windows platform (#8291) (Thanks @lukexjeremy!) +- `Test-Path`: Return `$false` when given an empty or `$null` `-Path`/`-LiteralPath` value (#8080) (Thanks @vexx32!) +- Token calculation fix for `Get-Help` executed on `ScriptBlock` for comment help. (#8238) (Thanks @hubuk!) +- Support `Get-PSHostProcessInfo` and `Enter-PSHostProcess` on Unix platforms (#8232) + +### Code Cleanup + +- Update `resgen`, `typegen` to use .Net Core 2.1 (#8369) (Thanks @bergmeister!) +- Change `Newtonsoft` deserializing bug comment to link to the new issue (#8377) (Thanks @louistio!) +- Cleanup `#if !CORECLR` code (#8337) (Thanks @iSazonov!) +- Cleanup `UpdatableHelpSystem` and enable XSD validation on MAML help content (#8335) (Thanks @iSazonov!) +- Remove old `customPSSnapInType` parameter from `PSSnapInInfo()` (#8333) (Thanks @iSazonov!) +- Cleanup `#if CORECLR` from some files (#8332) (Thanks @iSazonov!) +- Cleanup `AssemblyInfo` (#8190) (Thanks @iSazonov!) +- Fix `GetLocationCommand` output type parameter set and style issues (#8324) (Thanks @Meir017!) + +### Tools + +- Remove `dependabot` attribution and generate changelog sections using `CL-*` labels (#8386) + +### Tests + +- Update folder path for storing optimization profile and add test to validate loaded assemblies and libraries on startup (#8406) +- Fix an intermittent failure in macOS logging tests (#8385) +- Created a `csproj` to pin test modules and updated `build.psm1` accordingly (#8350) +- Update help content for `TabCompletion` tests only if it does not exist (#8355) +- Skip `Enter-PSHostProcess` tests on `AppVeyor` due to `PSReadline` issue (#8317) + +### Build and Packaging Improvements + +- Remove `AmazonLinux` Dockerfile (#8271) (Thanks @kiazhi!) +- Make `install-powershell.sh` auto-detect if it should use `wget` or `curl` (#8225) (Thanks @DarwinJS!) +- Bump `NJsonSchema` from `9.12.2` to `9.13.1` (#8319) (#8328) (#8412) (#8371) (#8384) +- Bump `Microsoft.PowerShell.Native` from `6.2.0-preview.2` to `6.2.0-preview.3` (#8411) +- Update the name of the artifact to be unique per artifact (#8405) +- Create unified release build for macOS and Linux packages (#8399) +- Add Linux `ARM64` build support (#8016) (Thanks @slide!) +- Update the timeout of CI builds (#8398) +- Bump `PackageManagement` from `1.2.2` to `1.2.4` in `/src/Modules` (#8320) (#8383) +- Bump `Newtonsoft.Json` from `11.0.2` to `12.0.1` (#8348) +- Enable pipeline to sync `PSGallery` modules to `AzArtifacts` feed (#8316) +- Build Alpine `tar.gz` package in release builds (#8340) +- Publish test package to `AppVeyor` daily build (#8273) +- Bump `Microsoft.CodeAnalysis.CSharp` from `2.9.0` to `2.10.0` (#8294) +- Bump `PowerShellGet` from `2.0.1` to `2.0.3` in `/src/Modules` (#8321) +- Enable `Open Here` context menu on Windows to work with root of a drive (#8287) +- Bump `System.Data.SqlClient` from `4.5.1` to `4.6.0` (#8266) + +### Documentation and Help Content + +- Merge `changelogs` from `6.1.1` and `6.0.5` into master (#8283) +- Remove all reference to `AppVeyor` and `Travis CI` from docs (#8376) +- Change default issue template to use different categories (#8203) + +## [6.2.0-preview.2] - 2018-11-15 + +### Breaking Changes + +- Honor `-OutputFormat` if specified in non-interactive, redirected, encoded command used with `pwsh` (#8115) +- Load assembly from module base path before trying to load from the `GAC` (#8073) +- Remove tilde from Linux preview packages (#8244) +- Move processing of `-WorkingDirectory` before processing of profiles (#8079) + +### Known Issues + +- PowerShell WSMan remoting does not work on Debian 9 due to missing symbolic links. + For more information and a workaround see issue [#7598](https://github.com/PowerShell/PowerShell/issues/7598) + +### Engine Updates and Fixes + +- Enable case-insensitive tab completion for files and folders on case-sensitive filesystem (#8128) +- Experimental feature: Implicit remoting batching performance improvements (#8038) +- Add a path for checking `ZoneInformation` without throwing an exception (#8025) (Thanks @powercode!) +- Fix [CVE-2018-8256](https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2018-8256), + issues with expanding `ZIP` files with relative paths (#8252) +- Fix [CVE-2018-8415](https://portal.msrc.microsoft.com/en-US/security-guidance/advisory/CVE-2018-8415), + issue logging when the `scriptblock` has a null character (#8253) +- Make `PSVersionInfo.PSVersion` and `PSVersionInfo.PSEdition` public (#8054) (Thanks @KirkMunro!) +- Enable distinct `ModuleAnalysisCache` files for each installation of `pwsh` (#8174) +- Consolidation of all Windows PowerShell work ported to PowerShell Core (#8257) +- Fix incorrect name check when auto-loading required modules (#8218) +- Adding verbose output for experimental implicit remoting batching feature (#8166) +- Add Type Inference for `$_ / $PSItem in catch{ }` blocks (#8020) (Thanks @vexx32!) +- Fix static method invocation type inference (#8018) (Thanks @SeeminglyScience!) + +### General Cmdlet Updates and Fixes + +- Reduce allocations in `Get-Content` cmdlet (#8103) (Thanks @iSazonov!) +- Enable `Set-Location -LiteralPath` to work with folders named `-` and `+` (#8089) +- Enable `Add-Content` to share read access with other tools while writing content (#8091) +- Add new `Offset` and `Count` parameters to `Format-Hex` and refactor the cmdlet (#7877) (Thanks @iSazonov!) +- Add `-Name`, `-NoUserOverrides` and `-ListAvailable` parameters to `Get-Culture` cmdlet (#7702) (Thanks @iSazonov!) +- Allow dynamic parameter to be returned even if path does not match any provider (#7957) +- Style fixes in `Format-Hex` (#8083) (Thanks @iSazonov!) +- Fix logic to rely on PowerShell major and minor version instead of build number to determine whether to output `formatdata` (#8063) +- Fix `Rename-Item -Path` with wildcard `char` (#7398) (Thanks @kwkam!) +- When using `Start-Transcript` and file exists, empty file rather than deleting (#8131) (Thanks @paalbra!) +- Error message enhancement for `Clear-Content` cmdlet when targeting a directory (#8134) (Thanks @kvprasoon!) +- Make `Select-String` faster by not doing extra work (#7673) (Thanks @powercode!) +- Remove `ShouldProcess` from `Format-Hex` (#8178) + +### Code Cleanup + +- Remove clone of command-line arguments array (#7910) (Thanks @iSazonov!) +- Use `DefaultPathSeparator` `char` instead of `DefaultPathSeparatorString` (#8082) (Thanks @iSazonov!) +- Replace `StringComparision.CurrentCulture` with `StringComparision.Ordinal` (#8068) (Thanks @iSazonov!) +- Fix typo in `-icontains` description from `incase sensitive` to `case insensitive` (#7840) (Thanks @StingyJack!) +- Refactor module version/`GUID` comparison logic (#7125) + +### Tools + +- Update `installpsh-amazonlinux.sh` for container specific issues (#7907) (Thanks @DarwinJS!) +- Update the `codeowners` file (#8017) + +### Tests + +- Filter the `TestPackage` artifact upload by name to avoid other `ZIP` files being uploaded (#8116) +- Adding `fxdependent` PowerShell package tests (#7830) +- Fix Windows Feature tests running in Azure DevOps (#8220) +- Create `$PROFILE` if it does not exist for `-WorkingDirectory` processing test (#8152) +- Add test coverage for additional `Get-Module` parameters (#8137) (Thanks @KevinMarquette!) +- Fix conflict with `Get-AdlStoreChildItem` from `az` module in tab completion tests (#8167) +- Fix static secret in code (#8186) + +### Build and Packaging Improvements + +- Bump `xunit.runner.visualstudio` from `2.4.0` to `2.4.1` (#8139) +- Bump `xunit` from `2.4.0` to `2.4.1` (#8140) +- Bump `Microsoft.ApplicationInsights` from `2.8.0` to `2.8.1` (#8104) +- Bump `NJsonSchema` from `9.11.1` to `9.12.1` (#8183, #8248) +- Fix `Start-PSBuild -Output` (#7504) (Thanks @kwkam!) +- Adding `YML` for Linux builds (#8168) +- Publish test package at `AGENT_WORKFOLDER` if `TEMP` is not available (#8108) +- Fix `psmodulerestore` path when built in Visual Studio Code (#8075) +- Use approved verb instead of `Generate-CrossGenAssembly` (#8151) (Thanks @kvprasoon!) +- Add path filters to CI `YAML` (#8222) +- Update `SignType` in `signing.xml` (#8223) +- Update metadata for `6.0.5` and `6.1.1` releases (#8259) +- Port changes to allow Azure DevOps NuGet feeds for Mac build (Internal 5818) +- Update version for dependencies (Internal 5822) +- Add code to use private NuGet feeds when running in internal CI system (#8187) +- Add title to `Open Here` window for `MSI` installer (#8164) +- Remove build and documentation references to `git` submodules (#8177) (Thanks @andschwa!) +- Add function to create a new `nuget.config` file (#8170) +- Update macOS release build to create the `nuget.config` (#8185) +- Workaround for accessing Azure Artifacts (#8188) +- Fix script path for `PowerShellPackageVsts.ps1` (#8189) +- `Microsoft.PowerShell.Native` now has `MUSL` binaries for Alpine. + +### Documentation and Help Content + +- Fix grammar in `README.md` (#8059) (Thanks @daviddreher2!) +- Update `powershell-beginners-guide.md` to add alias for `Clear-Host` (#7912) (Thanks @aavdberg!) +- Add Microsoft Docs link to FAQ (#8133) (Thanks @vongrippen!) +- Added updated photo of Visual Studio Code due to new version of Code (#8084) (Thanks @lassehastrup!) +- Update `license.rtf` to only have major version (#8127) +- Updated Pester Syntax in Writing Tests Guide (#8039) (Thanks @markwragg!) +- Remove duplicate parts from license file (#8143) (Thanks @azkarmoulana!) +- Fix spellings in `CHANGELOG.md` (#8062) +- Update license RTF to 6.2 (#8065) +- Combine notes about `ITuple` changes in Change Log (#8077) (Thanks @Jocapear!) +- Correct typos in `powershell-beginners-guide.md` (#8088) (Thanks @nycjan!) +- Added `Learn Windows PowerShell in a Month of Lunches` as recommended reading (#8067) (Thanks @tobvil!) +- Update `README.md` for `v6.1.1` (#8255) +- Fix some typos (#8206) (Thanks @jeis2497052!) +- Promote `HTTPS` (#8160) (Thanks @RDIL!) +- Simple grammatical correction in `README.md` file (#7978) (Thanks @iGotenz!) +- Update URLs to use `HTTPS` instead of `HTTP` in the documentation (#8165) (Thanks @RDIL!) +- Remove #7633 from `v6.2.0-preview.1` `CHANGELOG.md` updates. (#8101) (Thanks @stknohg!) + +## [6.2.0-preview.1] - 2018-10-18 + +### Breaking Changes + +- Do not add `PATHEXT` environment variable on Unix (#7697) (Thanks @iSazonov!) + +### Known Issues + +- Remoting on Windows IOT ARM platforms has an issue loading modules. See [#8053](https://github.com/PowerShell/PowerShell/issues/8053) + +### Engine Updates and Fixes + +- Add C# style type accelerators and suffixes for `ushort`, `uint`, `ulong`, and `short` literals (#7813) (Thanks @vexx32!) +- Create inferred types for `Select-Object`, `Group-Object`, `PSObject` and `Hashtable` (#7231) (Thanks @powercode!) +- Fix .NET adapter to be able to get members from `System.IntPtr` (#7808) +- Fix .NET adapter to not throw when fails to create a `PSMethod` due to `ByRef-like` type (#7788) +- Support calling method with `ByRef-like` type parameters (#7721) +- Fix perf issue in provider by using `Refresh()` to update the status rather than instantiating `ServiceController` which has a significant perf degradation from .NET Framework (#7680) +- Update PowerShell to handle the case where the Windows PowerShell module path is already in the environment's `PSModulePath` (#7727) +- Ensure the `SSHClientSessionTransportManager` stream writer and reader fields are cleared after dispose. (#7746) +- Add unified attribute for completion for `Encoding` parameter. (#7732) (Thanks @ThreeFive-O!) +- Add support for Byte Literals (#7901) (Thanks @vexx32!) +- Fix Property and `ScriptBlock` expressions in `EntrySelectedBy` tags within custom controls (#7913) (Thanks @SeeminglyScience!) +- Fix `BeginInvoke`/`EndInvoke` to return results when `Stop` or `BeginStop`/`EndStop` was called previously (#7917) +- Allow root node of `format.ps1xml` to have attributes that are ignored (#7987) +- Use non-virtual call to invoke 'family or assembly' methods on base class from PowerShell class (#7622) (#7624) (Thanks @yurko7!) +- Make the parameter to `ImportPSModule` use `params` so that it is easier to call (#7933) (Thanks @iSazonov!) + +### General Cmdlet Updates and Fixes + +- Add `EscapeHandling` parameter in `ConvertTo-Json` cmdlet (#7775) (Thanks @iSazonov!) +- Make `Add-Type` open source files with `FileAccess.Read` and `FileShare.Read` explicitly (#7915) (Thanks @IISResetMe!) +- No longer skips a column without name if double quote delimiter is used in `Import-Csv` (#7899) (Thanks @Topping!) +- Add support for `cd +` (#7206) (Thanks @bergmeister!) +- Allow numeric Ids and name of registered code pages in `-Encoding` parameters (#7636) (Thanks @iSazonov!) +- Remove extra space in `LastWriteTime` format (#7810) (Thanks @iSazonov!) +- Fix `Enter-PSSession -ContainerId` for the latest Windows (#7883) +- `Get/Add-Content` throws improved error when targeting a container (#7823) (Thanks @kvprasoon!) +- Ensure `NestedModules` property gets populated by `Test-ModuleManifest` (#7859) +- Add `%F` case to `Get-Date -UFormat` (#7630) (Thanks @britishben!) +- Fix file blocking issue with web cmdlets (#7676) (Thanks @Claustn!) +- Improve error message on non-Windows when importing `clixml` with `securestring` (#7997) +- Add prompt to the use of less in the function 'help' to instruct user how to quit (#7998) +- Fix `Set-Service -Status Stopped` to stop services with dependencies (#5525) (Thanks @zhenggu!) + +### Code Cleanup + +- Use `nameof()` in bound `parameters.contains key()` (#7908) (Thanks @iSazonov!) +- Cleanup all native code from repository (#7892) +- Add `XSDs` for Format and `Types.ps1xml` files (#7832) (Thanks @felixfbecker!) +- Remove unused commented out code (#7935) (Thanks @vpondala!) +- Add `.editorconfig` (#7357) (Thanks @iSazonov!) +- Remove unused stopwatch (#7878) +- Clean up `MshObject.cs` and `MshMemberInfo.cs` (#7446) +- Add `TimeToLive` and `Hops` aliases to `MaxHops` parameter of `Test-Connection` cmdlet. (#7850) (Thanks @sethvs!) +- Fix a typo in `Credential.cs` (#7696) (Thanks @sethvs!) +- Remove workaround on VSTS that is no longer needed (#7666) +- Improve code style of `Send-MailMessage` cmdlet (#7723) (Thanks @ThreeFive-O!) +- Cleanup `FileSystemProvider` from runtime checks (#7655) (Thanks @iSazonov!) +- Remove extra spaces in error messages in `Modules.resx` (#7662) (Thanks @sethvs!) +- Remove empty XML comment lines (missed in #7401) (#7641) (Thanks @kvprasoon!) +- Remove `Suspend-Job` and `Resume-Job` cmdlets from compilation (#7650) + +### Tools + +- Fix syntax error in `installpwsh-amazonlinux.sh` (#7905) (Thanks @DarwinJS!) +- Add tools for PowerShell perf analysis (#7595) (Thanks @lzybkr!) +- Started using [Dependabot](https://dependabot.com) to create PRs to update package and module versions + +### Tests + +- Add test for `$error[0]` tab completion (#7924) (Thanks @iSazonov!) +- Replace test certificates with self-signed certificate generating command (#7875) +- Standardize Pester syntax in `ReplaceOperator.Tests.ps1` (#7963) (Thanks @sethvs!) +- Updating `ModulePath.Tests` for `fxdependent` package (#7772) +- Add tests for `Import-Module -Force` (#7491) +- Updates to Docker package tests (#7667) +- Updating test gallery URL in `PackageManagement` tests (#7879) +- Add version checking tests for `Import-Module` (#7499) +- Update Markdown tests (#7838) +- Change locale of `mdspell` to `en-US` (#7671) +- Test changes needed for running in a container (#7869) +- Add daily build non-windows platforms (#7683) +- Remove workaround on VSTS that is no longer needed (#7666) +- Fix module specification `hashtable` in `ModuleSpecification.Tests.ps1` (#7663) (Thanks @sethvs!) +- Use `dotnet test` since the `dotnet xunit` test runner has been deprecated (#7980) (Thanks @bergmeister!) +- Fix pipeline test where `SmtpServer` key was set wrong in pipeline object (#7745) (Thanks @ThreeFive-O!) +- Change API to get host name to match cmdlet which is more reliable in Azure DevOps Pipelines `Hosted Windows VS2017` (#8003) +- Disable `travis-ci` (#7766) +- Make artifact upload only occur for non-PR builds (#7657) +- Change logic for downloading zip files based on job id (#7653) +- Add missing dependency for hosting xUnit tests + +### Build and Packaging Improvements + +- Change default of `Start-PSBuild` to include `-PSModuleRestore` (#7881) +- Specify verb, `pwsh`, for shell context menu to avoid overriding the default verb (#7932) (Thanks @bergmeister!) +- Converting aliases to cmdlets in `build.psm1` (#7964) (Thanks @kvprasoon!) +- Add dependencies for SUSE (#7938) (Thanks @Jellyfrog!) +- Wait for package manager not to be locked (#7817) +- Make `Start-PSPackage` give better message about how to fix `files.wxs` (#7841) +- Bump to .NET Core `2.1.5` with SDK `2.1.403` and latest packages (#7646, #7834, #7922, #7936) (Thanks @iSazonov!) +- Bump `Markdig.Signed` NuGet package from `0.15.3` to `0.15.4` (#7960) (Thanks @bergmeister!) +- Bump `Microsoft.ApplicationInsights` from `2.7.2` to `2.8.0` (#8002) +- Bump `Microsoft.PowerShell.Native` from `6.1.0-rc.1` to `6.1.0` (#7861) +- Bump `NJsonSchema` from `9.10.71` to `9.11.1` (#7705, #7764, #7990) +- Bump `PackageManagement` from `1.1.7.2` to `1.2.2` in /src/Modules (#8014, #8029) +- Bump `Pester` to use latest version (#8015) +- Bump `PowerShellGet` to `2.0.0` (#7831) +- Bump `PSReadLine` to `2.0.0-beta3` (#7711) +- Bump `Xunit.SkippableFact` from `1.3.6` to `1.3.12` (#7972) +- Make Windows MSI uninstallation shortcut remove work when more than one version is installed (#7701) (Thanks @bergmeister!) +- Update Docker files to use MCR (#7726) +- Update `metadata.json` in preparation for `6.1` release (#7741) +- Build and package framework dependent package (#7729) +- Make sure MSI build works when not preview (#7752) +- Remove `PDBs` from `fxdependent` package (#8006) +- Improve debugging of NuGet package generation and add type to filtering + +### Documentation and Help Content + +- Replace ambiguous term (#7902, #7931) (Thanks @iSazonov!) +- Updating incorrect example of `PowerShell.Create()` (#7926) (Thanks @1RedOne!) +- Update `governance.md` (#7927) (Thanks @tommymaynard!) +- Add `cURL` to the Bash users list in `README.md` (#7948) (Thanks @vmsilvamolina!) +- Optimize image assets used in documentation (#7874) (Thanks @RDIL!) +- Update build badges (#7792) +- Remove packaging, building and installation scripts for Ubuntu 17.10 (#7773) +- Remove badges for master build as it reflects last PR which is not actionable from the `README` file (#7816) +- Improve Markdown formatting of beginners guide (#7684) (Thanks @fbehrens!) +- Fix the `Basic cookbooks` link (#7934) (Thanks @vmsilvamolina!) +- Update version for PowerShell release `6.1.0` (#7751) +- Add VSTS CI build badges for master branch to `README.md` (#7691) (Thanks @bergmeister!) +- Add a paragraph on `files.wxs` updating (#7695) (Thanks @iSazonov!) +- Update `CONTRIBUTION.md` about adding an empty line after the copyright header (#7706) (Thanks @iSazonov!) +- Update docs about .NET Core version `2.0` to be about version `2.x` (#7467) (Thanks @bergmeister!) + +[6.2.7]: https://github.com/PowerShell/PowerShell/compare/v6.2.6...v6.2.7 +[6.2.6]: https://github.com/PowerShell/PowerShell/compare/v6.2.5...v6.2.6 +[6.2.5]: https://github.com/PowerShell/PowerShell/compare/v6.2.4...v6.2.5 +[6.2.4]: https://github.com/PowerShell/PowerShell/compare/v6.2.3...v6.2.4 +[6.2.3]: https://github.com/PowerShell/PowerShell/compare/v6.2.2...v6.2.3 +[6.2.2]: https://github.com/PowerShell/PowerShell/compare/v6.2.1...v6.2.2 +[6.2.1]: https://github.com/PowerShell/PowerShell/compare/v6.2.0...v6.2.1 +[6.2.0]: https://github.com/PowerShell/PowerShell/compare/v6.2.0-rc.1...v6.2.0 +[6.2.0-rc.1]: https://github.com/PowerShell/PowerShell/compare/v6.2.0-rc.1...v6.2.0 +[6.2.0-preview.4]: https://github.com/PowerShell/PowerShell/compare/v6.2.0-preview.3...v6.2.0-preview.4 +[6.2.0-preview.3]: https://github.com/PowerShell/PowerShell/compare/v6.2.0-preview.2...v6.2.0-preview.3 +[6.2.0-preview.2]: https://github.com/PowerShell/PowerShell/compare/v6.2.0-preview.1...v6.2.0-preview.2 +[6.2.0-preview.1]: https://github.com/PowerShell/PowerShell/compare/v6.1.0...v6.2.0-preview.1 diff --git a/CHANGELOG/7.0.md b/CHANGELOG/7.0.md new file mode 100644 index 00000000000..e054b34cfc9 --- /dev/null +++ b/CHANGELOG/7.0.md @@ -0,0 +1,1421 @@ +# 7.0 Changelog + +## [7.0.13] - 2022-10-20 + +### Engine Updates and Fixes + +- Stop sending telemetry about `ApplicationType` (#18265) + +### Build and Packaging Improvements + +
+ + + +

Bump .NET SDK to 3.1.424 (#18272)

+ +
+ +
    +
  • Update Wix file for new assemblies (Internal 22873)
  • +
  • Update the cgmanifest.json for v7.0.13 (#18318)
  • +
  • Update Newtonsoft.Json version for 7.0.13 release (#18259)
  • +
  • Fix build.psm1 to not specify both version and quality for dotnet-install (#18267)
  • +
  • Update list of PowerShell team members in release tools(#18266)
  • +
  • Move cgmanifest generation to daily (#18268)
  • +
  • Disable static analysis CI on 7.0 (#18269)
  • +
+ +
+ +[7.0.13]: https://github.com/PowerShell/PowerShell/compare/v7.0.12...v7.0.13 + + +## [7.0.12] - 2022-08-11 + +### General Cmdlet Updates and Fixes + +- Fix `Export-PSSession` to not throw error when a rooted path is specified for `-OutputModule` (#17671) + +### Tests + +- Enable more tests to be run in a container. (#17294) +- Switch to using GitHub action to verify markdown links for PRs (#17281) +- Add `win-x86` test package to the build (#15517) + +### Build and Packaging Improvements + +
+ + +

Bump .NET 3.1 SDK to 3.1.28

+
+ +
    +
  • Update wix file
  • +
  • Add a finalize template which causes jobs with issues to fail (#17314)
  • +
  • Make sure we execute tests on LTS package for older LTS releases (#17326)
  • +
  • Update AzureFileCopy task and fix the syntax for specifying pool (#17013)
  • +
+ +
+ +[7.0.12]: https://github.com/PowerShell/PowerShell/compare/v7.0.11...v7.0.12 + +## [7.0.11] - 2022-05-13 + +### Build and Packaging Improvements + +
+ + + +

Update .NET SDK to 3.1.419

+ +
+ +
    +
  • Add explicit job name for approval tasks in Snap stage (#16579)
  • +
  • Update to use mcr.microsoft.com (#17272)
  • +
  • Update global.json and wix
  • +
  • Put Secure supply chain analysis at correct place (#17273)
  • +
  • Partial back-port of: Update a few tests to make them more stable in CI (#16944) (Internal 20648)
  • +
  • Replace . in notices container name (#17292)
  • +
  • Add an approval for releasing build-info json (#16351)
  • +
  • Release build info json when it is preview (#16335)
  • +
  • Add a major-minor build info JSON file (#16301)
  • +
  • Update release instructions with link to new build (#17256)
  • +
  • Add condition to generate release file in local dev build only (#17255)
  • +
  • Removed old not-used-anymore docker-based tests for PS release packages (#16224)
  • +
  • Publish global tool package for stable releases (#15961)
  • +
  • Update to use windows-latest as the build agent image (#16831)
  • +
  • Don't upload dep or tar.gz for RPM build because there are none. (#17224)
  • +
  • Update to vPack task version 12 (#17225)
  • +
  • Make RPM license recognized (#17223)
  • +
  • Ensure psoptions.json and manifest.spdx.json files always exist in packages (#17226)
  • +
+ +
+ +[7.0.11]: https://github.com/PowerShell/PowerShell/compare/v7.0.10...v7.0.11 + +## [7.0.10] - 2022-04-26 + +### Engine Updates and Fixes + +- Fix for partial PowerShell module search paths, that can be resolved to CWD locations +- Do not include node names when sending telemetry. (#16981) to v7.0.10 (Internal 20186,Internal 20261) + +### Tests + +- Re-enable `PowerShellGet` tests targeting PowerShell gallery (#17062) +- Skip failing scriptblock tests (#17093) + +### Build and Packaging Improvements + +
+ + + +

Update .NET SDK to 3.1.418

+ +
+ +
    +
  • Fixed package names verification to support multi-digit versions (Internal 20363)
  • +
  • Fix build failure in `generate checksum file for packages` step - v7.0.10 (Internal 20275)
  • +
  • Updated files.wxs for 7.0.10 (Internal 20208)
  • +
  • Updated to .NET 3.1.24 / SDK 3.1.418 (Internal 20133)
  • +
  • Disable broken macOS CI job, which is unused (Internal 20189)
  • +
  • Update Ubuntu images to use Ubuntu 20.04 (#15906)
  • +
  • Update dotnet-install script download link (Internal 19949)
  • +
  • Create checksum file for global tools (Internal 19934)
  • +
  • Make sure global tool packages are published in stable build (Internal 19623)
  • +
+ +
+ +[7.0.10]: https://github.com/PowerShell/PowerShell/compare/v7.0.9...v7.0.10 + +## [7.0.9] - 2022-03-16 + +### Build and Packaging Improvements + +
+ + + +

Update .NET SDK to 3.1.417

+ +
+ +
    +
  • Fix the NuGet SDK package creation (Internal 19569)
  • +
  • Fix NuGet package compliance issues (#13045)
  • +
  • Fix issues in release build (#16332)
  • +
  • Enable ARM64 packaging for macOS (#15768)
  • +
  • Update feed and analyzer dependency (#16327)
  • +
  • Only upload stable buildinfo for stable releases (#16251)
  • +
  • Opt-in to build security monitoring (#16911)
  • +
  • Update experimental feature json files (#16838) (Thanks @!)
  • +
  • Ensure alpine and arm SKUs have the PowerShell configuration file with experimental features enabled (#16823)
  • +
  • Remove WiX install (#16834)
  • +
  • Add Linux package dependencies for packaging (#16807)
  • +
  • Switch to our custom images for build and release (#16801)
  • +
  • Remove all references to cmake for the builds in this repo (#16578)
  • +
  • Register NuGet source when generating CGManifest (#16570)
  • +
  • Update Images used for release (#16580)
  • +
  • Add Software Bill of Materials to the main packages (#16202, #16641, #16711)
  • +
  • Add GitHub Workflow to keep notices up to date (#16284)
  • +
  • Update the vmImage and PowerShell root directory for macOS builds (#16611)
  • +
  • Update macOS build image and root folder for build (#16609)
  • +
  • Add checkout to build json stage to get ci.psm1 (#16399)
  • +
  • Move mapping file into product repo and add Debian 11 (#16316)
  • +
+ +
+ +[7.0.9]: https://github.com/PowerShell/PowerShell/compare/v7.0.8...v7.0.9 + +## [7.0.8] - 2021-10-14 + +### Engine Updates and Fixes + +- Handle error from unauthorized access when removing `AppLocker` test files (#15881) +- Handle error when the telemetry mutex cannot be created (#15574) (Thanks @gukoff!) +- Configure `ApplicationInsights` to not send cloud role name (Internal 17099) +- Disallow `Add-Type` in NoLanguage mode on a locked down machine (Internal 17521) + +### Tools + +- Add `.stylecop` to `filetypexml` and format it (#16025) + +### Build and Packaging Improvements + +
+ + +

Bump .NET SDK to 3.1.414

+
+ +
    +
  • Update the nuget.config file used for building NuGet packages (Internal 17547)
  • +
  • Sign the .NET createdump executable (#16229)
  • +
  • Upgrade set-value package for markdown test (#16196)
  • +
  • Move vPack build to 1ES Pool (#16169)
  • +
  • Update to .NET SDK 3.1.414 (Internal 17532)
  • +
  • Fix the macOS build by updating the pool image name (#16010)
  • +
  • Move from PkgES hosted agents to 1ES hosted agents (#16023)
  • +
  • Use Alpine 3.12 for building PowerShell for Alpine Linux (#16008)
  • +
+ +
+ +### Documentation and Help Content + +- Fix example nuget.config (#14349) + +[7.0.8]: https://github.com/PowerShell/PowerShell/compare/v7.0.7...v7.0.8 + +## [7.0.7] - 2021-08-12 + +### Build and Packaging Improvements + +
+ + +Bump .NET SDK to 3.1.412 + + +
    +
  • Remove cat file from PSDesiredStateConfiguration module (Internal 16722)
  • +
  • Update .NET SDK to 3.1.412 (Internal 16717)
  • +
+ +
+ +[7.0.7]: https://github.com/PowerShell/PowerShell/compare/v7.0.6...v7.0.7 + +## [7.0.6] - 2021-03-11 + +### General Cmdlet Updates and Fixes + +- Fix web cmdlets to properly construct URI from body when using `-NoProxy` (#14673) +- Fix `PromptForCredential()` to add `targetName` as domain (#14504) +- Clean up the IPC named pipe on PowerShell exit (#12187) + +### Tests + +- Update markdown test packages with security fixes (#13730, #14145, #14454) + +### Build and Packaging Improvements + +
+ + +Bump .NET SDK to version 3.1.407 + + +
    +
  • Bump .NET to version 3.1.407 (Internal 14783)
  • +
  • Fix the miscellaneous analysis CI build (#14971, #14974, #14975)
  • +
  • Declare which variable group is used for checking the blob in the release build (#14970)
  • +
  • Use template that disables component governance for CI (#14938)
  • +
  • Suppress the warning for having multiple nuget feeds (#14893)
  • +
  • Disable codesign validation where the file type is not supported (#14885)
  • +
  • Make universal Deb package based on deb package spec (#14681)
  • +
  • Add manual release automation steps and improve changelog script (#14445)
  • +
  • Fix a typo in the Get-ChangeLog function (#14129)
  • +
  • Add validation and dependencies for Ubuntu 20.04 distribution to packaging script (#13993)
  • +
  • Add comment in release-BuildJson.yml for date formatting
  • +
  • Install wget on centos-7 docker image
  • +
  • Fix install-dotnet download (#14856)
  • +
  • Fix release build to upload global tool packages to artifacts (#14620)
  • +
  • Fixes to release pipeline for GA release (#14034)
  • +
  • Add checkout step to release build templates (#13840)
  • +
  • Add flag to make Linux script publish to production repo (#13714)
  • +
  • Use new release script for Linux packages (#13705)
  • +
  • Change stage dependency for docker release stage in release pipeline (#13512)
  • +
  • Create the folder before copying the global tools (#13476)
  • +
  • A few fixes to the release pipeline (#13473)
  • +
  • Change the variable group name (Internal 12339)
  • +
  • Create release pipeline as a yaml pipeline (#13394)
  • +
+ +
+ +[7.0.6]: https://github.com/PowerShell/PowerShell/compare/v7.0.5...v7.0.6 + +## [7.0.5] - 2021-02-11 + +### Build and Packaging Improvements + +
+ + +Bump .NET SDK to version 3.1.406 + + +
    +
  • Fix third party signing for files in sub-folders (#14751)
  • +
  • Bump .NET SDK to 3.1.12 (Internal 14462)
  • +
+ +
+ +[7.0.5]: https://github.com/PowerShell/PowerShell/compare/v7.0.4...v7.0.5 + +## [7.0.4] - 2021-01-19 + +### Build and Packaging Improvements + +
+ + +Bump .NET SDK to version 3.1.405 + + +
    +
  • Remove MyGet feeds from test nuget.config (Internal 14147)
  • +
  • Update WXS file for 7.0.4 (Internal 14122)
  • +
  • Update .NET dependencies for 7.0.4 (Internal 14104)
  • +
  • Fix 7.0.4 `Get-Module` test failure (Internal 13946)
  • +
  • Fix directory creation failure (Internal 13904)
  • +
  • Disable WMF link invocation test (#13479)
  • +
  • Use PowerShell Core for build and test of package in CI build (#13223)
  • +
  • Disable libmi dependent tests for macOS. (#14446)
  • +
  • Use one feed in each nuget.config in official builds (#14363)
  • +
  • Fix path signed RPMs are uploaded from in release build (#14424)
  • +
  • Fix syntax error in Windows packaging script (#14377)
  • +
  • Make AppLocker Enforce mode take precedence over UMCI Audit mode (#14353)
  • +
  • Fix issue with unsigned build (#14367)
  • +
  • Move macOS and NuGet to ESRP signing (#14324)
  • +
  • Move Windows package signing to use ESRP (#14060)
  • +
  • Move Linux to ESRP signing (#14210)
  • +
  • Migrate 3rd party signing to ESRP (#14010)
  • +
  • Don't do a shallow checkout (#13992)
  • +
  • Move to ESRP signing for Windows files (#13988)
  • +
  • Fix breaks in packages daily build due to macOS signing changes (#13421)
  • +
  • Sign individual files in package (#13392)
  • +
  • Use Authenticode certificate for MSIX signing (#13330)
  • +
  • Sign the MSIX files for the store (#12582)
  • +
  • Use temporary personal path at runspace startup when $env:HOME not defined (#13239)
  • +
  • Fix MSIX packaging to determine if a preview release by inspecting the semantic version string (#11991)
  • +
  • Add default help content to the assets folder (#13257)
  • +
+ +
+ +[7.0.4]: https://github.com/PowerShell/PowerShell/compare/v7.0.3...v7.0.4 + +## [7.0.3] - 2020-07-16 + +### Tests + +- Remove dependency on DNS for `Test-Connection` tests on macOS (#12943) + +### Build and Packaging Improvements + +
+ +
    +
  • Fix Azure file copy issues in release build by fixing the path to upload directory content (#13182)
  • +
  • Update .NET Core to 3.1.6 (Internal 12005)
  • +
  • Fix Azure file copy break in AzDevOps by updating task version to latest (#13173)
  • +
+ +
+ +[7.0.3]: https://github.com/PowerShell/PowerShell/compare/v7.0.2...v7.0.3 + +## [7.0.2] - 2020-06-11 + +### Engine Updates and Fixes + +- Ensure null-coalescing LHS is evaluated only once (#12667) +- Restrict loading of `amsi.dll` to `system32` folder (#12730) + +### General Cmdlet Updates and Fixes + +- Change `Get-FileHash` to close file handles before writing output (#12474) (Thanks @iSazonov!) + +### Tools + +- Update the PowerShell team list to correct changelog generation (#12927) + +### Tests + +- Pin major Pester version to 4 to prevent breaking changes caused by upcoming release of `v5` (#12262) (Thanks @bergmeister!) + +### Build and Packaging Improvements + +
+ + + +

Update to .NET Core 3.1.5

+ +
+ +
    +
  • Bump to .NET 3.1.5 and update dependencies (Internal 11699)
  • +
  • Check if Azure Blob exists before overwriting (#12921)
  • +
  • Upgrade APIScan version (#12876)
  • +
  • Fix break in package build by pinning ffi version to 1.12 (#12889)
  • +
  • Update the build to sign any unsigned files as 3rd party Dlls (#12581)
  • +
+ +
+ +## [7.0.1] - 2020-05-14 + +### Engine Updates and Fixes + +- Discover assemblies loaded by `Assembly.Load(byte[])` and `Assembly.LoadFile` (#12203) +- Allow case insensitive paths for determining `PSModulePath` (#12192) + +### General Cmdlet Updates and Fixes + +- Add `null` check for Windows PowerShell install path (#12296) +- Fix Null Reference error in CSV commands (#12281) (Thanks @iSazonov!) +- Fix `WinCompat` module loading to treat Core edition modules higher priority (#12269) +- Fix `` detection regex in web cmdlets (#12099) (Thanks @vexx32!) +- Miscellaneous minor updates to `WinCompat` (#11980) +- Fix `ConciseView` where error message is wider than window width and doesn't have whitespace (#11880, #11746) +- Make `Test-Connection` always use the default synchronization context for sending ping requests (#11517) + +### Tests + +- Fix CIM tab complete test failure (#12636) + +### Build and Packaging Improvements + +
+ + +Move to .NET Core 3.1.202 SDK and update packages. + + +
    +
  • Use dotnet core 3.1.202 (Internal 11551)
  • +
  • Bump PowerShellGet from 2.2.3 to 2.2.4 (#12342)
  • +
  • Move to standard internal pool for building (#12119)
  • +
  • Bump NJsonSchema from 10.1.5 to 10.1.7 (#12050)
  • +
+ +
+ +### Documentation and Help Content + +- Remove the version number of PowerShell from `LICENSE` (#12019) + +## [7.0.0] - 2020-03-04 + +### General Cmdlet Updates and Fixes + +- Enable `Ctrl+C` to work for global tool (#11959) +- Fix `ConciseView` to not show the line information within the error messages (#11952) + +### Build and Packaging Improvements + +- Publish PowerShell into the Windows engineering system package format (#11960) +- Bump .NET core framework to `3.1.2` (#11963) +- Ensure the man page `gzip` has the correct name for LTS release (#11956) +- Bump `Microsoft.ApplicationInsights` from `2.13.0` to `2.13.1` (#11925) + +## [7.0.0-rc.3] - 2020-02-21 + +### Breaking Changes + +- Fix `Invoke-Command` missing error on session termination (#11586) + +### Engine Updates and Fixes + +- Update the map between console color to `VT` sequences (#11891) +- Fix SSH remoting error on Windows platform (#11907) +- Restore the `PowerShellStreamType` `enum` with an `ObsoleteAttribute` (#11836) +- Handle cases where `CustomEvent` was not initially sent (#11807) +- Fix how COM objects are enumerated (#11795) +- Fix `NativeDllHandler` to not throw when file is not found (#11787) +- Restore `SetBreakpoints` API (#11622) +- Do not needlessly pass `-l login_name` or `-p port` to `ssh` (#11518) (Thanks @LucaFilipozzi!) +- Fix for `JEA` user role in virtual account (#11668) +- Do not resolve types from assemblies that are loaded in separate `AssemblyLoadContext` (#11088) + +### General Cmdlet Updates and Fixes + +- Sync current directory in `WinCompat` remote session (#11809) +- Add `WinCompat` deny list support using a setting in `powershell.config.json` (#11726) +- Fix unnecessary trimming of line resulting in incorrect index with `ConciseView` (#11670) + +### Code Cleanup + +- Change name of `ClrVersion` parameter back to revert change in capitalization (#11623) + +### Tools + +- Update changelog generation script (#11736) (Thanks @xtqqczze!) +- Update to `CredScan v2` (#11765) + +### Tests + +- Make sure to test whether we skip a test using consistent logic (#11892) +- Skip directory creation at root test on macOS (#11878) +- Update `Get-PlatformInfo` helper and tests for Debian 10, 11 and CentOS 8 (#11842) +- Ensure correct `pwsh` is used for test runs (#11486) (Thanks @iSazonov!) + +### Build and Packaging Improvements + +- Add `LTSRelease` value from `metadata.json` to `release.json` (#11897) +- Bump `Microsoft.ApplicationInsights` from `2.12.1` to `2.13.0` (#11894) +- Make LTS package always not a preview (#11895) +- Bump `System.Data.SqlClient` from `4.8.0` to `4.8.1` (#11879) +- Change `LTSRelease` value in `metadata.json` to true for `RC.3` release (Internal 10960) +- Update `LTS` logic to depend on `metadata.json` (#11877) +- Set default value of `LTSRelease` to false (#11874) +- Refactor packaging pipeline (#11852) +- Make sure `LTS` packages have symbolic links for `pwsh` and `pwsh-lts` (#11843) +- Bump `Microsoft.PowerShell.Native` from `7.0.0-rc.2` to `7.0.0` (#11839) +- Update the NuGet package generation to include `cimcmdlet.dll` and most of the built-in modules (#11832) +- Bump `Microsoft.PowerShell.Archive` from `1.2.4.0` to `1.2.5` (#11833) +- Bump `PSReadLine` from `2.0.0-rc2` to `2.0.0` (#11831) +- Add trace source and serialization primitives to the allowed assembly list (Internal 10911) +- Update the `NextReleaseTag` to be v7.0.0-preview.7 (#11372) +- Change packaging to produce `LTS` packages (#11772) +- Build tar packages only when building on Ubuntu (#11766) +- Bump `NJsonSchema` from `10.1.4` to `10.1.5` (#11730) +- Fix symbolic link creation in `packaging.psm1` (#11723) +- Bump `Microsoft.ApplicationInsights` from `2.12.0` to `2.12.1` (#11708) +- Bump `NJsonSchema` from `10.1.3` to `10.1.4` (#11620) +- Move to latest Azure DevOps agent images (#11704) +- Bump `Markdig.Signed` from `0.18.0` to `0.18.1` (#11641) + +### Documentation and Help Content + +- Add links to diffs on Github in changelog (#11652) (Thanks @xtqqczze!) +- Fix markdown-link test failure (#11653) (Thanks @xtqqczze!) + +## [7.0.0-rc.2] - 2020-01-16 + +### Breaking Changes +- Use `ISOWeek` for week numbers in `Get-Date` accounting for leap years #11536 (Thanks @paalbra!) + +### Engine Updates and Fixes +- Revert the PRs that made `DBNull.Value` and `NullString.Value` treated as `$null` (#11584) +- Support expanding `~` in `$env:PATH` when doing command discovery (#11552) +- Skip null data in output data received handler to fix a `NullReferenceException` (#11448) (Thanks @iSazonov!) +- Add `ssh` parameter sets for the parameter `-JobName` in `Invoke-Command` (#11444) +- Adding `PowerShell Editor Services` and `PSScriptAnalyzer` to tracked modules (#11514) +- Fix condition when key exchange stops responding with `SecureString` for the `OutOfProc` transports (#11380, #11406) +- Add setting to disable the implicit `WinPS` module loading (#11332) + +### General Cmdlet Updates and Fixes +- Fix `NullReferenceException` in `ConciseView` (#11435) (Thanks @iSazonov!) +- Remove the default value of `$true` for the parameter `-RequireLicenseAcceptance` in `New-ModuleManifest` (#11512) (Thanks @ThomasNieto!) +- Make Web Cmdlets skip processing the content headers with a null or empty value for backward compatibility (#11421) (Thanks @spongemike2!) +- Don't format exceptions that are not `ErrorRecord` objects (#11415) +- Mark `InitialSessionState.ImportPSSnapIn` as Obsolete (#11399) +- Use `PositionMessage` for the line context information for `ConciseView` (#11398) +- Add trailing line number to `filename` for `ConciseView` (#11391) +- Update `HelpInfoUri` for all modules in PowerShell 7.0 (#11389) +- Remove unnecessary newline in `ConciseView` (#11383) +- Move `Set-StrictMode` to the outer script block for `ErrorView` (#11381) +- Remove the declaration of `Get-Error` experimental feature from module manifest (#11369) +- Update error message if `Update-Help` fails for the current `UICulture` (#11356) +- `Test-Connection`: Fallback to hop IP Address on `-Traceroute` without `-ResolveDestination` (#11335) (Thanks @vexx32!) +- Add null host name check in `WSMan` (#11288) (Thanks @iSazonov!) +- Add `Type` member to exceptions containing type of exception for `Get-Error` (#11076) +- Write an error if argument is a directory in `Get-FileHash` cmdlet (#11114) (Thanks @iSazonov!) +- Update `Get-Error` to not modify the original `$Error` object (#11125) + +### Code Cleanup +- Use .NET code to check for processor architecture instead of P/Invoke (#11046) (Thanks @iSazonov!) + +### Tests +- Test fixes for various platforms (#11579, #11541) +- Various test fixes for debugger and remoting (#11528) +- `DSC` test fixes for `Alpine` and `Raspbian` (#11508) +- Normalize line endings before comparing string in tests (#11499) +- Fix `ssh` remoting test to work on all platforms (#11500) +- Build test artifacts for `Alpine` (#11483) +- Make null member access tests as string to avoid parsing errors (#11385) +- Fix test failing when `UnixStat` feature is disabled (#11370) +- Update hosting tests to use the SDK version from the build property (#11368) +- Add retry to `Enter-PSHostProcess` test (#11360) + +### Build and Packaging Improvements +- Bump `Microsoft.PowerShell.Native` from `7.0.0-rc.1` to `7.0.0.rc.2` (#11583) +- Update .NET SDK version to 3.1.101 (#11582) +- Bump `PSReadLine` from `2.0.0-rc1` to `2.0.0-rc2` (#11581) +- Bump `NJsonSchema` from `10.0.28` to `10.1.3` (#11382, #11573) +- Generate the correct reference assembly for `Microsoft.PowerShell.ConsoleHost` NuGet package (#11545) +- Update building of `MSIX` for `RC` to use 100 range revision (#11526) +- Fix symbolic links on Debian 10 packages (#11474) +- Bump `Microsoft.PowerShell.Archive` from `1.2.3.0` to `1.2.4.0` (#11502) +- Add script to rebuild `WIX` component references (#11485) +- Bump `PackageManagement` from `1.4.5` to `1.4.6` (#11427) +- Bump `PowerShellGet` from `2.2.2` to `2.2.3` (#11426) +- Bump `ThreadJob` from `2.0.2` to `2.0.3` (#11416) +- Fix symbolic links to `libs` on Debian 10 (#11390) +- Improve Ubuntu detection for Ubuntu derivatives like `GalliumOS` etc (#11155) + +### Documentation and Help Content +- Fix broken link in debugging `README.md` (#11503) + +## [7.0.0-rc.1] - 2019-12-16 + +### Breaking Changes +- Make update notification support `LTS` and default channels (#11132) + +### Engine Updates and Fixes +- Improvements in breakpoint APIs for remote scenarios (#11312) +- Fix PowerShell class definition leaking into another Runspace (#11273) +- Fix a regression in formatting caused by the `FirstOrDefault` primitive added in `7.0.0-Preview1` (#11258) +- Additional Microsoft Modules to track in `PS7` Telemetry (#10751) +- Make approved features non-experimental (#11303) +- Update `ConciseView` to use `TargetObject` if applicable (#11075) +- Fix `NullReferenceException` in `CompletionCompleters` public methods (#11274) +- Fix apartment thread state check on non-Windows platforms (#11301) +- Update setting `PSModulePath` to concatenate the process and machine environment variables (#11276) +- Bump `.NET Core` to `3.1.0` (#11260) +- Fix detection of `$PSHOME` in front of `$env:PATH` (#11141) + +### General Cmdlet Updates and Fixes +- Fix for issue on Raspbian for setting date of file changes in `UnixStat` Experimental Feature (#11313) +- Add `-AsPlainText` to `ConvertFrom-SecureString` (#11142) +- Added `WindowsPS` version check for `WinCompat` (#11148) +- Fix error-reporting in some `WinCompat` scenarios (#11259) +- Add native binary resolver (#11032) (Thanks @iSazonov!) +- Update calculation of char width to respect `CJK` chars correctly (#11262) +- Add `Unblock-File` for macOS (#11137) +- Fix regression in `Get-PSCallStack` (#11210) (Thanks @iSazonov!) +- Avoid automatically loading the `ScheduledJob` module when using Job cmdlets (#11194) +- Add `OutputType` to `Get-Error` cmdlet and preserve original `TypeNames` (#10856) +- Fix null reference in `SupportsVirtualTerminal` property (#11105) + +### Code Cleanup +- Change comment and element text to meet Microsoft standards (#11304) + +### Tests +- Make unreliable `DSC` test pending (#11131) + +### Build and Packaging Improvements +- Fix Nuget package signing for Coordinated Package build (#11316) +- Update dependencies from PowerShell Gallery and NuGet (#11323) +- Bump `Microsoft.ApplicationInsights` from `2.11.0` to `2.12.0` (#11305) +- Bump `Microsoft.CodeAnalysis.CSharp` from `3.3.1` to `3.4.0` (#11265) +- Updates packages for Debian 10 and 11 (#11236) +- Only enable experimental features prior to `RC` (#11162) +- Update macOS minimum version (#11163) +- Bump `NJsonSchema` from `10.0.27` to `10.0.28` (#11170) + +### Documentation and Help Content +- Refactor change logs into one log per release (#11165) +- Fix `FWLinks` for PowerShell 7 online help documents (#11071) + +## [7.0.0-preview.6] - 2019-11-21 + +### Breaking Changes + +- Update `Test-Connection` to work more like the one in Windows PowerShell (#10697) (Thanks @vexx32!) +- Preserve `$?` for `ParenExpression`, `SubExpression` and `ArrayExpression` (#11040) +- Set working directory to current directory in `Start-Job` (#10920) (Thanks @iSazonov!) + +### Engine Updates and Fixes + +- Allow `pwsh` to inherit `$env:PSModulePath` and enable `powershell.exe` to start correctly (#11057) + +### Experimental Features + +- Provide Unix stat information in filesystem output (#11042) +- Support null-conditional operators `?.` and `?[]` in PowerShell language (#10960) +- Support using non-compatible Windows PowerShell modules in PowerShell Core (#10973) + +### Performance + +- Avoid using closure in `Parser.SaveError` (#11006) +- Improve the caching when creating new `Regex` instances (#10657) (Thanks @iSazonov!) +- Improve processing of the PowerShell built-in type data from `types.ps1xml`, `typesV3.ps1xml` and `GetEvent.types.ps1xml` (#10898) +- Update `PSConfiguration.ReadValueFromFile` to make it faster and more memory efficient (#10839) + +### General Cmdlet Updates and Fixes + +- Add limit check in `Get-WinEvent` (#10648) (Thanks @iSazonov!) +- Fix command runtime so `StopUpstreamCommandsException` doesn't get populated in `-ErrorVariable` (#10840) +- Set the output encoding to `[Console]::OutputEncoding` for native commands (#10824) +- Support multi-line code blocks in examples (#10776) (Thanks @Greg-Smulko!) +- Add Culture parameter to `Select-String` cmdlet (#10943) (Thanks @iSazonov!) +- Fix `Start-Job` working directory path with trailing backslash (#11041) +- `ConvertFrom-Json`: Unwrap collections by default (#10861) (Thanks @danstur!) +- Use case-sensitive Hashtable for `Group-Object` cmdlet with `-CaseSensitive` and `-AsHashtable` switches (#11030) (Thanks @vexx32!) +- Handle exception if enumerating files fails when rebuilding path to have correct casing (#11014) +- Fix `ConciseView` to show `Activity` instead of `myCommand` (#11007) +- Allow web cmdlets to ignore HTTP error statuses (#10466) (Thanks @vdamewood!) +- Fix piping of more than one `CommandInfo` to `Get-Command` (#10929) +- Add back `Get-Counter` cmdlet for Windows (#10933) +- Make `ConvertTo-Json` treat `[AutomationNull]::Value` and `[NullString]::Value` as `$null` (#10957) +- Remove brackets from `ipv6` address for SSH remoting (#10968) +- Fix crash if command sent to pwsh is just whitespace (#10977) +- Added cross-platform `Get-Clipboard` and `Set-Clipboard` (#10340) +- Fix setting original path of filesystem object to not have extra trailing slash (#10959) +- Support `$null` for `ConvertTo-Json` (#10947) +- Add back `Out-Printer` command on Windows (#10906) +- Fix `Start-Job -WorkingDirectory` with whitespace (#10951) +- Return default value when getting `null` for a setting in `PSConfiguration.cs` (#10963) (Thanks @iSazonov!) +- Handle IO exception as non-terminating (#10950) +- Add `GraphicalHost` assembly to enable `Out-GridView`, `Show-Command`, and `Get-Help -ShowWindow` (#10899) +- Take `ComputerName` via pipeline in `Get-HotFix` (#10852) (Thanks @kvprasoon!) +- Fix tab completion for parameters so that it shows common parameters as available (#10850) +- Fix `GetCorrectCasedPath()` to first check if any system file entries is returned before calling `First()` (#10930) +- Set working directory to current directory in `Start-Job` (#10920) (Thanks @iSazonov!) +- Change `TabExpansion2` to not require `-CursorColumn` and treat as `$InputScript.Length` (#10849) +- Handle case where Host may not return Rows or Columns of screen (#10938) +- Fix use of accent colors for hosts that don't support them (#10937) +- Add back `Update-List` command (#10922) +- Update `FWLink` Id for `Clear-RecycleBin` (#10925) +- During tab completion, skip file if can't read file attributes (#10910) +- Add back `Clear-RecycleBin` for Windows (#10909) +- Add `$env:__SuppressAnsiEscapeSequences` to control whether to have VT escape sequence in output (#10814) + +### Code Cleanup + +- Cleanup style issues in `Compiler.cs` (#10368) (Thanks @iSazonov!) +- Remove the unused type converter for `CommaDelimitedStringCollection` (#11000) (Thanks @iSazonov!) +- Cleanup style in `InitialSessionState.cs` (#10865) (Thanks @iSazonov!) +- Code clean up for `PSSession` class (#11001) +- Remove the not-working 'run `Update-Help` from `Get-Help` when `Get-Help` runs for the first time' feature (#10974) +- Fix style issues (#10998) (Thanks @iSazonov!) +- Cleanup: use the built-in type alias (#10882) (Thanks @iSazonov!) +- Remove the unused setting key `ConsolePrompting` and avoid unnecessary string creation when querying `ExecutionPolicy` setting (#10985) +- Disable update notification check for daily builds (#10903) (Thanks @bergmeister!) +- Reinstate debugging API lost in #10338 (#10808) + +### Tools + +- Add default setting for the `SDKToUse` property so that it builds in VS (#11085) +- `Install-Powershell.ps1`: Add parameter to use MSI installation (#10921) (Thanks @MJECloud!) +- Add basic examples for `install-powershell.ps1` (#10914) (Thanks @kilasuit!) + +### Tests + +- Fix `stringdata` test to correctly validate keys of hashtables (#10810) +- Unload test modules (#11061) (Thanks @iSazonov!) +- Increase time between retries of testing URL (#11015) +- Update tests to accurately describe test actions. (#10928) (Thanks @romero126!) + +### Build and Packaging Improvements + +- Updating links in `README.md` and `metadata.json` for Preview.5 (#10854) +- Select the files for compliance tests which are owned by PowerShell (#10837) +- Allow `win7x86` `msix` package to build. (Internal 10515) +- Allow semantic versions to be passed to `NormalizeVersion` function (#11087) +- Bump .NET core framework to `3.1-preview.3` (#11079) +- Bump `PSReadLine` from `2.0.0-beta5` to `2.0.0-beta6` in /src/Modules (#11078) +- Bump `Newtonsoft.Json` from `12.0.2` to `12.0.3` (#11037) (#11038) +- Add Debian 10, 11 and CentOS 8 packages (#11028) +- Upload `Build-Info` Json file with the `ReleaseDate` field (#10986) +- Bump .NET core framework to `3.1-preview.2` (#10993) +- Enable build of x86 MSIX package (#10934) +- Update the dotnet SDK install script URL in `build.psm1` (#10927) +- Bump `Markdig.Signed` from `0.17.1` to `0.18.0` (#10887) +- Bump `ThreadJob` from `2.0.1` to `2.0.2` (#10886) +- Update `AppX` Manifest and Packaging module to conform to MS Store requirements (#10878) + +### Documentation and Help Content + +- Update `CONTRIBUTING.md` (#11096) (Thanks @mklement0!) +- Fix installation doc links in `README.md` (#11083) +- Adds examples to `install-powershell.ps1` script (#11024) (Thanks @kilasuit!) +- Fix to `Select-String` emphasis and `Import-DscResource` in CHANGELOG.md (#10890) +- Remove the stale link from `powershell-beginners-guide.md` (#10926) + +## [7.0.0-preview.5] - 2019-10-23 + +### Breaking Changes + +- Make `$PSCulture` consistently reflect in-session culture changes (#10138) (Thanks @iSazonov!) + +### Engine Updates and Fixes + +- Move to `.NET Core 3.1 preview 1` (#10798) +- Refactor reparse tag checks in file system provider (#10431) (Thanks @iSazonov!) +- Replace `CR` and new line with a `0x23CE` character in script logging (#10616) +- Fix a resource leak by unregistering the event handler from `AppDomain.CurrentDomain.ProcessExit` (#10626) + +### Experimental Features + +- Implement `Get-Error` cmdlet as Experimental Feature (#10727,#10800) +- Add `ConciseView` for `$ErrorView` and update it to remove unnecessary text and not color entire line in red (#10641,#10724) +- Support the pipeline chain operators `&&` and `||` in PowerShell language (#9849,#10825,#10836) +- Implement null coalescing (`??`) and null coalescing assignment (`??=`) operators (#10636) +- Support notification on `pwsh` startup when a new release is available and update notification message (#10689,#10777) + +### General Cmdlet Updates and Fixes + +- Add emphasis to `Select-String` output (with `-NoEmphasis` parameter to opt-out) (#8963) (Thanks @derek-xia!) +- Add back `Get-HotFix` cmdlet (#10740) +- Make `Add-Type` usable in applications that host `PowerShell` (#10587) +- Use more effective evaluation order in `LanguagePrimitives.IsNullLike()` (#10781) (Thanks @vexx32!) +- Improve handling of mixed-collection piped input and piped streams of input in `Format-Hex` (#8674) (Thanks @vexx32!) +- Use type conversion in `SSHConnection` hashtables when value doesn't match expected type (#10720) (Thanks @SeeminglyScience!) +- Fix `Get-Content -ReadCount 0` behavior when `-TotalCount` is set (#10749) (Thanks @eugenesmlv!) +- Reword access denied error message in `Get-WinEvent` (#10639) (Thanks @iSazonov!) +- Enable tab completion for variable assignment that is enum or type constrained (#10646) +- Remove unused `SourceLength` remoting property causing formatting issues (#10765) +- Add `-Delimiter` parameter to `ConvertFrom-StringData` (#10665) (Thanks @steviecoaster!) +- Add positional parameter for `ScriptBlock` when using `Invoke-Command` with `SSH` (#10721) (Thanks @machgo!) +- Show line context information if multiple lines but no script name for `ConciseView` (#10746) +- Add support for `\\wsl$\` paths to file system provider (#10674) +- Add the missing token text for `TokenKind.QuestionMark` in parser (#10706) +- Set current working directory of each `ForEach-Object -Parallel` running script to the same location as the calling script. (#10672) +- Replace `api-ms-win-core-file-l1-2-2.dll` with `Kernell32.dll` for `FindFirstStreamW` and `FindNextStreamW` APIs (#10680) (Thanks @iSazonov!) +- Tweak help formatting script to be more `StrictMode` tolerant (#10563) +- Add `-SecurityDescriptorSDDL` parameter to `New-Service` (#10483) (Thanks @kvprasoon!) +- Remove informational output, consolidate ping usage in `Test-Connection` (#10478) (Thanks @vexx32!) +- Read special reparse points without accessing them (#10662) (Thanks @iSazonov!) +- Direct `Clear-Host` output to terminal (#10681) (Thanks @iSazonov!) +- Add back newline for grouping with `Format-Table` and `-Property` (#10653) +- Remove [ValidateNotNullOrEmpty] from `-InputObject` on `Get-Random` to allow empty string (#10644) +- Make suggestion system string distance algorithm case-insensitive (#10549) (Thanks @iSazonov!) +- Fix null reference exception in `ForEach-Object -Parallel` input processing (#10577) + +### Code Cleanup + +- Remove `WorkflowJobSourceAdapter` reference that is no longer used (#10326) (Thanks @KirkMunro!) +- Cleanup `COM` interfaces in jump list code by fixing `PreserveSig` attributes (#9899) (Thanks @weltkante!) +- Add a comment describing why `-ia` is not the alias for `-InformationAction` common parameter (#10703) (Thanks @KirkMunro!) +- Rename `InvokeCommandCmdlet.cs` to `InvokeExpressionCommand.cs` (#10659) (Thanks @kilasuit!) +- Add minor code cleanups related to update notifications (#10698) +- Remove deprecated workflow logic from the remoting setup scripts (#10320) (Thanks @KirkMunro!) +- Update help format to use proper case (#10678) (Thanks @tnieto88!) +- Clean up `CodeFactor` style issues coming in commits for the last month (#10591) (Thanks @iSazonov!) +- Fix typo in description of `PSTernaryOperator` experimental feature (#10586) (Thanks @bergmeister!) + +### Performance + +- Add minor performance improvements for runspace initialization (#10569) (Thanks @iSazonov!) + +### Tools + +- Make `Install-PowerShellRemoting.ps1` handle empty string in `PowerShellHome` parameter (#10526) (Thanks @Orca88!) +- Switch from `/etc/lsb-release` to `/etc/os-release` in `install-powershell.sh` (#10773) (Thanks @Himura2la!) +- Check `pwsh.exe` and `pwsh` in daily version on Windows (#10738) (Thanks @centreboard!) +- Remove unneeded tap in `installpsh-osx.sh` (#10752) + +### Tests + +- Temporary skip the flaky test `TestAppDomainProcessExitEvenHandlerNotLeaking` (#10827) +- Make the event handler leaking test stable (#10790) +- Sync capitalization in `CI` `YAML` (#10767) (Thanks @RDIL!) +- Add test for the event handler leaking fix (#10768) +- Add `Get-ChildItem` test (#10507) (Thanks @iSazonov!) +- Replace ambiguous language for tests from `switch` to `parameter` for accuracy (#10666) (Thanks @romero126!) + +### Build and Packaging Improvements + +- Update package reference for `PowerShell SDK` to `preview.5` (Internal 10295) +- Update `ThirdPartyNotices.txt` (#10834) +- Bump `Microsoft.PowerShell.Native` to `7.0.0-preview.3` (#10826) +- Bump `Microsoft.ApplicationInsights` from `2.10.0` to `2.11.0` (#10608) +- Bump `NJsonSchema` from `10.0.24` to `10.0.27` (#10756) +- Add `MacPorts` support to the build system (#10736) (Thanks @Lucius-Q-User!) +- Bump `PackageManagement` from `1.4.4` to `1.4.5` (#10728) +- Bump `NJsonSchema` from `10.0.23` to `10.0.24` (#10635) +- Add environment variable to differentiate client/server telemetry in `MSI` (#10612) +- Bump `PSDesiredStateConfiguration` from `2.0.3` to `2.0.4` (#10603) +- Bump `Microsoft.CodeAnalysis.CSharp` from `3.2.1` to `3.3.1` (#10607) +- Update to `.Net Core 3.0 RTM` (#10604) (Thanks @bergmeister!) +- Update `MSIX` packaging so the version to `Windows Store` requirements (#10588) + +### Documentation and Help Content + +- Merge stable and servicing change logs (#10527) +- Update used `.NET` version in build docs (#10775) (Thanks @Greg-Smulko!) +- Replace links from `MSDN` to `docs.microsoft.com` in `powershell-beginners-guide.md` (#10778) (Thanks @iSazonov!) +- Fix broken `DSC` overview link (#10702) +- Update `Support_Question.md` to link to `Stack Overflow` as another community resource (#10638) (Thanks @mklement0!) +- Add processor architecture to distribution request template (#10661) +- Add new PowerShell MoL book to learning PowerShell docs (#10602) + +## [7.0.0-preview.4] - 2019-09-19 + +### Engine Updates and Fixes + +- Add support to `ActionPreference.Break` to break into debugger when `Debug`, `Error`, `Information`, `Progress`, `Verbose` or `Warning` messages are generated (#8205) (Thanks @KirkMunro!) +- Enable starting control panel add-ins within PowerShell Core without specifying `.CPL` extension. (#9828) + +### Performance + +- Make `ForEach-Object` faster for its commonly used scenarios (#10454) and fix `ForEach-Object -Parallel` performance problem with many runspaces (#10455) + +### Experimental Features + +- Update `PSDesiredStateConfiguration` module version to `2.0.3` and bring new tests; enable compilation to MOF on non-Windows and use of Invoke-DSCResource without LCM (#10516) +- Add APIs for breakpoint management in runspaces and enable attach to process without `BreakAll` for PowerShell Editor Services (#10338) (Thanks @KirkMunro!) +- Support [ternary operator](https://github.com/PowerShell/PowerShell-RFC/pull/218) in PowerShell language (#10367) + +### General Cmdlet Updates and Fixes + +- Add PowerShell Core group policy definitions (#10468) +- Update console host to support `XTPUSHSGR`/`XTPOPSGR` VT control sequences that are used in [composability scenarios](https://github.com/microsoft/terminal/issues/1796). (#10208) +- Add `WorkingDirectory` parameter to `Start-Job` (#10324) (Thanks @davinci26!) +- Remove the event handler that was causing breakpoint changes to be erroneously replicated to the host runspace debugger (#10503) (Thanks @KirkMunro!) +- Replace `api-ms-win-core-job-12-1-0.dll` with `Kernell32.dll` in `Microsoft.PowerShell.Commands.NativeMethods` P/Invoke API(#10417) (Thanks @iSazonov!) +- Fix wrong output for `New-Service` in variable assignment and `-OutVariable` (#10444) (Thanks @kvprasoon!) +- Fix global tool issues around exit code, command line parameters and path with spaces (#10461) +- Fix recursion into OneDrive - change `FindFirstFileEx()` to use `SafeFindHandle` type (#10405) +- Skip auto-loading `PSReadLine` on Windows if the [NVDA screen reader](https://www.nvaccess.org/about-nvda/) is active (#10385) +- Increase built-with-PowerShell module versions to `7.0.0.0` (#10356) +- Add throwing an error in `Add-Type` if a type with the same name already exists (#9609) (Thanks @iSazonov!) + +### Code Cleanup + +- Convert `ActionPreference.Suspend` enumeration value into a non-supported, reserved state, and remove restriction on using `ActionPreference.Ignore` in preference variables (#10317) (Thanks @KirkMunro!) +- Replace `ArrayList` with `List` to get more readable and reliable code without changing functionality (#10333) (Thanks @iSazonov!) +- Make code style fixes to `TestConnectionCommand` (#10439) (Thanks @vexx32!) +- Cleanup `AutomationEngine` and remove extra `SetSessionStateDrive` method call (#10416) (Thanks @iSazonov!) +- Rename default `ParameterSetName` back to `Delimiter` for `ConvertTo-Csv` and `ConvertFrom-Csv` (#10425) + +### Tools + +- Update `install-powershell.ps1` to check for already installed daily build (#10489) + +### Tests + +- Add experimental check to `ForEach-Object -Parallel` tests (#10354) (Thanks @KirkMunro!) +- Update tests for Alpine validation (#10428) + +### Build and Packaging Improvements + +- Bump `PowerShellGet` version from `2.2` to `2.2.1` (#10382) +- Bump `PackageManagement` version from `1.4.3` to `1.4.4` (#10383) +- Update `README.md` and `metadata.json` for `7.0.0-preview.4` (Internal 10011) +- Upgrade `.Net Core 3.0` version from `Preview 9` to `RC1` (#10552) (Thanks @bergmeister!) +- Fix `ExperimentalFeature` list generation (Internal 9996) +- Bump `PSReadLine` version from `2.0.0-beta4` to `2.0.0-beta5` (#10536) +- Fix release build script to set release tag +- Update version of `Microsoft.PowerShell.Native` to `7.0.0-preview.2` (#10519) +- Upgrade to `Netcoreapp3.0 preview9` (#10484) (Thanks @bergmeister!) +- Make sure the daily coordinated build, knows it is a daily build (#10464) +- Update the combined package build to release the daily builds (#10449) +- Remove appveyor reference (#10445) (Thanks @RDIL!) +- Bump `NJsonSchema` version from `10.0.22` to `10.0.23` (#10421) +- Remove the deletion of `linux-x64` build folder because some dependencies for Alpine need it (#10407) + +### Documentation and Help Content + +- Update `README.md` and metadata for `v6.1.6` and `v6.2.3` releases (#10523) +- Fix a typo in `README.md` (#10465) (Thanks @vedhasp!) +- Add a reference to `PSKoans` module to Learning Resources documentation (#10369) (Thanks @vexx32!) +- Update `README.md` and `metadata.json` for `7.0.0-preview.3` (#10393) + +## [7.0.0-preview.3] - 2019-08-20 + +### Breaking Changes + +- Remove `kill` alias for `Stop-Process` cmdlet on Unix (#10098) (Thanks @iSazonov!) +- Support for starting PowerShell as a login shell (`pwsh -Login` / `pwsh -l`) support (#10050) + +### Engine Updates and Fixes + +- Additional Telemetry - implementation of [`RFC0036`](https://github.com/PowerShell/PowerShell-RFC/pull/158) (#10336) +- Implement `ForEach-Object -Parallel` as an experimental feature (#10229) +- Skip `JumpList` on `NanoServer` and `IoT` (#10164) +- Make `Get-DscResource` work with class based resources (#10350) +- Fix `#requires -version` for `pwsh` 7 to include `6.1` and `6.2` in `PSCompatibleVersions` (#9943) (Thanks @bgelens!) +- Add dispose of `_runspaceDebugCompleteEvent` event object. (#10323) +- Fix performance regression from disabling debugger in system lockdown mode (#10269) +- Special case the `posix` locale in `WildcardPattern` (#10186) +- Use `Platform.IsWindowsDesktop` instead of checking both NanoServer and IoT (#10205) + +### General Cmdlet Updates and Fixes + +- Enable Experimental Features by default on Preview builds (#10228) +- Enable `-sta` and `-mta` switches for `pwsh` (`-sta` is required for `GUIs`) (#10061) +- Make breakpoints display better over PowerShell remoting (#10339) (Thanks @KirkMunro!) +- Add support for `AppX` reparse points (#10331) +- Make module name matching for `get-module -FullyQualifiedName` case insensitive (#10329) +- Expose `PreRelease` label in `PSModuleInfo` formatter (#10316) +- Add `-Raw` switch to `Select-String` which allows returning only the string that was matched (#9901) (Thanks @Jawz84!) + +- ### Performance + +- Reduce allocations in `MakePath()` method (#10027) (Thanks @iSazonov!) +- Remove extra check that the system dll exists (#10244) (Thanks @iSazonov!) +- Avoid boxing when passing value type arguments to `PSTraceSource.WriteLine` (#10052) (Thanks @iSazonov!) +- Reduce allocations in `Escape()` and `Unescape()` (#10041) (Thanks @iSazonov!) + +### Code Cleanup + +- Add the license header to `nanoserver.tests.ps1` (#10171) +- Mark `-parallel` and `-throttlelimit` reserved for `foreach` and `switch` statements (#10328) (Thanks @KirkMunro!) +- Deprecate workflow debugging code (#10321) (Thanks @KirkMunro!) +- Fix style issues in `InternalCommands.cs` (#10352) (Thanks @iSazonov!) +- Deprecate internal `HelpCategory.Workflow` enumeration (#10319) (Thanks @KirkMunro!) +- Update `Microsoft.PowerShell.CoreCLR.Eventing` to resolve conflict with `System.Diagnostics.EventLog` (#10305) +- Don't collect process start time as it's not being used on `consolehost` startup (#10294) +- .NET Core 3.0 now aborts the thread for us. Remove the `ThreadAbortException` code (#10230) (Thanks @iSazonov!) +- Use `nameof()` in `LocationGlobber` and `PathInfo` (#10200) (Thanks @iSazonov!) + +### Tools + +- Fix Hungarian prefix `my` (#9976) (Thanks @RDIL!) +- Fix spelling error in issue template (#10256) +- Quote arguments in `.vscode/tasks.json` in case of spaces (#10204) (Thanks @msftrncs!) + +### Tests + +- Remove `markdownlint` tests due to security issues (#10163) +- Add tests for `WildcardPattern.Escape()` and `Unescape()` (#10090) (Thanks @iSazonov!) +- Cleanup Docker release testing (#10310) (Thanks @RDIL!) + +### Build and Packaging Improvements + +- Update `Microsoft.Management.Infrastructure` version to `2.0.0-preview.2` (#10366) +- Move to `.NET Core 3.0 preview.8` (#10351) (#10227) (Thanks @bergmeister!) +- Bump `NJsonSchema` from `10.0.21` to `10.0.22` (#10364) +- Add `Microsoft.PowerShell.CoreCLR.Eventing.dll` to exception list for build fix (#10337) +- Bump `Microsoft.CodeAnalysis.CSharp` from `3.1.0` to `3.2.1` (#10273) (#10330) +- Revert the temporary AzDevOps artifact workaround (#10260) +- Fix macOS build break (#10207) + +### Documentation and Help Content + +- Update docs for `7.0.0-preview.2` release (#10160) (#10176) +- `PSSA` also includes formatting (#10172) +- Refactor security policy documentation so that they appear in the Security policy tab of GitHub (#9905) (Thanks @bergmeister!) +- Add tooling section to PR template (#10144) +- Update `README.md` and `metadata.json` for next releases (#10087) +- Update DotNet Support links (#10145) +- Update our language on our policy applying to security issues (#10304) +- Update dead links from `powershell.com` (#10297) +- Create `Distribution_Request` issue template (#10253) +- Fix: Removed dependency file with `Dependabot` (#10212) (Thanks @RDIL!) + +## [7.0.0-preview.2] - 2019-07-17 + +### Breaking Changes + +- Cleanup workflow - remove `PSProxyJob` (#10083) (Thanks @iSazonov!) +- Disable `Enter-PSHostProcess` cmdlet when system in lock down mode (Internal 9168) + +### Engine Updates and Fixes + +- Consider `DBNull.Value` and `NullString.Value` the same as `$null` when comparing with `$null` and casting to bool (#9794) (Thanks @vexx32!) +- Allow methods to be named after keywords (#9812) (Thanks @vexx32!) +- Create `JumpList` in `STA` thread as some `COM` `APIs` are strictly `STA` only to avoid sporadic `CLR` crashes (#9928) (#10057) (Thanks @bergmeister!) +- Skip `JumpList` on `NanoServer` and `IoT` (#10164) +- Display `COM` method signature with argument names (#9858) (Thanks @nbkalex!) +- Use the original precision (prior-dotnet-core-3) for double/float-to-string conversion (#9893) +- `Import-DscResource` can now clobber built-in DSC resource names (#9879) +- Add ability to pass `InitialSessionState` to the `ConsoleShell.Start` (#9802) (Thanks @asrosent!) +- Have console host not enter command prompt mode when using `Read-Host -Prompt` (#9743) +- Fix use of `Start-Process http://bing.com` (#9793) +- Support negative numbers in `-split` operator (#8960) (Thanks @ece-jacob-scott!) + +### General Cmdlet Updates and Fixes + +- Support DSC compilation on Linux. (#9834) +- Add alias for Service `StartType` (#9940) (Thanks @NeoBeum!) +- Add `-SecurityDescriptorSddl` parameter to `Set-Service` (#8626) (Thanks @kvprasoon!) +- Fix auto-download of files when enumerating files from a `OneDrive` folder (#9895) +- Set request headers when request body is empty in Web Cmdlets (#10034) (Thanks @markekraus!) +- Fix wrong comparison in `CertificateProvider` (#9987) (Thanks @iSazonov!) +- Sync docs changes into the embedded help for `pwsh` (#9952) +- Display Duration when displaying `HistoryInfo` (#9751) (Thanks @rkeithhill!) +- Update console startup and help `url` for PowerShell docs (#9775) +- Make `UseAbbreviationExpansion` and `TempDrive` official features (#9872) +- Fix `Get-ChildItem -Path` with wildcard `char` (#9257) (Thanks @kwkam!) + +### Performance + +- Add another fast path to `WildcardPattern.IsMatch` for patterns that only have an asterisk in the end (#10054) (Thanks @iSazonov!) +- Move some of the creations of `WildcardPattern` in outer loop to avoid unnecessary allocation (#10053) (Thanks @iSazonov!) +- Make `Foreach-Object` 2 times faster by reducing unnecessary allocations and boxing (#10047) +- Use a static cache for `PSVersionInfo.PSVersion` to avoid casting `SemanticVersion` to `Version` every time accessing that property (#10028) +- Reduce allocations in `NavigationCmdletProvider.NormalizePath()` (#10038) (Thanks @iSazonov!) +- Add fast path for wildcard patterns that contains no wildcard characters (#10020) +- Avoid `Assembly.GetName()` in `ClrFacade.GetAssemblies(string)` to reduce allocations of `CultureInfo` objects (#10024) (Thanks @iSazonov!) +- Avoid the `int[]` and `int[,]` allocation when tokenizing line comments and matching wildcard pattern (#10009) + +### Tools + +- Update change log generation tool to deal with private commits (#10096) +- Update `Start-PSBuild -Clean` logic of `git clean` to ignore locked files from `VS2019` (#10071) (Thanks @bergmeister!) +- Indent fix in `markdown-link.tests.ps1` (#10049) (Thanks @RDIL!) +- `Start-PSBuild -Clean` does not remove all untracked files (#10022) (Thanks @vexx32!) +- Add module to support Pester tests for automating debugger commands (`stepInto`, `stepOut`, etc.), along with basic tests (#9825) (Thanks @KirkMunro!) +- Remove `markdownlint` tests due to security issues (#10163) + +### Code Cleanup + +- Cleanup `CompiledScriptBlock.cs` (#9735) (Thanks @vexx32!) +- Cleanup workflow code (#9638) (Thanks @iSazonov!) +- Use `AddOrUpdate()` instead of `Remove` then `Add` to register runspace (#10007) (Thanks @iSazonov!) +- Suppress `PossibleIncorrectUsageOfAssignmentOperator` rule violation by adding extra parenthesis (#9460) (Thanks @xtqqczze!) +- Use `AddRange` in `GetModules()` (#9975) (Thanks @iSazonov!) +- Code cleanup: use `IndexOf(char)` overload (#9722) (Thanks @iSazonov!) +- Move `consts` and methods to single `CharExtensions` class (#9992) (Thanks @iSazonov!) +- Cleanup: Use `EndsWith(char)` and `StartsWith(char)` (#9994) (Thanks @iSazonov!) +- Remove `LCIDToLocaleName` `P/Invoke` from `GetComputerInfoCommand` (#9716) (Thanks @iSazonov!) +- Cleanup Parser tests (#9792) (Thanks @vexx32!) +- Remove `EtwActivity` empty constructor and make minor style fixes (#9958) (Thanks @RDIL!) +- Fix style issues from last commits (#9937) (Thanks @iSazonov!) +- Remove dead code about `IsTransparentProxy` (#9966) +- Fix minor typos in code comments (#9917) (Thanks @RDIL!) +- Style fixes for `CimAsyncOperations` (#9945) (Thanks @RDIL!) +- Fix minor `CodeFactor` style issues in `ModuleCmdletBase` (#9915) (Thanks @RDIL!) +- Clean up the use of `SetProfileRoot` and `StartProfile` in ConsoleHost (#9931) +- Fix minor style issues come from last commits (#9640) (Thanks @iSazonov!) +- Improve whitespace for Parser tests (#9806) (Thanks @vexx32!) +- Use new `string.ConCat()` in `Process.cs` (#9720) (Thanks @iSazonov!) +- Code Cleanup: Tidy up `scriptblock.cs` (#9732) (Thanks @vexx32!) + +### Tests + +- Mark `Set-Service` tests with password as `Pending` (#10146) +- Fix test password generation rule to meet Windows complexity requirements (#10143) +- Add test for `New-Item -Force` (#9971) (Thanks @robdy!) +- Fix gulp versions (#9916) (Thanks @RDIL!) +- Indentation fixes in `ci.psm1` (#9947) (Thanks @RDIL!) +- Remove some `Travis-CI` references (#9919) (Thanks @RDIL!) +- Improve release testing Docker images (#9942) (Thanks @RDIL!) +- Use `yarn` to install global tools (#9904) (Thanks @RDIL!) +- Attempt to work around the zip download issue in Azure DevOps Windows CI (#9911) +- Update PowerShell SDK version for hosting tests (Internal 9185) + +### Build and Packaging Improvements + +- Update the target framework for reference assemblies to `netcoreapp3.0` (#9747) +- Pin version of `netDumbster` to `2.0.0.4` (#9748) +- Fix daily `CodeCoverageAndTest` build by explicitly calling `Start-PSBootStrap` (#9724) +- Split the `fxdependent` package on Windows into two packages (#10134) +- Bump `System.Data.SqlClient` (#10109) +- Bump `System.Security.AccessControl` (#10100) +- Add performance tag to change log command (Internal) +- Upgrade .Net Core 3 SDK from `preview5` to `preview6` and related out of band `Nuget` packages from `2.1` to `3.0-preview6` (#9888) (Thanks @bergmeister!) +- Add to `/etc/shells` on macOS (#10066) +- Bump `Markdig.Signed` from `0.17.0` to `0.17.1` (#10062) +- Update copyright symbol for `NuGet` packages (#9936) +- Download latest version `(6.2.0)` of `PSDesiredStateConfiguration` `nuget` package. (#9932) +- Add automated `RPM` signing to release build (#10013) +- Bump `ThreadJob` from `1.1.2` to `2.0.1` in `/src/Modules` (#10003) +- Bump `PowerShellGet` from `2.1.4` to `2.2` in /src/Modules (#9933) (#10085) +- Bump `PackageManagement` from `1.4` to `1.4.3` in `/src/Modules` (#9820) (#9918) (#10084) +- Update to use `TSAv2` (#9914) +- Bump `NJsonSchema` from `9.14.1` to `10.0.21` (#9805) (#9843) (#9854) (#9862) (#9875) (#9885) (#9954) (#10017) +- Bump `System.Net.Http.WinHttpHandler` from `4.5.3` to `4.5.4` (#9786) +- Bump `Microsoft.ApplicationInsights` from `2.9.1` to `2.10.0` (#9757) +- Increase timeout of NuGet job to workaround build timeout (#9772) + +### Documentation and Help Content + +- Change log `6.1.4` (#9759) +- Change log for release `6.2.1` (#9760) +- Add quick steps for adding docs to cmdlets (#9978) +- Update readme `gitter` badge (#9920) (Thanks @RDIL!) +- Update `README` and `metadata.json` for `7.0.0-preview.1` release (#9767) + +## [7.0.0-preview.1] - 2019-05-30 + +### Breaking Changes + +- Disable the debugger when in system lock-down mode (#9645) +- Fix `Get-Module -FullyQualifiedName` option to work with paths (#9101) (Thanks @pougetat!) +- Fix `-NoEnumerate` behavior in `Write-Output` (#9069) (Thanks @vexx32!) +- Make command searcher treat wildcard as literal if target exists for execution (#9202) + +### Engine Updates and Fixes + +- Port PowerShell to .NET Core 3.0 (#9597) +- Make sure we always return an object in command searcher (#9623) +- Support line continuance with pipe at the start of a line (#8938) (Thanks @KirkMunro!) +- Add support for `ValidateRangeKind` to `ParameterMetadata.GetProxyAttributeData` (#9059) (Thanks @indented-automation!) +- Allow passing just a dash as an argument to a file via pwsh (#9479) +- Fix tab completion for functions (#9383) +- Reduce string allocation in console output code (#6882) (Thanks @iSazonov!) +- Fixing test run crash by not passing script block to the callback (#9298) +- Add Binary Parsing Support & Refactor `TryGetNumberValue` & `ScanNumberHelper` (#7993) (Thanks @vexx32!) +- Add PowerShell remoting enable/disable cmdlet warning messages (#9203) +- Add `xsd` for `cdxml` (#9177) +- Improve formatting performance by having better primitives on `PSObject` (#8785) (Thanks @powercode!) +- Improve type inference of array literals and foreach statement variables (#8100) (Thanks @SeeminglyScience!) +- Fix for `FormatTable` remote deserialization regression (#9116) +- Get `MethodInfo` from .NET public type with explicit parameter types (#9029) (Thanks @iSazonov!) +- Add retry logic to the operation that updates `powershell.config.json` (#8779) (Thanks @iSazonov!) +- Update the task-based `async` APIs added to PowerShell to return a Task object directly (#9079) +- Add 5 `InvokeAsync` overloads and `StopAsync` to the `PowerShell` type (#8056) (Thanks @KirkMunro!) +- Remove unused cached types (#9015) + +### General Cmdlet Updates and Fixes + +- Fix use of unicode ellipsis in `XML` for truncating error messages (#9589) +- Improve error message in FileSystemProvider when removing a folder containing hidden or read only files (#9551) (Thanks @iSazonov!) +- Enable recursion into `OneDrive` by not treating placeholders as symlinks (#9509) +- Change `MatchType` for `EnumerationOptions` to be `Win32` making this consistent with Windows PowerShell (#9529) +- Add Support for null Usernames in Web Cmdlet Basic Auth (#9536) (Thanks @markekraus!) +- Fix null reference when `Microsoft.PowerShell.Utility` is loaded as a `snapin` in hosting scenarios (#9404) +- Update width of `DateTime` to accommodate change in Japan `DateTime` format with new era starting 5/1/19 (#9503) +- Fix `Get-Runspace` runspace object format Type column (#9438) +- Return correct casing of filesystem path during normalization (#9250) +- Move warning message to `EndProcessing` so it only shows up once (#9385) +- Fix the platform check in `CimDSCParser.cs` (#9338) +- New `New-PSBreakpoint` cmdlet & new `-Breakpoint` parameter for `Debug-Runspace` (#8923) +- Fix help paging issues on macOS/Linux and with custom pager that takes arguments (#9033) (Thanks @rkeithhill!) +- Add `QuoteFields` parameter to `ConvertTo-Csv` and `Export-Csv` (#9132) (Thanks @iSazonov!) +- Fix progress for Get-ComputerInfo (#9236) (Thanks @powercode!) +- Add `ItemSeparator` and `AltItemSeparator` properties in `ProviderInfo` (#8587) (Thanks @renehernandez!) +- Add timestamp to `pshost` trace listener (#9230) +- Implement `Get-Random -Count` without specifying an `InputObject` list (#9111) (Thanks @pougetat!) +- Enable `SecureString` cmdlets for non-Windows (#9199) +- Add Obsolete message to `Send-MailMessage` (#9178) +- Fix `Restart-Computer` to work on `localhost` when WinRM is not present (#9160) +- Make `Start-Job` throw terminating exception when `-RunAs32` is specified in 64-bit pwsh (#9143) +- Make `Start-Job` throw terminating error when PowerShell is being hosted (#9128) +- Made `-Subject` parameter of `Send-MailMessage` command no longer mandatory. (#8961) (Thanks @ece-jacob-scott!) +- Make `New-ModuleManifest` consistent with `Update-ModuleManifest` (#9104) (Thanks @pougetat!) +- Add support for empty `NoteProperty` in `Group-Object` (#9109) (Thanks @iSazonov!) +- Remove `Hardlink` from `Mode` property in default file system format (#8789) (Thanks @powercode!) +- Fixing issue with help progress with `Get-Help` not calling `Completed` (#8788) (Thanks @powercode!) +- Allow `Test-ModuleManifest` to work when `RootModule` has no file extension (#8687) (Thanks @pougetat!) +- Add `UseQuotes` parameter to `Export-Csv` and `ConvertTo-Csv` cmdlets (#8951) (Thanks @iSazonov!) +- Update version for `PowerShell.Native` and hosting tests (#8983) +- Refactor shuffle in `Get-Random` to save a full iteration of the objects. (#8969) (Thanks @st0le!) +- Suggest `-Id pid` for `Get-Process pid` (#8959) (Thanks @MohiTheFish!) + +### Code Cleanup + +- `Attributes.cs` - Style / Formatting Fixes (#9625) (Thanks @vexx32!) +- Remove Workflow from `PSSessionType` (#9618) (Thanks @iSazonov!) +- Update use of "PowerShell Core" to just "PowerShell" (#9513) +- Use `IPGlobalProperties` on all platforms for getting host name (#9530) (Thanks @iSazonov!) +- Remove `IsSymLink()` P/Invoke on Unix (#9534) (Thanks @iSazonov!) +- Cleanup unused P/Invokes on Unix (#9531) (Thanks @iSazonov!) +- Update use of `Windows PowerShell` to just `PowerShell` (#9508) +- Cleanup: sort `usings` (#9490) (Thanks @iSazonov!) +- Cleanup `Export-Command` from `AssemblyInfo` (#9455) (Thanks @iSazonov!) +- Run CodeFormatter for `System.Management.Automation` (#9402) (Thanks @iSazonov!) +- Run CodeFormatter with `BraceNewLine`,`UsingLocation`,`FormatDocument`,`NewLineAbove` rules (#9393) (Thanks @iSazonov!) +- Run CodeFormatter for `WSMan.Management` (#9400) (Thanks @iSazonov!) +- Run CodeFormatter for `WSMan.Runtime` (#9401) (Thanks @iSazonov!) +- Run CodeFormatter for `Security` module (#9399) (Thanks @iSazonov!) +- Run CodeFormatter for `MarkdownRender` (#9398) (Thanks @iSazonov!) +- Run CodeFormatter for `Eventing` (#9394) (Thanks @iSazonov!) +- Use `Environment.NewLine` for new lines in `ConsoleHost` code (#9392) (Thanks @iSazonov!) +- Run CodeFormatter for Diagnostics module (#9378) (Thanks @iSazonov!) +- Run CodeFormatter for `Microsoft.PowerShell.Commands.Management` (#9377) (Thanks @iSazonov!) +- Run CodeFormatter for Utility module (#9376) (Thanks @iSazonov!) +- Style: Match file name casings of C# source files for Utility commands (#9329) (Thanks @ThreeFive-O!) +- Update repo for Ubuntu 14.04 EOL (#9324) +- Cleanup: sort `usings` (#9283) (Thanks @iSazonov!) +- Fix StyleCop Hungarian Notation (#9281) (Thanks @iSazonov!) +- Style: Update StyleCop rules (#8500) +- Enhance the P/Invoke code for `LookupAccountSid` in `Process.cs` (#9197) (Thanks @iSazonov!) +- Fix coding style for `NewModuleManifestCommand` (#9134) (Thanks @pougetat!) +- Remove unused method `CredUIPromptForCredential` from `HostUtilities.cs` (#9220) (Thanks @iSazonov!) +- Remove non-existent paths from `.csproj` files (#9214) (Thanks @ThreeFive-O!) +- Typo in new parameter set (#9205) +- Minor `FileSystemProvider` cleanup (#9182) (Thanks @RDIL!) +- Cleanup style issues in `CoreAdapter` and `MshObject` (#9190) (Thanks @iSazonov!) +- Minor cleanups in `Process.cs` (#9195) (Thanks @iSazonov!) +- Refactor `ReadConsole` P/Invoke in `ConsoleHost` (#9165) (Thanks @iSazonov!) +- Clean up `Get-Random` cmdlet (#9133) (Thanks @pougetat!) +- Fix to not pass `StringBuilder` by reference (`out` or `ref`) in P/Invoke (#9066) (Thanks @iSazonov!) +- Update AppVeyor comments in `Implicit.Remoting.Tests.ps1` (#9020) (Thanks @RDIL!) +- Remove AppImage from tools (#9100) (Thanks @Geweldig!) +- Using supported syntax for restoring warnings - Visual Studio 2019 complains about enable. (#9107) (Thanks @powercode!) +- Use `Type.EmptyTypes` and `Array.Empty()` to replace our custom code of the same functionality (#9042) (Thanks @iSazonov!) +- Rename private methods in `MshCommandRuntime.cs` (#9074) (Thanks @vexx32!) +- Cleanup & update `ErrorRecord` class code style (#9021) (Thanks @vexx32!) +- Remove unused cached types from `CachedReflectionInfo` (#9019) (Thanks @iSazonov!) +- Fix CodeFactor brace style issues in `FileSystemProvider` (#8992) (Thanks @RDIL!) +- Use `List.AddRange` to optimize `-Split` (#9001) (Thanks @iSazonov!) +- Remove Arch Linux Dockerfile (#8990) (Thanks @RDIL!) +- Cleanup `dllimport` (#8847) (Thanks @iSazonov!) + +### Tools + +- Convert custom attribute `ValidatePathNotInSettings` to function (#9406) +- Create `DependaBot` `config.yml` (#9368) +- Add more users to failures detection and fix alias for static analysis (#9292) +- Make `install-powershell.ps1` work on Windows Server 2012 R2 (#9271) +- Enable `PoshChan` for getting and automatic retrieval of test failures for a PR (#9232) +- Fix capitalization cases for `PoshChan` (#9188) (Thanks @RDIL!) +- Update to new format for `PoshChan` settings and allow all users access to reminders (#9198) +- Fix settings to use dashes instead of underscore (#9167) +- Fix `AzDevOps` context names and add all PowerShell team members (#9164) +- Add settings for `PoshChan` (#9162) +- Adding `CmdletsToExport` and `AliasesToExport` to test module manifests. (#9108) (Thanks @powercode!) +- Delete Docker manifest creation script (#9076) (Thanks @RDIL!) +- Make install scripts more consistent over different operating systems (#9071) (Thanks @Geweldig!) +- Comment cleanup in `releaseTools.psm1` (#9064) (Thanks @RDIL!) +- Fix duplicate recommendation of Azure DevOps extension for Visual Studio Code (#9032) (Thanks @ThreeFive-O!) +- Code coverage artifacts (#8993) + +### Tests + +- Update version tests to use `NextReleaseVersion` from `metadata.json` (#9646) +- Convert Windows CI to stages (#9607) +- Multiple test fixes and improved logging for fragile tests (#9569) +- Add unit and feature tests for `Send-MailMessage` (#9213) (Thanks @ThreeFive-O!) +- Update to Pester `4.8.0` (#9510) +- Ensure `Wait-UntilTrue` returns `$true` in Pester tests (#9458) (Thanks @xtqqczze!) +- Adding tests for `Remove-Module` (#9276) (Thanks @pougetat!) +- Allow CI to run on branches with this name pattern: `feature*` (#9415) +- Mark tests in macOS CI which use `AppleScript` as pending/inconclusive (#9352) +- Reduce time for stack overflow test (#9302) +- Added more tests for `Import-Alias` by file regarding parsing difficult aliases strings (#9247) (Thanks @SytzeAndr!) +- Move from `npm` to `Yarn` for markdown tests (#9312) (Thanks @RDIL!) +- Only search for functions in Constrained Language help tests (#9301) +- Fix skipping of tests in `RemoteSession.Basic.Tests.ps1` (#9304) +- Make sure non-Windows CI fails when a test fails (#9303) +- Update tests to account for when `$PSHOME` is read only (#9279) +- Add tests for command globbing (#9180) +- Fix tab completion test to handle multiple matches (#8891) +- Refactor macOS CI so that tests run in parallel (#9056) +- Fix `Enter-PSHostProcess` tests flakiness (#9007) +- Add source for `Install-Package` to install `netDumbster` (#9081) +- Style fixes for `Select-Xml` tests (#9037) (Thanks @ThreeFive-O!) +- Enable cross-platform `Send-MailMessage` tests for CI (#8859) (Thanks @ThreeFive-O!) +- Added `RequireSudoOnUnix` tags to `PowerShellGet` tests and remove pending parameter (#8954) (Thanks @RDIL!) +- Style fixes for `ConvertTo-Xml` tests (#9036) (Thanks @ThreeFive-O!) +- Align name schemes for test files (#9034) (Thanks @ThreeFive-O!) +- Pending `NamedPipeConnectionInfo` test (#9003) (Thanks @iSazonov!) +- Add test for `-WhatIf` for `New-FileCatalog` (#8966) (Thanks @mjanko5!) + +### Build and Packaging Improvements + +- Fix the PowerShell version number in MSI packages (Internal 8547) +- Add cleanup before building test package (Internal 8529) +- Update version for SDK tests and `Microsoft.PowerShell.Native` package (Internal 8512) +- Update the target framework for reference assemblies to `netcoreapp3.0` (Internal 8510) +- Fix syncing modules from PowerShell gallery by normalizing version numbers (Internal 8504) +- Add `tsaVersion` property as `TsaV1` for compliance build phase (#9176) +- Add ability to cross compile (#9374) +- Add `AcessToken` variable to jobs that perform signing (#9351) +- Add CI for `install-powershell.sh` and Amazon Linux (#9314) +- Add component detection to all jobs (#8964) +- Add Preview assets for `MSIX` (#9375) +- Add secret scanning to CI (#9249) +- Build test packages for `windows`, `linux-x64`, `linux-arm`, `linux-arm64` and `macOS` (#9476) +- Bump `gulp` from `4.0.0` to `4.0.2` (#9441, #9544) +- Bump `Markdig.Signed` from `0.15.7` to `0.17.0` (#8981, #9579) +- Bump `Microsoft.CodeAnalysis.CSharp` from `2.10.0` to `3.1.0` (#9277, 9653) +- Bump `Microsoft.PowerShell.Native` from `6.2.0-rc.1` to `6.2.0` (#9200) +- Bump `Microsoft.Windows.Compatibility` from `2.0.1` to `2.1.1` (#9605) +- Bump `Newtonsoft.Json` from `12.0.1` to `12.0.2` (#9431, #9434) +- Bump `NJsonSchema` from `9.13.19` to `9.14.1` (#9044, #9136, #9166, #9172, #9184, #9196, #9265, #9349, #9388, #9421, #9429, #9478, #9523, #9616) +- Bump `PackageManagement` from `1.3.1` to `1.4` (#9567, #9650) +- Bump `PowerShellGet` from `2.0.4` to `2.1.4` in /src/Modules (#9110, #9145, #9600, #9691) +- Bump `PSReadLine` from `2.0.0-beta3` to `2.0.0-beta4` (#9554) +- Bump `SelfSignedCertificate` (#9055) +- Bump `System.Data.SqlClient` from `4.6.0` to `4.6.1` (#9601) +- Bump `System.Net.Http.WinHttpHandler` from `4.5.2` to `4.5.3` (#9333) +- Bump `Microsoft.PowerShell.Archive` from `1.2.2.0` to `1.2.3.0` (#9593) +- Check to be sure that the test result file has actual results before uploading (#9253) +- Clean up static analysis config (#9113) (Thanks @RDIL!) +- Create `codecoverage` and test packages for non-Windows (#9373) +- Create test package for macOS on release builds (#9344) +- Disable Homebrew analytics in macOS Azure DevOps builds (#9130) (Thanks @RDIL!) +- Enable building of `MSIX` package (#9289) +- Enable building on Kali Linux (#9471) +- Fix artifact Download issue in release build (#9095) +- Fix build order in `windows-daily` build (#9275) +- Fix dependencies of NuGet build to wait on `DEB` uploads to finish (#9118) +- Fix `MSI` Upgrade failure for preview builds (#9013) +- Fix publishing daily `nupkg` to MyGet (#9269) +- Fix the failed test and update `Publish-TestResults` to make Azure DevOps fail the task when any tests failed (#9457) +- Fix variable name in `windows-daily.yml` (#9274) +- Fixed Dockerfile syntax highlighting (#8991) (Thanks @RDIL!) +- Make `CodeCoverage` configuration build portable symbol files (#9346) +- Make Linux CI parallel (#9209) +- Move artifacts to artifact staging directory before uploading (#9273) +- Performance improvements for release build (#9179) +- Preserve user shortcuts pinned to TaskBar during MSI upgrade (#9305) (Thanks @bergmeister!) +- Publish global tool packages to `pwshtool` blob and bug fixes (#9163) +- Publish test package on release builds (#9063) +- Publish windows daily build to MyGet (#9288) +- Remove appveyor references from packaging tools (#9117) (Thanks @RDIL!) +- Remove code from `CI.psm1` to optionally run Feature tests (#9212) (Thanks @RDIL!) +- Remove duplicate `PoliCheck` task and pin to specific version (#9297) +- Run `Start-PSBootStrap` in Code Coverage build to install .NET SDK (#9690) +- Switch from `BMP` to `PNG` for graphical `MSI` installer assets (#9606) +- Translate Skipped the test results into something Azure DevOps does NOT understand (#9124) +- Update Markdown test dependencies (#9075) (Thanks @RDIL!) +- Update UML to represent SDK and Global tool builds (#8997) +- Use IL assemblies for NuGet packages to reduce size (#9171) + +### Documentation and Help Content + +- Add checkbox to PR checklist for experimental feature use (#9619) (Thanks @KirkMunro!) +- Updating committee membership (#9577) (Thanks @HemantMahawar!) +- Update `CODEOWNERS` file to reduce noise (#9547) +- add download link to `raspbian64` to readme (#9520) +- Update `Support_Question.md` (#9218) (Thanks @vexx32!) +- Fix version of `PowerShellGet` in changelog (#9335) +- Update release process template to clarify that most tasks are coordinated by the release pipeline (#9238) +- Fix several problems in `WritingPesterTests` guideline (#9078) (Thanks @ThreeFive-O!) +- Update `ChangeLog` for `6.2.0` (#9245) +- Update docs for `v6.2.0` (#9229) +- Update `feature-request` issue template to move instructions into comments. (#9187) (Thanks @mklement0!) +- Update link to Contributing guide to new `PowerShell-Doc` repo (#9090) (Thanks @iSazonov!) +- Correct punctuation in `README.md` (#9045) (Thanks @yashrajbharti!) +- Update Docker `README.md` (#9010) (Thanks @RDIL!) +- Update release process issue template (#9051) (Thanks @RDIL!) +- Documentation Cleanup (#8851) (Thanks @RDIL!) +- Update docs for `6.2.0-rc.1` release (#9022) +- Update release template (#8996) + +[7.0.3]: https://github.com/PowerShell/PowerShell/compare/v7.0.2...v7.0.3 +[7.0.2]: https://github.com/PowerShell/PowerShell/compare/v7.0.1...v7.0.2 +[7.0.1]: https://github.com/PowerShell/PowerShell/compare/v7.0.0...v7.0.1 +[7.0.0]: https://github.com/PowerShell/PowerShell/compare/v7.0.0-rc.3...v7.0.0 +[7.0.0-rc.3]: https://github.com/PowerShell/PowerShell/compare/v7.0.0-rc.2...v7.0.0-rc.3 +[7.0.0-rc.2]: https://github.com/PowerShell/PowerShell/compare/v7.0.0-rc.1...v7.0.0-rc.2 +[7.0.0-rc.1]: https://github.com/PowerShell/PowerShell/compare/v7.0.0-preview.6...v7.0.0-rc.1 +[7.0.0-preview.6]: https://github.com/PowerShell/PowerShell/compare/v7.0.0-preview.5...v7.0.0-preview.6 +[7.0.0-preview.5]: https://github.com/PowerShell/PowerShell/compare/v7.0.0-preview.4...v7.0.0-preview.5 +[7.0.0-preview.4]: https://github.com/PowerShell/PowerShell/compare/v7.0.0-preview.3...v7.0.0-preview.4 +[7.0.0-preview.3]: https://github.com/PowerShell/PowerShell/compare/v7.0.0-preview.2...v7.0.0-preview.3 +[7.0.0-preview.2]: https://github.com/PowerShell/PowerShell/compare/v7.0.0-preview.1...v7.0.0-preview.2 +[7.0.0-preview.1]: https://github.com/PowerShell/PowerShell/compare/v6.2.0-rc.1...v7.0.0-preview.1 diff --git a/CHANGELOG/7.1.md b/CHANGELOG/7.1.md new file mode 100644 index 00000000000..eba1998e29c --- /dev/null +++ b/CHANGELOG/7.1.md @@ -0,0 +1,1158 @@ +# 7.1 Changelog + +## [7.1.7] - 2022-04-26 + +### Engine Updates and Fixes + +- Fix for partial PowerShell module search paths, that can be resolved to CWD locations +- Do not include node names when sending telemetry. (#16981) to v7.1.7 (Internal 20187,Internal 20260) + +### Tests + +- Re-enable `PowerShellGet` tests targeting PowerShell gallery (#17062) +- Skip failing scriptblock tests (#17093) + +### Build and Packaging Improvements + +
+ + + +

Update .NET SDK to 5.0.407

+ +
+ +
    +
  • Fix build failure in `generate checksum file for packages` step - v7.1.7 (Internal 20274)
  • +
  • Updated files.wxs for 7.1.7 (Internal 20210)
  • +
  • Updated to .NET 5.0.16 / SDK 5.0.407 (Internal 20131)
  • +
  • Update Ubuntu images to use Ubuntu 20.04 (#15906)
  • +
  • Update dotnet-install script download link (Internal 19950)
  • +
  • Create checksum file for global tools (#17056) (Internal 19928)
  • +
  • Make sure global tool packages are published in stable build (Internal 19624)
  • +
+ +
+ +[7.1.7]: https://github.com/PowerShell/PowerShell/compare/v7.1.6...v7.1.7 + +## [7.1.6] - 2022-03-16 + +### Build and Packaging Improvements + +
+ + + +

Update .NET SDK to 5.0.406

+ +
+ +
    +
  • Update the mapping file (#16316, Internal 19528)
  • +
  • Remove code that handles dotnet5 feed (Internal 19525)
  • +
  • Fix issues in release build (#16332)
  • +
  • Enable ARM64 packaging for macOS (#15768)
  • +
  • Update feed and analyzer dependency (#16327)
  • +
  • Only upload stable buildinfo for stable releases (#16251)
  • +
  • Opt-in to build security monitoring (#16911)
  • +
  • Update experimental feature json files (#16838)
  • +
  • Ensure alpine and arm SKUs have the PowerShell configuration file with experimental features enabled (#16823)
  • +
  • Remove WiX install (#16834)
  • +
  • Add Linux package dependencies for packaging (#16807)
  • +
  • Switch to our custom images for build and release (#16801)
  • +
  • Remove all references to cmake for the builds in this repo (#16578)
  • +
  • Register NuGet source when generating CGManifest (#16570)
  • +
  • Update images used for release (#16580)
  • +
  • Add GitHub Workflow to keep notices up to date (#16284)
  • +
  • Update the vmImage and PowerShell root directory for macOS builds (#16611)
  • +
  • Add Software Bill of Materials to the main packages (#16202, #16641, #16711)
  • +
  • Update macOS build image and root folder for build (#16609)
  • +
  • Add diagnostics used to take corrective action when releasing buildInfo JSON file (#16404)
  • +
  • Add checkout to build json stage to get ci.psm1 (#16399)
  • +
+ +
+ +[7.1.6]: https://github.com/PowerShell/PowerShell/compare/v7.1.5...v7.1.6 + +## [7.1.5] - 2021-10-14 + +### Engine Updates and Fixes + +- Handle error from unauthorized access when removing `AppLocker` test files (#15881) +- Test more thoroughly whether a command is `Out-Default` for transcription scenarios (#15653) +- Handle error when the telemetry mutex cannot be created (#15574) (Thanks @gukoff!) +- Configure `ApplicationInsights` to not send cloud role name (Internal 17100) +- Disallow `Add-Type` in NoLanguage mode on a locked down machine (Internal 17522) + +### Tools + +- Add `.stylecop` to `filetypexml` and format it (#16025) + +### Build and Packaging Improvements + +
+ + +

Bump .NET SDK to 5.0.402

+
+ +
    +
  • Upgrade set-value package for markdown test (#16196)
  • +
  • Sign the .NET createdump executable (#16229)
  • +
  • Move vPack build to 1ES Pool (#16169)
  • +
  • Update to .NET SDK 5.0.402 (Internal 17537)
  • +
  • Move from PkgES hosted agents to 1ES hosted agents (#16023)
  • +
  • Fix the macOS build by updating the pool image name (#16010)
  • +
  • Use Alpine 3.12 for building PowerShell for Alpine Linux (#16008)
  • +
+ +
+ +### Documentation and Help Content + +- Fix example nuget.config (#14349) + +[7.1.5]: https://github.com/PowerShell/PowerShell/compare/v7.1.4...v7.1.5 + +## [7.1.4] - 2021-08-12 + +### Build and Packaging Improvements + +
+ + +Bump .NET SDK to version 5.0.400 + + +
    +
  • Remove the cat file from PSDesiredStateConfiguration module (Internal 16723)
  • +
  • Update .NET SDK version and other packages (Internal 16715)
  • +
+ +
+ +[7.1.4]: https://github.com/PowerShell/PowerShell/compare/v7.1.3...v7.1.4 + +## [7.1.3] - 2021-03-11 + +### Engine Updates and Fixes + +- Remove the 32K character limit on the environment block for `Start-Process` (#14111) +- Fix webcmdlets to properly construct URI from body when using `-NoProxy` (#14673) + +### General Cmdlet Updates and Fixes + +- Fix `PromptForCredential()` to add `targetName` as domain (#14504) + +### Build and Packaging Improvements + +
+ + + +Bump .NET SDK to 5.0.4 + + + +
    +
  • Bump .NET SDK to 5.0.4 (Internal 14775)
  • +
  • Disable running markdown link verification in release build CI (#14971, #14974, #14975)
  • +
  • Use template that disables component governance for CI (#14938)
  • +
  • Declare which variable group is used for checking the blob in the release build (#14970)
  • +
  • Add suppress for nuget multi-feed warning (#14893)
  • +
  • Disable code signing validation where the file type is not supported (#14885)
  • +
  • Install wget on CentOS 7 docker image (#14857)
  • +
  • Fix install-dotnet download (#14856)
  • +
  • Make universal Deb package based on deb package spec (#14681)
  • +
  • Fix release build to upload global tool packages to artifacts (#14620)
  • +
  • Update ini component version in test package.json (#14454)
  • +
  • Add manual release automation steps and improve changelog script (#14445)
  • +
  • Update markdown test packages with security fixes (#14145)
  • +
  • Fix a typo in the Get-ChangeLog function (#14129)
  • +
  • Disable global tool copy to unblock release
  • +
+ +
+ +[7.1.3]: https://github.com/PowerShell/PowerShell/compare/v7.1.2...v7.1.3 + +## [7.1.2] - 2021-02-11 + +### Build and Packaging Improvements + +
+ + +Bump .NET SDK to version 5.0.103 + + +
    +
  • Fix third party signing for files in sub-folders (#14751)
  • +
  • Bump .NET SDK to version 5.0.103 (Internal 14459)
  • +
  • Publish the global tool package for stable release
  • +
+ +
+ +[7.1.2]: https://github.com/PowerShell/PowerShell/compare/v7.1.1...v7.1.2 + +## [7.1.1] - 2021-01-14 + +### General Cmdlet Updates and Fixes + +- Avoid an exception if file system does not support reparse points (#13634) (Thanks @iSazonov!) +- Make AppLocker Enforce mode take precedence over UMCI Audit mode (#14353) + +### Code Cleanup + +- Fix syntax error in Windows packaging script (#14377) + +### Build and Packaging Improvements + +
+ +
    +
  • Use one feed in each nuget.config in official builds (#14363)
  • +
  • Fix path signed RPMs are uploaded from in release build (#14424)
  • +
  • Fix issue with unsigned build (#14367)
  • +
  • Move macOS and NuGet packages to ESRP signing (#14324)
  • +
  • Move Windows packages signing to use ESRP (#14060)
  • +
  • Move Linux packages to ESRP signing (#14210)
  • +
  • Migrate 3rd party signing to ESRP (#14010)
  • +
  • Don't do a shallow checkout (#13992)
  • +
  • Move to ESRP signing for Windows files (#13988)
  • +
  • Add checkout step to release build templates (#13840)
  • +
+ +
+ +[7.1.1]: https://github.com/PowerShell/PowerShell/compare/v7.1.0...v7.1.1 + +## [7.1.0] - 2020-11-11 + +### Engine Updates and Fixes + +- Fix a logic bug in `MapSecurityZone` (#13921) (Thanks @iSazonov!) + +### General Cmdlet Updates and Fixes + +- Update `pwsh -?` output to match docs (#13748) + +### Tests + +- `markdownlint` security updates (#13730) + +### Build and Packaging Improvements + +
+ +
    +
  • Fixes to release pipeline for GA release (Internal 13410)
  • +
  • Add validation and dependencies for Ubuntu 20.04 distribution to packaging script (#13993)
  • +
  • Change PkgES Lab to unblock build (Internal 13376)
  • +
  • Add .NET install workaround for RTM (#13991)
  • +
  • Bump Microsoft.PowerShell.Native version from 7.1.0-rc.2 to 7.1.0 (#13976)
  • +
  • Bump PSReadLine version to 2.1.0 (#13975)
  • +
  • Bump .NET to version 5.0.100-rtm.20526.5 (#13920)
  • +
  • Update script to use .NET RTM feeds (#13927)
  • +
+ +
+ +[7.1.0]: https://github.com/PowerShell/PowerShell/compare/v7.1.0-rc.2...v7.1.0 + +## [7.1.0-rc.2] - 2020-10-20 + +### Engine Updates and Fixes + +- Rename `Get-Subsystem` to `Get-PSSubsystem` and fix two related minor issues (#13765) +- Add missing `PSToken` token table entries to fix the `PSParser` API (#13779) +- Add additional PowerShell modules to the tracked modules list (#12183) +- Fix blocking wait when starting file associated with a Windows application (#13750) +- Revert `PSNativePSPathResolution` to being an experimental feature (#13734) + +### General Cmdlet Updates and Fixes + +- Emit warning if `ConvertTo-Json` exceeds `-Depth` value (#13692) + +### Build and Packaging Improvements + +- Change Linux package script call to publish to the production repository in release builds (#13714) +- Update `PSReadLine` version to `2.1.0-rc1` (#13777) +- Move PowerShell build to dotnet `5.0-RC.2` (#13780) +- Bump `Microsoft.PowerShell.Native` to `7.1.0-rc.2` (#13794) + +[7.1.0-rc.2]: https://github.com/PowerShell/PowerShell/compare/v7.1.0-rc.1...v7.1.0-rc.2 + +## [7.1.0-rc.1] - 2020-09-29 + +### Engine Updates and Fixes + +- Make fixes to `ComInterop` code as suggested by .NET team (#13533) + +### General Cmdlet Updates and Fixes + +- Fix case where exception message contains just ``"`n"`` on Windows (#13684) +- Recognize `CONOUT$` and `CONIN$` as reserved device names (#13508) (Thanks @davidreis97!) +- Fix `ConciseView` for interactive advanced function when writing error (#13623) + +### Code Cleanup + +
+ + + +

We thank the following contributors!

+

@xtqqczze, @soccypowa

+ +
+ +
    +
  • Simplify logical negation (#13555) (Thanks @xtqqczze!)
  • +
  • Fixed the indentation of the help content for -nologo (#13557) (Thanks @soccypowa!)
  • +
+ +
+ +### Build and Packaging Improvements + +
+ + + +

We thank the following contributors!

+

@heaths

+ +
+ +
    +
  • Bump NJsonSchema from 10.1.24 to 10.1.26 (#13586)
  • +
  • Bump PowerShellGet from 2.2.4 to 2.2.5 (#13683)
  • +
  • Bump Microsoft.ApplicationInsights from 2.14.0 to 2.15.0 (#13639)
  • +
  • Update PowerShell to build against dotnet 5.0-RC.1 (#13643)
  • +
  • Write the InstallLocation to fixed registry key (#13576) (Thanks @heaths!)
  • +
+ +
+ +### Documentation and Help Content + +- Update `README` and `metadata.json` for `7.1.0-preview.7` release (#13565) + +[7.1.0-rc.1]: https://github.com/PowerShell/PowerShell/compare/v7.1.0-preview.7...v7.1.0-rc.1 + +## [7.1.0-preview.7] - 2020-09-08 + +### Breaking Changes + +- Fix `$?` to not be `$false` when native command writes to `stderr` (#13395) + +### Engine Updates and Fixes + +- Initial work of the subsystem plugin model (for minimal powershell) (#13186) +- Optimize `GetSystemLockdownPolicy` for non-lockdown scenarios (#13438) + +### General Cmdlet Updates and Fixes + +- Revert "Add the parameter `-Paged` to `Get-Help` to support paging (#13374)" (#13519) +- Add support for `TLS` 1.3 in Web cmdlets (#13409) (Thanks @iSazonov!) +- Add null check for `args` in `CommandLineParser` (#13451) (Thanks @iSazonov!) +- Process reparse points for Microsoft Store applications (#13481) (Thanks @iSazonov!) +- Move `PSNullConditionalOperators` feature out of experimental (#13529) +- Move `PSNativePSPathResolution` feature out of Experimental (#13522) +- Use field if property does not exist for `ObRoot` when using PowerShell Direct to container (#13375) (Thanks @hemisphera!) +- Suppress `UTF-7` obsolete warnings (#13484) +- Avoid multiple enumerations of an `IEnumerable` instance in `Compiler.cs` (#13491) +- Change `Add-Type -OutputType` to not support `ConsoleApplication` and `WindowsApplication` (#13440) +- Create warnings when `UTF-7` is specified as an encoding (#13430) + +### Code Cleanup + +
+ + + +

We thank the following contributors!

+

@xtqqczze, @tamasvajk

+ +
+ +
    +
  • Add single blank line after copyright header (#13486) (Thanks @xtqqczze!)
  • +
  • Use read-only auto-implemented properties (#13507) (Thanks @xtqqczze!)
  • +
  • Use boolean instead of bitwise operators on bool values (#13506) (Thanks @xtqqczze!)
  • +
  • Fix erroneous assert (#13495) (Thanks @tamasvajk!)
  • +
  • Cleanup: remove duplicate words in comments (#13539) (Thanks @xtqqczze!)
  • +
  • Reformat StringUtil (#13509) (Thanks @xtqqczze!)
  • +
  • Use uint instead of long for PDH constants (#13502) (Thanks @xtqqczze!)
  • +
  • Cleanup: Remove redundant empty lines (#13404) (Thanks @xtqqczze!)
  • +
  • Add StringUtil.Format overload to avoid unnecessary allocations (#13408) (Thanks @xtqqczze!)
  • +
  • Fix test hooks for CommandLineParameterParser (#13459)
  • +
  • Remove redundant delegate creation (#13441) (Thanks @xtqqczze!)
  • +
+ +
+ +### Tools + +- vscode: Add `editorconfig` to recommended extensions (#13537) (Thanks @xtqqczze!) +- Remove the out-dated `ZapDisable` related code from `build.psm1` (#13350) (Thanks @jackerr3!) + +### Tests + +- Disable `WMF` download link validation test (#13479) + +### Build and Packaging Improvements + +
+ + + +

We thank the following contributors!

+

@yecril71pl

+ +
+ +
    +
  • Add Microsoft.NET.Test.Sdk dependency (Internal 12589)
  • +
  • Update .NET NuGet package version to 5.0.0-preview.8.20407.11 (Internal 12555)
  • +
  • Update to .NET 5 preview 8 (#13530)
  • +
  • Change stage dependency for docker release stage in release pipeline (#13512)
  • +
  • Bump Microsoft.NET.Test.Sdk from 16.7.0 to 16.7.1 (#13492)
  • +
  • Create the folder before copying the global tools (#13476)
  • +
  • A few fixes to the release pipeline (#13473)
  • +
  • Bump Markdig.Signed from 0.20.0 to 0.21.1 (#13463)
  • +
  • Add a pre-check for git to build.psm1 (#13227) (Thanks @yecril71pl!)
  • +
+ +
+ +### Documentation and Help Content + +- Update `README` links and `metadata.json` for `7.1.0-preview.6` (#13437) + +[7.1.0-preview.7]: https://github.com/PowerShell/PowerShell/compare/v7.1.0-preview.6...v7.1.0-preview.7 + +## [7.1.0-preview.6] - 2020-08-17 + +### Breaking Changes + +- Rename `-FromUnixTime` to `-UnixTimeSeconds` on `Get-Date` to allow Unix time input (#13084) (Thanks @aetos382!) +- Make `$ErrorActionPreference` not affect `stderr` output of native commands (#13361) +- Allow explicitly specified named parameter to supersede the same one from hashtable splatting (#13162) + +### Engine Updates and Fixes + +- Refactor command line parser to do early parsing (#11482) (Thanks @iSazonov!) +- Add support for some .NET intrinsic type converters (#12580) (Thanks @iSazonov!) +- Refresh and enable the `ComInterop` code in PowerShell (#13304) + +### Experimental Features + +- Add `-Runspace` parameter to all `*-PSBreakpoint` cmdlets (#10492) (Thanks @KirkMunro!) + +### General Cmdlet Updates and Fixes + +- Fix error message from new symbolic link missing target (#13085) (Thanks @yecril71pl!) +- Make the parameter `args` non-nullable in the public `ConsoleHost` APIs (#13429) +- Add missing dispose for `CancellationTokenSource` (#13420) (Thanks @Youssef1313!) +- Add the parameter `-Paged` to `Get-Help` to support paging (#13374) +- Fix `Get-Help` not properly displaying if parameter supports wildcards (#13353) (Thanks @ThomasNieto!) +- Update `pwsh` help for `-InputFormat` parameter (#13355) (Thanks @sethvs!) +- Declare MIT license for files copied from Roslyn (#13305) (Thanks @xtqqczze!) +- Improve `BigInteger` casting behaviors (#12629) (Thanks @vexx32!) +- Fix `Get-Acl -LiteralPath "HKLM:Software\Classes\*"` behavior (#13107) (Thanks @Shriram0908!) +- Add `DefaultVisit` method to the visitor interface and class (#13258) +- Fix conflicting shorthand switch `-s` (STA) for `pwsh` (#13262) (Thanks @iSazonov!) +- Change `Read-Host -MaskInput` to use existing `SecureString` path, but return as plain text (#13256) +- Remove `ComEnumerator` as COM objects using `IEnumerator` is now supported in .NET 5.0 (#13259) +- Use temporary personal path at Runspace startup when the 'HOME' environment variable is not defined (#13239) +- Fix `Invoke-Command` to detect recursive call of the same history entry (#13197) +- Change `pwsh` executable `-inputformat` switch prefix `-in` to `-inp` to fix conflict with `-interactive` (#13205) (Thanks @iSazonov!) +- Handle WSL filesystem path when analyze security zone of a file (#13120) +- Make other switches mandatory in `Split-Path` (#13150) (Thanks @kvprasoon!) +- New Fluent Design icon for PowerShell 7 (#13100) (Thanks @sarthakmalik!) +- Fix `Move-Item` to support cross-mount moves on Unix (#13044) + +### Code Cleanup + +
+ + + +

We thank the following contributors!

+

@xtqqczze, @yecril71pl, @ThomasNieto, @dgoldman-msft

+ +
+ +
    +
  • Use null check with pattern-matching instead of object.ReferenceEquals (#13065) (Thanks @xtqqczze!)
  • +
  • Fix comparison of value type object to null (#13285) (Thanks @xtqqczze!)
  • +
  • Use is operator instead of as operator (#13287) (Thanks @xtqqczze!)
  • +
  • Change SwitchParameter fields to properties (#13291) (Thanks @xtqqczze!)
  • +
  • Change "operable" to "executable" (#13281) (Thanks @yecril71pl!)
  • +
  • Remove AssemblyInfo property from list views (#13331) (Thanks @ThomasNieto!)
  • +
  • Use is not syntax where appropriate and remove unnecessary parentheses (#13323) (Thanks @xtqqczze!)
  • +
  • Remove unreachable code in CustomShellCommands.cs (#13316) (Thanks @xtqqczze!)
  • +
  • Add copyright header to .editorconfig and update files (#13306) (Thanks @xtqqczze!)
  • +
  • Fix typo in Out-File.cs and Out-Printer.cs (#13298) (Thanks @dgoldman-msft!)
  • +
  • Fix SA1026CodeMustNotContainSpaceAfterNewKeywordInImplicitlyTypedArrayAllocation (#13249) (Thanks @xtqqczze!)
  • +
  • Remove usage of do statement to create an infinite loop (#13137) (Thanks @xtqqczze!)
  • +
  • Use int instead of uint in places where it's more appropriate (#13141) (Thanks @xtqqczze!)
  • +
  • Use int instead of long to avoid Interlocked.Read (#13069) (Thanks @xtqqczze!)
  • +
+ +
+ +### Tools + +- Fix `dotnet` install errors (#13387) +- Increase the timeout of Windows daily build to 90 minutes (#13354) +- Update the `dependabot` configuration to version 2 (#13230) (Thanks @RDIL!) +- Fix `Test-XUnitTestResults` function (#13270) (Thanks @iSazonov!) +- Update `.devcontainer` to use nightly docker SDK images (#13128) + +### Tests + +- Mark `Test-Connection -TraceRoute` tests as pending (#13310) + +### Build and Packaging Improvements + +
+ + + +

We thank the following contributors!

+

@xtqqczze, @iSazonov, @77, @WorrenB

+ +
+ +
    +
  • Update README.md and metadata.json for next release (#13059)
  • +
  • Create release pipeline as a yaml pipeline (#13394)
  • +
  • Update infrastructure to consume private builds from .NET (#13427)
  • +
  • Fix breaks in packages daily build due to macOS signing changes (#13421)
  • +
  • Sign individual files for macOS PKG (#13392)
  • +
  • Disable code sign validation on jobs that do not sign (#13389)
  • +
  • Bump PSReadLine from 2.0.2 to 2.0.4 (#13240)
  • +
  • Update build documentation for Visual Studio 2019 dependency (#13336) (Thanks @xtqqczze!)
  • +
  • Bump Microsoft.CodeAnalysis.CSharp from 3.6.0 to 3.7.0 (#13360)
  • +
  • Bump Microsoft.NET.Test.Sdk from 16.6.1 to 16.7.0 (#13364)
  • +
  • Bump xunit.runner.visualstudio from 2.4.2 to 2.4.3 (#13343)
  • +
  • Use Authenticode certificate for MSIX signing (#13330)
  • +
  • Add default help content to the assets folder (#13257)
  • +
  • Update .NET SDK version from 5.0.100-preview.7.20366.2 to 5.0.100-preview.7.20366.15 (#13200)
  • +
  • Set C# language version to preview/9.0 (#13090) (Thanks @iSazonov!)
  • +
  • Use pwsh for build and test of package in CI build (#13223)
  • +
  • Remove rcedit dependency, move daily ico dependency to props file (#13123)
  • +
  • Bump NJsonSchema from 10.1.23 to 10.1.24 (#13214)
  • +
  • Update .NET SDK version from 5.0.100-preview.7.20364.3 to 5.0.100-preview.7.20366.2 (#13192)
  • +
  • Add support for installing arm64 MSIX package. (#13043) (Thanks @77!)
  • +
  • Fix Azure file copy issues in release build (#13182)
  • +
  • Update .NET SDK version from 5.0.100-preview.7.20358.6 to 5.0.100-preview.7.20364.3 (#13155)
  • +
  • Fix Azure file copy break in Azure DevOps (#13173)
  • +
  • Bump Xunit.SkippableFact from 1.4.8 to 1.4.13 (#13143)
  • +
  • Add new chibi svg version of the avatar (#13160) (Thanks @WorrenB!)
  • +
  • Refactor MSI code to make it easier to add a WiX exe installer (#13139)
  • +
  • Disable ReadyToRun for debug build (#13144) (Thanks @iSazonov!)
  • +
  • Add new chibi version of the avatar (#13140)
  • +
  • Update .NET SDK version from 5.0.100-preview.7.20356.2 to 5.0.100-preview.7.20358.6 (#13134) (Thanks @github-actions[bot]!)
  • +
  • Update .NET SDK version from 5.0.100-preview.6.20318.15 to 5.0.100-preview.7.20356.2 (#13125) (Thanks @github-actions[bot]!)
  • +
+ +
+ +### Documentation and Help Content + +- Fix/clarify instructions for running Start-PSPester tests (#13373) +- Improve inline documentation for `VerbInfo` (#13265) (Thanks @yecril71pl!) +- Improve the wording of inline comments in the help system (#13274) (Thanks @yecril71pl!) +- Correct grammar in `README.md` and other docs (#13269) (Thanks @tasnimzotder!) +- Add "GitHub Actions Python builds" to `ADOPTERS.md` (#13228) (Thanks @brcrista!) +- Update change logs for `6.2.x` and `7.0.x` (#13194) +- Update `README.md` and `metadata.json` for the v7.0.3 release (#13187) + +[7.1.0-preview.6]: https://github.com/PowerShell/PowerShell/compare/v7.1.0-preview.5...v7.1.0-preview.6 + +## [7.1.0-preview.5] - 2020-07-06 + +### Engine Updates and Fixes + +- Ensure assemblies listed in the module manifest `FileList` field are not loaded (#12968) + +### Code Cleanup + +
+ + + +

We thank the following contributors!

+

@xtqqczze

+ +
+ +
    +
  • Code performance fixes (#12956) (Thanks @xtqqczze!)
  • +
+ +
+ +### Tools + +- Add missing `.editorconfig` settings present in `dotnet/runtime` (#12871) (Thanks @xtqqczze!) + +### Tests + +- Add new test for `Format-Custom` to avoid data loss (#11393) (Thanks @iSazonov!) + +### Build and Packaging Improvements + +
+ + +

Fixed upgrade code in MSI package.

+ +
+
    +
  • Change log for v7.1.0-preview.5 (Internal 11880)
  • +
  • Fix Path for the Preview MSI (#13070)
  • +
  • Correct stable and preview upgrade codes for MSI (#13036)
  • +
  • Changelog for `v7.1.0-preview.4` (Internal 11841)
  • +
  • Fix NuGet package compliance issues (#13045)
  • +
  • Bump xunit.runner.visualstudio from 2.4.1 to 2.4.2 (#12874)
  • +
  • Bump NJsonSchema from `10.1.21` to `10.1.23` (#13032) (#13022)
  • +
+ +
+ +### Documentation and Help Content + +- Fix links for MSI packages to point to `7.1.0-preview.3` (#13056) +- Add update `packages.microsoft.com` step to distribution request template. (#13008) +- Update `windows-core.md` (#13053) (Thanks @xtqqczze!) +- Add `@rjmholt` to maintainers list (#13033) +- Update docs for `v7.1.0-preview.4` release (#13028) + +## [7.1.0-preview.4] - 2020-06-25 + +### Breaking Changes + +- Make the switch parameter `-Qualifier` not positional for `Split-Path` (#12960) (Thanks @yecril71pl!) +- Resolve the working directory as literal path for `Start-Process` when it's not specified (#11946) (Thanks @NoMoreFood!) +- Make `-OutFile` parameter in web cmdlets to work like `-LiteralPath` (#11701) (Thanks @iSazonov!) + +### Engine Updates and Fixes + +- Ensure null-coalescing LHS is evaluated only once (#12667) +- Fix path handling bug in `PSTask` (#12554) (Thanks @IISResetMe!) +- Remove extra line before formatting group (#12163) (Thanks @iSazonov!) +- Make module formatting not generate error with strict mode (#11943) +- Adding more ETW logs to WSMan plugin (#12798) (Thanks @krishnayalavarthi!) +- Restrict loading of `amsi.dll` to `system32` folder (#12730) + +### General Cmdlet Updates and Fixes + +- Fix `NullReferenceException` in `CommandSearcher.GetNextCmdlet` (#12659) (Thanks @powercode!) +- Prevent `NullReferenceException` in Unix computer cmdlets with test hooks active (#12651) (Thanks @vexx32!) +- Fix issue in `Select-Object` where `Hashtable` members (e.g. `Keys`) cannot be used with `-Property` or `-ExpandProperty` (#11097) (Thanks @vexx32!) +- Fix conflicting shorthand switch `-w` for pwsh (#12945) +- Rename the `CimCmdlet` resource file (#12955) (Thanks @iSazonov!) +- Remove use of `Test-Path` in `ConciseView` (#12778) +- Flag `default` switch statement condition clause as keyword (#10487) (Thanks @msftrncs!) +- Add parameter `SchemaFile` to `Test-Json` cmdlet (#11934) (Thanks @beatcracker!) +- Bring back Certificate provider parameters (#10622) (Thanks @iSazonov!) +- Fix `New-Item` to create symbolic link to relative path target (#12797) (Thanks @iSazonov!) +- Add `CommandLine` property to Process (#12288) (Thanks @iSazonov!) +- Adds `-MaskInput` parameter to `Read-Host` (#10908) (Thanks @davinci26!) +- Change `CimCmdlets` to use `AliasAttribute` (#12617) (Thanks @thlac!) + +### Code Cleanup + +
+ + + +

We thank the following contributors!

+

@xtqqczze, @sethvs, @romero126, @kvprasoon, @powercode

+ +
+ +
    +
  • Use nameof operator (#12716) (Thanks @xtqqczze!)
  • +
  • Fix comments in Mshexpression.cs (#12711) (Thanks @sethvs!)
  • +
  • Formatting: remove duplicate semicolons (#12666) (Thanks @xtqqczze!)
  • +
  • Replace SortedList with Generic.SortedList<TKey,TValue> (#12954) (Thanks @xtqqczze!)
  • +
  • Use HashSet instead of Hashtable with null values (#12958) (Thanks @xtqqczze!)
  • +
  • Rename CopyItem.Tests.ps1 to Copy-Item.Tests.ps1 to match other tests (#10701) (Thanks @romero126!)
  • +
  • Fix RCS1114: Remove redundant delegate creation (#12917) (Thanks @xtqqczze!)
  • +
  • Code redundancy fixes (#12916) (Thanks @xtqqczze!)
  • +
  • Update the PowerShell modules to use the new Help URI (#12686)
  • +
  • Reorder modifiers according to preferred order (#12864) (Thanks @xtqqczze!)
  • +
  • Expand numberOfPowershellRefAssemblies list capacity (#12840) (Thanks @xtqqczze!)
  • +
  • Add readonly modifier to internal static members (#11777) (Thanks @xtqqczze!)
  • +
  • cleanup: Use coalesce expression (#12829) (Thanks @xtqqczze!)
  • +
  • Add missing assessibility modifiers (#12820) (Thanks @xtqqczze!)
  • +
  • Use t_ naming convention for ThreadStatic members (#12826) (Thanks @xtqqczze!)
  • +
  • Formatting: Add empty line between declarations (#12824) (Thanks @xtqqczze!)
  • +
  • Clarify defaultRefAssemblies list capacity in AddType.cs (#12520) (Thanks @xtqqczze!)
  • +
  • Fixing "Double "period" (..) in message for System.InvalidOperationException" (#12758) (Thanks @kvprasoon!)
  • +
  • Rethrow to preserve stack details for better maintainability (#12723) (Thanks @xtqqczze!)
  • +
  • Delete license.rtf (#12738) (Thanks @xtqqczze!)
  • +
  • Nullable annotations for CommandSearcher (#12733) (Thanks @powercode!)
  • +
  • Redundancy: Remove 'partial' modifier from type with a single part (#12725) (Thanks @xtqqczze!)
  • +
  • Remove phrase 'All rights reserved' from Microsoft copyright statements (#12722) (Thanks @xtqqczze!)
  • +
  • IDictionary -> IDictionary<string, FunctionInfo> for FunctionTable (#12658) (Thanks @powercode!)
  • +
+ +
+ +### Tools + +- Use correct isError parameter with Write-Log (#12989) +- Disable `NonPrivateReadonlyFieldsMustBeginWithUpperCaseLetter` rule in `StyleCop` (#12855) (Thanks @xtqqczze!) +- Add @TylerLeonhardt to PowerShell team list to correct changelog generation (#12927) +- Enable the upload of `ETW` traces to `CLR CAP` in Windows daily build (#12890) +- Prevent GitHub workflow for daily dotnet build updates from running in forks (#12763) (Thanks @bergmeister!) +- Add GitHub action for PR creation and `Wix` file generation logic (#12748) + +### Tests + +- Remove duplicate tests from `Measure-Object.Tests.ps1` (#12683) (Thanks @sethvs!) +- Fix tests to not write errors to console (#13010) +- Make sure tabcompletion tests run (#12981) +- Remove dependency on DNS for `Test-Connection` tests on macOS (#12943) +- Restore `markdownlint` tests (#12549) (Thanks @xtqqczze!) +- Wrap tests in pester blocks (#12700) (Thanks @xtqqczze!) + +### Build and Packaging Improvements + +
+ + + +

We thank the following contributors!

+

@iSazonov, @kvprasoon, @Saancreed, @heaths, @xtqqczze

+ +
+ +
    +
  • Update Distribution_Request.md
  • +
  • Bump NJsonSchema from 10.1.15 to 10.1.16 (#12685)
  • +
  • Disable uploading Symbols package (#12687)
  • +
  • Update .NET SDK version from 5.0.100-preview.5.20279.10 to 5.0.100-preview.6.20318.15 (#13018)
  • +
  • Remove component ref when re-generating the wix file (#13019)
  • +
  • Make sure icons are added to MSI staging folder (#12983)
  • +
  • Update DotnetRutimeMetadata.json to point to preview 6 (#12972)
  • +
  • Bump PSReadLine from 2.0.1 to 2.0.2 (#12909)
  • +
  • Bump NJsonSchema from 10.1.18 to 10.1.21 (#12944)
  • +
  • Check if Azure Blob exists before overwriting (#12921)
  • +
  • Enable skipped tests (#12894) (Thanks @iSazonov!)
  • +
  • Fix break in package build by pinning ffi version to 1.12 (#12889)
  • +
  • Upgrade APIScan version (#12876)
  • +
  • Make contributors unique in Release notes (#12878) (Thanks @kvprasoon!)
  • +
  • Update Linux daily CI to run in a single agent & collect traces (#12866)
  • +
  • Update .NET SDK version from 5.0.100-preview.5.20278.13 to 5.0.100-preview.5.20279.10 (#12844) (Thanks @github-actions[bot]!)
  • +
  • Sign the MSIX files for the store (#12582)
  • +
  • Update the CI builds (#12830)
  • +
  • Update .NET SDK version from 5.0.100-preview.5.20272.6 to 5.0.100-preview.5.20278.13 (#12772) (Thanks @github-actions[bot]!)
  • +
  • Allow use of build module on unknown Linux distros (#11146) (Thanks @Saancreed!)
  • +
  • Fix MSI upgrade and shortcut issues (#12792) (Thanks @heaths!)
  • +
  • Bump NJsonSchema from 10.1.17 to 10.1.18 (#12812)
  • +
  • Update .NET SDK version from 5.0.100-preview.5.20269.29 to 5.0.100-preview.5.20272.6 (#12759) (Thanks @github-actions[bot]!)
  • +
  • Bump NJsonSchema from 10.1.16 to 10.1.17 (#12761)
  • +
  • Update to dotnet SDK 5.0.0-preview.5.20268.9 (#12740)
  • +
  • Remove assets\license.rtf (#12721) (Thanks @xtqqczze!)
  • +
  • Bump Microsoft.CodeAnalysis.CSharp from 3.5.0 to 3.6.0 (#12731)
  • +
+ +
+ +### Documentation and Help Content + +- Update `README` and `metadata` files for next release (#12717) +- Update `README.md` removing experimental status of `Arm` builds, but `Win-Arm64` is still preview for Stable release. (#12707) +- Add link to Github compare in changelog (#12713) (Thanks @xtqqczze!) +- Added missing changelog for v7.1.0-preview.2 (#12665) +- Update required Visual Studio version in build docs (#12628) (Thanks @xtqqczze!) +- minor update to Distribution_Request.md (#12705) (Thanks @kilasuit!) +- Update docs.microsoft.com links (#12653) (Thanks @xtqqczze!) +- Update change log for `6.2.5` release (#12670) +- Update `README.md` and `metadata.json` for next release (#12668) +- Merge 7.0.1 change log (#12669) +- Remove markdown unused definitions (#12656) (Thanks @xtqqczze!) +- Add HoloLens to list of PowerShell adopters (#12940) (Thanks @reynoldsbd!) +- Update `README.md` and `metadata.json` for next releases (#12939) +- Fix broken link in `README.md` (#12887) (Thanks @xtqqczze!) +- Minor typo corrections in Distribution Request Issue Templates (#12744) (Thanks @corbob!) +- Correct 'review-for-comments' in `Governance.md` (#11035) (Thanks @MarvTheRobot!) +- Fix markdown ordered lists (#12657) (Thanks @xtqqczze!) +- Fix broken `docs.microsoft.com` link (#12776) (Thanks @xtqqczze!) +- Replace link to Slack with link to PowerShell Virtual User Group (#12786) (Thanks @xtqqczze!) +- Update `LICENSE.txt` so that it's recognized as MIT (#12729) + +## [7.1.0-preview.3] - 2020-05-14 + +### Breaking Changes + +- Fix string parameter binding for `BigInteger` numeric literals (#11634) (Thanks @vexx32!) + +### Engine Updates and Fixes + +- Set correct `PSProvider` full name at module load time (#11813) (Thanks @iSazonov!) + +### Experimental Features + +- Support passing `PSPath` to native commands (#12386) + +### General Cmdlet Updates and Fixes + +- Fix incorrect index in format string in ParameterBinderBase (#12630) (Thanks @powercode!) +- Copy the `CommandInfo` property in `Command.Clone()` (#12301) (Thanks @TylerLeonhardt!) +- Apply `-IncludeEqual` in `Compa-Object` when `-ExcludeDifferent` is specified (#12317) (Thanks @davidseibel!) +- Change `Get-FileHash` to close file handles before writing output (#12474) (Thanks @HumanEquivalentUnit!) +- Fix inconsistent exception message in `-replace` operator (#12388) (Thanks @jackdcasey!) + +### Code Cleanup + +
+ + + +

We thank the following contributors!

+

@xtqqczze, @RDIL, @powercode, @xtqqczze, @xtqqczze

+ +
+ +
    +
  • Replace Unicode non-breaking space character with space (#12576) (Thanks @xtqqczze!)
  • +
  • Remove unused New-DockerTestBuild.ps1 (#12610) (Thanks @RDIL!)
  • +
  • Annotate Assert methods for better code analysis (#12618) (Thanks @powercode!)
  • +
  • Use correct casing for cmdlet names and parameters in *.ps1 files throughout the codebase (#12584) (Thanks @xtqqczze!)
  • +
  • Document why PackageVersion is used in PowerShell.Common.props (#12523) (Thanks @xtqqczze!)
  • +
+ +
+ +### Tools + +- Update `@PoshChan` config to include `SSH` (#12526) (Thanks @vexx32!) +- Update log message in `Start-PSBootstrap` (#12573) (Thanks @xtqqczze!) +- Add the `.NET SDK` installation path to the current process path in `tools/UpdateDotnetRuntime.ps1` (#12525) + +### Tests + +- Make CIM tab completion test case insensitive (#12636) +- Mark ping tests as Pending due to stability issues in macOS (#12504) + +### Build and Packaging Improvements + +
+ + + +

We thank the following contributors!

+

@jcotton42, @iSazonov, @iSazonov, @iSazonov

+ +
+ +
    +
  • Update build to use the new .NET SDK 5.0.100-preview.4.20258.7 (#12637)
  • +
  • Bump NJsonSchema from 10.1.14 to 10.1.15 (#12608)
  • +
  • Bump NJsonSchema from 10.1.13 to 10.1.14 (#12598)
  • +
  • Bump NJsonSchema from 10.1.12 to 10.1.13 (#12583)
  • +
  • Update the build to sign any unsigned files as 3rd party Dlls (#12581)
  • +
  • Update .NET SDK to 5.0.100-preview.4.20229.10 (#12538)
  • +
  • Add ability to Install-Dotnet to specify directory (#12469)
  • +
  • Allow / in relative paths for using module (#7424) (#12492) (Thanks @jcotton42!)
  • +
  • Update dotnet metadata for next channel for automated updates (#12502)
  • +
  • Bump .NET to 5.0.0-preview.4 (#12507)
  • +
  • Bump Microsoft.ApplicationInsights from 2.13.1 to 2.14.0 (#12479)
  • +
  • Bump PackageManagement from 1.4.6 to 1.4.7 in /src/Modules (#12506)
  • +
  • Bump Xunit.SkippableFact from 1.3.12 to 1.4.8 (#12480)
  • +
  • Fix quotes to allow variable expansion (#12512)
  • +
  • Use new TargetFramework as net5.0 in packaging scripts (#12503) (Thanks @iSazonov!)
  • +
  • Use new value for TargetFramework as net5.0 instead of netcoreapp5.0 (#12486) (Thanks @iSazonov!)
  • +
  • Disable PublishReadyToRun for framework dependent packages (#12450)
  • +
  • Add dependabot rules to ignore updates from .NET (#12466)
  • +
  • Update README.md and metadata.json for upcoming release (#12441)
  • +
  • Turn on ReadyToRun (#12361) (Thanks @iSazonov!)
  • +
  • Add summary to compressed sections of change log (#12429)
  • +
+ +
+ +### Documentation and Help Content + +- Add link to life cycle doc to distribution request template (#12638) +- Update TFM reference in build docs (#12514) (Thanks @xtqqczze!) +- Fix broken link for blogs in documents (#12471) + +## [7.1.0-preview.2] - 2020-04-23 + +### Breaking Changes + +- On Windows, `Start-Process` creates a process environment with + all the environment variables from current session, + using `-UseNewEnvironment` creates a new default process environment (#10830) (Thanks @iSazonov!) +- Do not wrap return result to `PSObject` when converting ScriptBlock to delegate (#10619) + +### Engine Updates and Fixes + +- Allow case insensitive paths for determining `PSModulePath` (#12192) +- Add PowerShell version 7.0 to compatible version list (#12184) +- Discover assemblies loaded by `Assembly.Load(byte[])` and `Assembly.LoadFile` (#12203) + +### General Cmdlet Updates and Fixes + +- Fix `WinCompat` module loading to treat PowerShell 7 modules with higher priority (#12269) +- Implement `ForEach-Object -Parallel` runspace reuse (#12122) +- Fix `Get-Service` to not modify collection while enumerating it (#11851) (Thanks @NextTurn!) +- Clean up the IPC named pipe on PowerShell exit (#12187) +- Fix `` detection regex in web cmdlets (#12099) (Thanks @vexx32!) +- Allow shorter signed hex literals with appropriate type suffixes (#11844) (Thanks @vexx32!) +- Update `UseNewEnvironment` parameter behavior of `Start-Process` cmdlet on Windows (#10830) (Thanks @iSazonov!) +- Add `-Shuffle` switch to `Get-Random` command (#11093) (Thanks @eugenesmlv!) +- Make `GetWindowsPowerShellModulePath` compatible with multiple PS installations (#12280) +- Fix `Start-Job` to work on systems that don't have Windows PowerShell registered as default shell (#12296) +- Specifying an alias and `-Syntax` to `Get-Command` returns the aliased commands syntax (#10784) (Thanks @ChrisLGardner!) +- Make CSV cmdlets work when using `-AsNeeded` and there is an incomplete row (#12281) (Thanks @iSazonov!) +- In local invocations, do not require `-PowerShellVersion 5.1` for `Get-FormatData` in order to see all format data. (#11270) (Thanks @mklement0!) +- Added Support For Big Endian `UTF-32` (#11947) (Thanks @NoMoreFood!) +- Fix possible race that leaks PowerShell object dispose in `ForEach-Object -Parallel` (#12227) +- Add `-FromUnixTime` to `Get-Date` to allow Unix time input (#12179) (Thanks @jackdcasey!) +- Change default progress foreground and background colors to provide improved contrast (#11455) (Thanks @rkeithhill!) +- Fix `foreach -parallel` when current drive is not available (#12197) +- Do not wrap return result to `PSObject` when converting `ScriptBlock` to `delegate` (#10619) +- Don't write DNS resolution errors on `Test-Connection -Quiet` (#12204) (Thanks @vexx32!) +- Use dedicated threads to read the redirected output and error streams from the child process for out-of-proc jobs (#11713) + +### Code Cleanup + +
+ + + +

We thank the following contributors!

+

@ShaydeNofziger, @RDIL

+ +
+ +
    +
  • Fix erroneous comment in tokenizer.cs (#12206) (Thanks @ShaydeNofziger!)
  • +
  • Fix terms checker issues (#12189)
  • +
  • Update copyright notice to latest guidance (#12190)
  • +
  • CodeFactor cleanup (#12251) (Thanks @RDIL!)
  • +
+ +
+ +### Tools + +- Update .NET dependency update script to include test `csproj` files (#12372) +- Scripts to update to .NET prerelease version (#12284) + +### Tests + +- Pin major Pester version to 4 to prevent breaking changes caused by upcoming release of v5 (#12262) (Thanks @bergmeister!) + +### Build and Packaging Improvements + +
+ + + +

We thank the following contributors!

+

@rkitover, @bergmeister

+ +
+ +
    +
  • Add the nuget.config from root to the temporary build folder (#12394)
  • +
  • Bump System.IO.Packaging (#12365)
  • +
  • Bump Markdig.Signed from 0.18.3 to 0.20.0 (#12379)
  • +
  • Bump to .NET 5 Preview 3 pre-release (#12353)
  • +
  • Bump PowerShellGet from 2.2.3 to 2.2.4 (#12342)
  • +
  • Linux: Initial support for Gentoo installations. (#11429) (Thanks @rkitover!)
  • +
  • Upgrade to .NET 5 Preview 2 (#12250) (Thanks @bergmeister!)
  • +
  • Fix the Sync PSGalleryModules to Artifacts build (#12277)
  • +
  • Bump PSReadLine from 2.0.0 to 2.0.1 (#12243)
  • +
  • Bump NJsonSchema from 10.1.11 to 10.1.12 (#12230)
  • +
  • Update change log generation script to support collapsible sections (#12214)
  • +
+ +
+ +### Documentation and Help Content + +- Add documentation for `WebResponseObject` and `BasicHtmlWebResponseObject` properties (#11876) (Thanks @kevinoid!) +- Add Windows 10 IoT Core reference in `Adopters.md` (#12266) (Thanks @parameshbabu!) +- Update `README.md` and `metadata.json` for `7.1.0-preview.1` (#12211) + +## [7.1.0-preview.1] - 2020-03-26 + +### Breaking Changes + +- Use invariant culture string conversion for `-replace` operator (#10954) (Thanks @iSazonov!) + +### Engine Updates and Fixes + +- Revert the PRs that made `DBNull.Value` and `NullString.Value` treated as `$null` (#11648) + +### Experimental Features + +- Use invariant culture string conversion for `-replace` operator (#10954) (Thanks @iSazonov!) + +### General Cmdlet Updates and Fixes + +- Fix an operator preference order issue in binder code (#12075) (Thanks @DamirAinullin!) +- Fix `NullReferenceException` when binding common parameters of type `ActionPreference` (#12124) +- Fix default formatting for deserialized `MatchInfo` (#11728) (Thanks @iSazonov!) +- Use asynchronous streams in `Invoke-RestMethod` (#11095) (Thanks @iSazonov!) +- Address UTF-8 Detection In `Get-Content -Tail` (#11899) (Thanks @NoMoreFood!) +- Handle the `IOException` in `Get-FileHash` (#11944) (Thanks @iSazonov!) +- Change `PowerShell Core` to `PowerShell` in a resource string (#11928) (Thanks @alexandair!) +- Bring back `MainWindowTitle` in `PSHostProcessInfo` (#11885) (Thanks @iSazonov!) +- Miscellaneous minor updates to Windows Compatibility (#11980) +- Fix `ConciseView` to split `PositionMessage` using `[Environment]::NewLine` (#12010) +- Remove network hop restriction for interactive sessions (#11920) +- Fix `NullReferenceException` in `SuspendStoppingPipeline()` and `RestoreStoppingPipeline()` (#11870) (Thanks @iSazonov!) +- Generate GUID for `FormatViewDefinition` `InstanceId` if not provided (#11896) +- Fix `ConciseView` where error message is wider than window width and doesn't have whitespace (#11880) +- Allow cross-platform `CAPI-compatible` remote key exchange (#11185) (Thanks @silijon!) +- Fix error message (#11862) (Thanks @NextTurn!) +- Fix `ConciseView` to handle case where there isn't a console to obtain the width (#11784) +- Update `CmsCommands` to use Store vs certificate provider (#11643) (Thanks @mikeTWC1984!) +- Enable `pwsh` to work on Windows systems where `mpr.dll` and STA is not available (#11748) +- Refactor and implement `Restart-Computer` for `Un*x` and macOS (#11319) +- Add an implementation of `Stop-Computer` for Linux and macOS (#11151) +- Fix `help` function to check if `less` is available before using (#11737) +- Update `PSPath` in `certificate_format_ps1.xml` (#11603) (Thanks @xtqqczze!) +- Change regular expression to match relation-types without quotes in Link header (#11711) (Thanks @Marusyk!) +- Fix error message during symbolic link deletion (#11331) +- Add custom `Selected.*` type to `PSCustomObject` in `Select-Object` only once (#11548) (Thanks @iSazonov!) +- Add `-AsUTC` to the `Get-Date` cmdlet (#11611) +- Fix grouping behavior with Boolean values in `Format-Hex` (#11587) (Thanks @vexx32!) +- Make `Test-Connection` always use the default synchronization context for sending ping requests (#11517) +- Correct startup error messages (#11473) (Thanks @iSazonov!) +- Ignore headers with null values in web cmdlets (#11424) (Thanks @iSazonov!) +- Re-add check for `Invoke-Command` job dispose. (#11388) +- Revert "Update formatter to not write newlines if content is empty (#11193)" (#11342) (Thanks @iSazonov!) +- Allow `CompleteInput` to return results from `ArgumentCompleter` when `AST` or Script has matching function definition (#10574) (Thanks @M1kep!) +- Update formatter to not write new lines if content is empty (#11193) + +### Code Cleanup + +
+ +
    +
  • Use span-based overloads (#11884) (Thanks @iSazonov!)
  • +
  • Use new string.Split() overloads (#11867) (Thanks @iSazonov!)
  • +
  • Remove unreachable DSC code (#12076) (Thanks @DamirAinullin!)
  • +
  • Remove old dead code from FullCLR (#11886) (Thanks @iSazonov!)
  • +
  • Use Dictionary.TryAdd() where possible (#11767) (Thanks @iSazonov!)
  • +
  • Use Environment.NewLine instead of hard-coded linefeed in ParseError.ToString (#11746)
  • +
  • Fix FileSystem provider error message (#11741) (Thanks @iSazonov!)
  • +
  • Reformat code according to EditorConfig rules (#11681) (Thanks @xtqqczze!)
  • +
  • Replace use of throw GetExceptionForHR with ThrowExceptionForHR (#11640) (Thanks @xtqqczze!)
  • +
  • Refactor delegate types to lambda expressions (#11690) (Thanks @xtqqczze!)
  • +
  • Remove Unicode BOM from text files (#11546) (Thanks @xtqqczze!)
  • +
  • Fix Typo in Get-ComputerInfo cmdlet description (#11321) (Thanks @doctordns!)
  • +
  • Fix typo in description for Get-ExperimentalFeature PSWindowsPowerShellCompatibility (#11282) (Thanks @alvarodelvalle!)
  • +
  • Cleanups in command discovery (#10815) (Thanks @iSazonov!)
  • +
  • Review CurrentCulture (#11044) (Thanks @iSazonov!)
  • +
+ +
+ +### Tools + +- Change recommended VS Code extension name from `ms-vscode.csharp` to `ms-dotnettools.csharp` (#12083) (Thanks @devlead!) +- Specify `csharp_preferred_modifier_order` in `EditorConfig` (#11775) (Thanks @xtqqczze!) +- Update `.editorconfig` (#11675) (Thanks @xtqqczze!) +- Enable `EditorConfig` support in `OmniSharp` (#11627) (Thanks @xtqqczze!) +- Specify charset in `.editorconfig` as `utf-8` (no BOM) (#11654) (Thanks @xtqqczze!) +- Configure the issue label bot (#11527) +- Avoid variable names that conflict with automatic variables (#11392) (Thanks @xtqqczze!) + +### Tests + +- Add empty `preview.md` file to fix broken link (#12041) +- Add helper functions for SSH remoting tests (#11955) +- Add new tests for `Get-ChildItem` for `FileSystemProvider` (#11602) (Thanks @iSazonov!) +- Ensure that types referenced by `PowerShellStandard` are present (#10634) +- Check state and report reason if it's not "opened" (#11574) +- Fixes for running tests on Raspbian (#11661) +- Unify pester test syntax for the arguments of `-BeOfType` (#11558) (Thanks @xtqqczze!) +- Correct casing for automatic variables (#11568) (Thanks @iSazonov!) +- Avoid variable names that conflict with automatic variables part 2 (#11559) (Thanks @xtqqczze!) +- Update pester syntax to v4 (#11544) (Thanks @xtqqczze!) +- Allow error 504 (Gateway Timeout) in `markdown-link` tests (#11439) (Thanks @xtqqczze!) +- Re-balance CI tests (#11420) (Thanks @iSazonov!) +- Include URL in the markdown-links test error message (#11438) (Thanks @xtqqczze!) +- Use CIM cmdlets instead of WMI cmdlets in tests (#11423) (Thanks @xtqqczze!) + +### Build and Packaging Improvements + +
+ +
    +
  • Put symbols in separate package (#12169)
  • +
  • Disable x86 PDB generation (#12167)
  • +
  • Bump NJsonSchema from 10.1.5 to 10.1.11 (#12050) (#12088) (#12166)
  • +
  • Create crossgen symbols for Windows x64 and x86 (#12157)
  • +
  • Move to .NET 5 preview.1 (#12140)
  • +
  • Bump Microsoft.CodeAnalysis.CSharp from 3.4.0 to 3.5.0 (#12136)
  • +
  • Move to standard internal pool for building (#12119)
  • +
  • Fix package syncing to private Module Feed (#11841)
  • +
  • Add Ubuntu SSH remoting tests CI (#12033)
  • +
  • Bump Markdig.Signed from 0.18.1 to 0.18.3 (#12078)
  • +
  • Fix MSIX packaging to determine if a Preview release by inspecting the semantic version string (#11991)
  • +
  • Ignore last exit code in the build step as dotnet may return error when SDK is not installed (#11972)
  • +
  • Fix daily package build (#11882)
  • +
  • Fix package sorting for syncing to private Module Feed (#11838)
  • +
  • Set StrictMode version 3.0 (#11563) (Thanks @xtqqczze!)
  • +
  • Bump .devcontainer version to dotnet 3.1.101 (#11707) (Thanks @Jawz84!)
  • +
  • Move to version 3 of AzFileCopy (#11697)
  • +
  • Update README.md and metadata.json for next release (#11664)
  • +
  • Code Cleanup for environment data gathering in build.psm1 (#11572) (Thanks @xtqqczze!)
  • +
  • Update Debian Install Script To Support Debian 10 (#11540) (Thanks @RandomNoun7!)
  • +
  • Update ADOPTERS.md (#11261) (Thanks @edyoung!)
  • +
  • Change back to use powershell.exe in 'SetVersionVariables.yml' to unblock daily build (#11207)
  • +
  • Change to use pwsh to have consistent JSON conversion for DateTime (#11126)
  • +
+ +
+ +### Documentation and Help Content + +- Replace `VSCode` link in `CONTRIBUTING.md` (#11475) (Thanks @stevend811!) +- Remove the version number of PowerShell from LICENSE (#12019) +- Add the 7.0 change log link to `CHANGELOG/README.md` (#12062) (Thanks @LabhanshAgrawal!) +- Improvements to the contribution guide (#12086) (Thanks @ShaydeNofziger!) +- Update the doc about debugging dotnet core in VSCode (#11969) +- Update `README.md` and `metadata.json` for the next release (#11918) (#11992) +- Update `Adopters.md` to include info on Azure Pipelines and GitHub Actions (#11888) (Thanks @alepauly!) +- Add information about how Amazon AWS uses PowerShell. (#11365) (Thanks @bpayette!) +- Add link to .NET CLI version in build documentation (#11725) (Thanks @joeltankam!) +- Added info about `DeploymentScripts` in `ADOPTERS.md` (#11703) +- Update `CHANGELOG.md` for `6.2.4` release (#11699) +- Update `README.md` and `metadata.json` for next release (#11597) +- Update the breaking change definition (#11516) +- Adding System Frontier to the PowerShell Core adopters list `ADOPTERS.md` (#11480) (Thanks @OneScripter!) +- Update `ChangeLog`, `README.md` and `metadata.json` for `7.0.0-rc.1` release (#11363) +- Add `AzFunctions` to `ADOPTERS.md` (#11311) (Thanks @Francisco-Gamino!) +- Add `Universal Dashboard` to `ADOPTERS.md` (#11283) (Thanks @adamdriscoll!) +- Add `config.yml` for `ISSUE_TEMPLATE` so that Doc, Security, Support, and Windows PowerShell issues go to URLs (#11153) +- Add `Adopters.md` file (#11256) +- Update `Readme.md` for `preview.6` release (#11108) +- Update `SUPPORT.md` (#11101) (Thanks @mklement0!) +- Update `README.md` (#11100) (Thanks @mklement0!) + +[7.1.0-preview.5]: https://github.com/PowerShell/PowerShell/compare/v7.1.0-preview.4...v7.1.0-preview.5 +[7.1.0-preview.4]: https://github.com/PowerShell/PowerShell/compare/v7.1.0-preview.3...v7.1.0-preview.4 +[7.1.0-preview.3]: https://github.com/PowerShell/PowerShell/compare/v7.1.0-preview.2...v7.1.0-preview.3 +[7.1.0-preview.2]: https://github.com/PowerShell/PowerShell/compare/v7.1.0-preview.1...v7.1.0-preview.2 +[7.1.0-preview.1]: https://github.com/PowerShell/PowerShell/compare/v7.0.0-preview.6...v7.1.0-preview.1 + diff --git a/CHANGELOG/7.2.md b/CHANGELOG/7.2.md new file mode 100644 index 00000000000..c9bde27a841 --- /dev/null +++ b/CHANGELOG/7.2.md @@ -0,0 +1,1863 @@ +# 7.2 Changelog + +## [7.2.23] - 2024-08-20 + +### Build and Packaging Improvements + +
+ + + +

Bump .NET to 6.0.425

+ +
+ +
    +
  • Add feature flags for removing network isolation
  • +
  • Bump PackageManagement to 1.4.8.1 (#24162)
  • +
  • Bump .NET to 6.0.425 (#24161)
  • +
  • Skip build steps that do not have exe packages (#23945) (#24156)
  • +
  • Use correct signing certificates for RPM and DEBs (#21522) (#24154)
  • +
  • Fix exe signing with third party signing for WiX engine (#23878) (#24155)
  • +
  • Fix error in the vPack release, debug script that blocked release (#23904)
  • +
  • Add vPack release (#23898)
  • +
  • Fix nuget publish download path
  • +
  • Use correct signing certificates for RPM and DEBs (#21522)
  • +
+ +
+ +### Documentation and Help Content + +- Update docs sample nuget.config (#24109) (#24157) + +[7.2.23]: https://github.com/PowerShell/PowerShell/compare/v7.2.22...v7.2.23 + +## [7.2.22] - 2024-07-18 + +### Engine Updates and Fixes + +- Resolve paths correctly when importing files or files referenced in the module manifest (Internal 31777 31788) + +### Build and Packaging Improvements + +
+ + + +

Bump .NET to 6.0.424

+ +
+ +
    +
  • Enumerate over all signed zip packages
  • +
  • Update TPN for release v7.2.22 (Internal 31807)
  • +
  • Update CG Manifest for 7.2.22 (Internal 31804)
  • +
  • Add macos signing for package files (#24015) (#24058)
  • +
  • Update .NET version to 6.0.424 (#24033)
  • +
+ +
+ +[7.2.22]: https://github.com/PowerShell/PowerShell/compare/v7.2.21...v7.2.22 + +## [7.2.21] - 2024-06-18 + +### Build and Packaging Improvements + +
+ + + +

Release 7.2.20 broadly (was previously just released to the .NET SDK containers.)

Release 7.2.20 broadly

+ +
+ +
    +
  • Fixes for change to new Engineering System.
  • +
  • Create powershell.config.json for PowerShell.Windows.x64 global tool (#23941) (#23942)
  • +
+ +
+ +[7.2.21]: https://github.com/PowerShell/PowerShell/compare/v7.2.19...v7.2.21 + +## [7.2.20] - 2024-06-06 + +Limited release for dotnet SDK container images. + + + +

Update .NET 6 to 6.0.31 and how global tool is generated

+ +
+ +
    +
  • Fixes for change to new Engineering System.
  • +
  • Create powershell.config.json for PowerShell.Windows.x64 global tool (#23941) (#23942)
  • +
  • Update change log for v7.2.20
  • +
  • Update installation on Wix module (#23808)
  • +
  • Updates to package and release pipelines (#23800)
  • +
  • Use feed with Microsoft Wix toolset (#21651)
  • +
  • update wix package install (#21537)
  • +
  • Use PSScriptRoot to find path to Wix module (#21611)
  • +
  • Create the Windows.x64 global tool with shim for signing (#21559)
  • +
  • Add branch counter variables for daily package builds (#21523)
  • +
  • Official PowerShell Package pipeline (#21504)
  • +
  • Add a PAT for fetching PMC cli (#21503)
  • +
  • [StepSecurity] Apply security best practices (#21480)
  • +
  • Fix build failure due to missing reference in GlobalToolShim.cs (#21388)
  • +
  • Fix argument passing in GlobalToolShim (#21333)
  • +
  • Update .NET 6 to 6.0.31 (Internal 31302)
  • +
  • Re-apply the OneBranch changes to packaging.psm1"
  • +
+ + + +[7.2.20]: https://github.com/PowerShell/PowerShell/compare/v7.2.19...v7.2.20 + +## [7.2.19] - 2024-04-11 + +### Build and Packaging Improvements + +
+ + + +

Bump to .NET 6.0.29

+ +
+ +
    +
  • Allow artifacts produced by partially successful builds to be consumed by release pipeline
  • +
  • Update SDK, dependencies and cgmanifest for 7.2.19
  • +
  • Revert changes to packaging.psm1
  • +
  • Verify environment variable for OneBranch before we try to copy (#21441)
  • +
  • Multiple fixes in official build pipeline (#21408)
  • +
  • Add dotenv install as latest version does not work with current Ruby version (#21239)
  • +
  • PowerShell co-ordinated build OneBranch pipeline (#21364)
  • +
  • Remove surrogateFile setting of APIScan (#21238)
  • +
+ +
+ +[7.2.19]: https://github.com/PowerShell/PowerShell/compare/v7.2.18...v7.2.19 + +## [7.2.18] - 2024-01-11 + +### Build and Packaging Improvements + +
+ + + +

Bump .NET to 6.0.418

+ +
+ +
    +
  • Update ThirdPartyNotices.txt for v7.2.18 (Internal 29173)
  • +
  • Update cgmanifest.json for v7.2.18 release (Internal 29161)
  • +
  • Update .NET SDK to 6.0.418 (Internal 29141)
  • +
  • Back port 3 build changes to apiscan.yml (#21036)
  • +
  • Set the ollForwardOnNoCandidateFx in runtimeconfig.json to roll forward only on minor and patch versions (#20689)
  • +
  • Remove the ref folder before running compliance (#20373)
  • +
  • Fix the tab completion tests (#20867)
  • +
+ +
+ +[7.2.18]: https://github.com/PowerShell/PowerShell/compare/v7.2.17...v7.2.18 + +## [7.2.17] - 2023-11-16 + +### General Cmdlet Updates and Fixes + +- Redact Auth header content from ErrorRecord (Internal 28411) + +### Build and Packaging Improvements + +
+ + + +

Bump to .NET to version 6.0.417

+ +
+ +
    +
  • Bump to .NET 6.0.417 (Internal 28486)
  • +
  • Copy azure blob with PowerShell global tool to private blob and move to CDN during release (Internal 28450)
  • +
+ +
+ +[7.2.17]: https://github.com/PowerShell/PowerShell/compare/v7.2.16...v7.2.17 + +## [7.2.16] - 2023-10-26 + +### Build and Packaging Improvements + +
+ + + +

Update .NET 6 to version 6.0.416

+ +
+ +
    +
  • Fix release pipeline yaml
  • +
  • Fix issues with merging backports in packaging (Internal 28158)
  • +
  • Update .NET 6 and TPN (Internal 28149)
  • +
  • Add runtime and packaging type info for mariner2 arm64 (#19450) (#20564)
  • +
  • Add mariner arm64 to PMC release (#20176) (#20567)
  • +
  • Remove HostArchitecture dynamic parameter for osxpkg (#19917) (#20565)
  • +
  • Use fxdependent-win-desktop runtime for compliance runs (#20326) (#20568)
  • +
  • Add SBOM for release pipeline (#20519) (#20570)
  • +
  • Increase timeout when publishing packages to pacakages.microsoft.com (#20470) (#20569)
  • +
  • Add mariner arm64 package build to release build (#19946) (#20566)
  • +
+ +
+ +[7.2.16]: https://github.com/PowerShell/PowerShell/compare/v7.2.15...v7.2.16 + +## [7.2.15] - 2023-10-10 + +### Security Fixes + +- Block getting help from network locations in restricted remoting sessions (Internal 27699) + +### Build and Packaging Improvements + +
+ + + +

Build infrastructure maintenance

+ +
+ +
    +
  • Release build: Change the names of the PATs (#20315)
  • +
  • Switch to GitHub Action for linting markdown (#20309)
  • +
  • Put the calls to Set-AzDoProjectInfo and Set-AzDoAuthToken` in the right order (#20312)
  • +
+ +
+ +[7.2.15]: https://github.com/PowerShell/PowerShell/compare/v7.2.14...v7.2.15 + +## [7.2.14] - 2023-09-18 + +### Build and Packaging Improvements + +
+ + + +

Bump .NET SDK version to 6.0.414

+ +
+ +
    +
  • Update to use .NET SDK 6.0.414 (Internal 27575)
  • +
  • Enable vPack provenance data (#20242)
  • +
  • Start using new packages.microsoft.com CLI (#20241)
  • +
  • Remove spelling CI in favor of GitHub Action (#20239)
  • +
  • Make PR creation tool use --web because it is more reliable (#20238)
  • +
  • Update variable used to bypass the blocking check for multiple NuGet feeds (#20237)
  • +
  • Don't publish notice on failure because it prevents retry (#20236)
  • +
  • Publish rpm package for rhel9 (#20234)
  • +
  • Add ProductCode in registry for MSI install (#20233)
  • +
+ +
+ +### Documentation and Help Content + +- Update man page to match current help for pwsh (#20240) +- Update the link for getting started in `README.md` (#20235) + +[7.2.14]: https://github.com/PowerShell/PowerShell/compare/v7.2.13...v7.2.14 + +## [7.2.13] - 2023-07-13 + +### Tests + +- Increase the timeout to make subsystem tests more reliable (#19937) +- Increase the timeout when waiting for the event log (#19936) + +### Build and Packaging Improvements + +
+ + + +

Bump .NET SDK version to 6.0.412

+ +
+ +
    +
  • Update Notice file (#19956)
  • +
  • Update cgmanifest (#19938)
  • +
  • Bump to 6.0.412 SDK (#19933)
  • +
  • Update variable used to bypass the blocking check for multiple NuGet feeds (#19935)
  • +
+ +
+ +[7.2.13]: https://github.com/PowerShell/PowerShell/compare/v7.2.12...v7.2.13 + +## [7.2.12] - 2023-06-27 + +### Build and Packaging Improvements + +
+ + + +

Bump .NET version to 6.0.411

+ +
+ +
    +
  • Disable SBOM signing for CI and add extra files for packaging tests (#19729)
  • +
  • Update ThirdPartyNotices (Internal 26349)
  • +
  • Update the cgmanifest
  • +
  • Add PoolNames variable group to compliance pipeline (#19408)
  • +
  • Add tool to trigger license information gathering for NuGet modules (#18827)
  • +
  • Update to .NET 6.0.410 (#19798)
  • +
  • Always regenerate files wxs fragment (#19803)
  • +
  • Add prompt to fix conflict during backport (#19583)
  • +
  • Add backport function to release tools (#19568)
  • +
  • Do not remove penimc_cor3.dll from build (#18438)
  • +
  • Remove unnecessary native dependencies from the package (#18213)
  • +
  • Delete symbols on Linux as well (#19735)
  • +
  • Bump Microsoft.PowerShell.MarkdownRender (#19751)
  • +
  • Backport compliance changes (#19719)
  • +
  • Delete charset regular expression test (#19585)
  • +
  • Fix issue with merge of 19068 (#19586)
  • +
  • Update the team member list in releaseTools.psm1 (#19574)
  • +
  • Verify that packages have license data (#19543) (#19575)
  • +
  • Update experimental-feature.json (#19581)
  • +
  • Fix the regular expression used for package name check in vPack build (#19573)
  • +
  • Make the vPack PAT library more obvious (#19572)
  • +
  • Add an explicit manual stage for changelog update (#19551) (#19567)
  • +
+ +
+ +[7.2.12]: https://github.com/PowerShell/PowerShell/compare/v7.2.11...v7.2.12 + +## [7.2.11] - 2023-04-12 + +### Build and Packaging Improvements + +
+ + + +

Bump .NET version to 6.0.16

+ +
+ +
    +
  • Update ThirdPartyNotices.txt
  • +
  • Update cgmanifest.json
  • +
  • Fix the template that creates nuget package
  • +
  • Update the wix file
  • +
  • Update .NET SDK to 6.0.408
  • +
  • Fix the build script and signing template
  • +
  • Fix stage dependencies and typo in release build (#19353)
  • +
  • Fix issues in release build and release pipeline (#19338)
  • +
  • Restructure the package build to simplify signing and packaging stages (#19321)
  • +
  • Skip VT100 tests on Windows Server 2012R2 as console does not support it (#19413)
  • +
  • Improve package management acceptance tests by not going to the gallery (#19412)
  • +
  • Test fixes for stabilizing tests (#19068)
  • +
  • Add stage for symbols job in Release build (#18937)
  • +
  • Use reference assemblies generated by dotnet (#19302)
  • +
  • Add URL for all distributions (#19159)
  • +
  • Update release pipeline to use Approvals and automate some manual tasks (#17837)
  • +
+ +
+ +[7.2.11]: https://github.com/PowerShell/PowerShell/compare/v7.2.10...v7.2.11 + +## [7.2.10] - 2023-02-23 + +### Build and Packaging Improvements + +
+ + + +

Bump .NET version to 6.0.14

+ +
+ +
    +
  • Fixed package names verification to support multi-digit versions (#17220)
  • +
  • Add pipeline secrets (from #17837) (Internal 24413)
  • +
  • Update to azCopy 10 (#18509)
  • +
  • Update third party notices for v7.2.10 (Internal 24346)
  • +
  • Update cgmanifest for v7.2.10 (Internal 24333)
  • +
  • Pull latest patches for 7.2.10 dependencies (Internal 24325)
  • +
  • Update SDK to 6.0.406 for v7.2.10 (Internal 24324)
  • +
  • Add test for framework dependent package in release pipeline (#18506) (#19114)
  • +
  • Mark 7.2.x releases as latest LTS but not latest stable (#19069)
  • +
+ +
+ +[7.2.10]: https://github.com/PowerShell/PowerShell/compare/v7.2.9...v7.2.10 + +## [7.2.9] - 2023-01-24 + +### Engine Updates and Fixes + +- Fix for JEA session leaking functions (Internal 23821 & 23819) + +### General Cmdlet Updates and Fixes + +- Correct incorrect cmdlet name in script (#18919) + +### Build and Packaging Improvements + +
+ + + +

Bump .NET version to 6.0.13

+ +
+ +
    +
  • Create test artifacts for windows arm64 (#18932)
  • +
  • Update dependencies for .NET release (Internal 23816)
  • +
  • Don't install based on build-id for RPM (#18921)
  • +
  • Apply expected file permissions to linux files after authenticode signing (#18922)
  • +
  • Add authenticode signing for assemblies on linux builds (#18920)
  • +
+ +
+ +[7.2.9]: https://github.com/PowerShell/PowerShell/compare/v7.2.8...v7.2.9 + +## [7.2.8] - 2022-12-13 + +### Engine Updates and Fixes + +- Remove TabExpansion for PSv2 from remote session configuration (Internal 23294) + +### Build and Packaging Improvements + +
+ + + +

Bump .NET SDK to 6.0.403

+ +
+ +
    +
  • Update CGManifest and ThirdPartyNotices
  • +
  • Update Microsoft.CSharp from 4.3.0 to 4.7.0
  • +
  • Update to latest SDK (#18610)
  • +
  • Allow two-digit revisions in vPack package validation pattern (#18569)
  • +
  • Update outdated dependencies (#18576)
  • +
  • Work around args parsing issue (#18606)
  • +
  • Bump System.Data.SqlClient from 4.8.4 to 4.8.5 (#18515)
  • +
+ +
+ +[7.2.8]: https://github.com/PowerShell/PowerShell/compare/v7.2.7...v7.2.8 + +## [7.2.7] - 2022-10-20 + +### Engine Updates and Fixes + +- On Unix, explicitly terminate the native process during cleanup only if it's not running in background (#18280) +- Stop sending telemetry about `ApplicationType` (#18168) + +### General Cmdlet Updates and Fixes + +- Remove the 1-second minimum delay in `Invoke-WebRequest` for downloading small files, and prevent file-download-error suppression (#18170) +- Enable searching for assemblies in GAC_Arm64 on Windows (#18169) +- Fix error formatting to use color defined in `$PSStyle.Formatting` (#18287) + +### Tests + +- Use Ubuntu 20.04 for SSH remoting test (#18289) + +### Build and Packaging Improvements + +
+ + + +

Bump .NET to version 6.0.402 (#18188)(#18290)

+ +
+ +
    +
  • Update cgmanifest (#18319)
  • +
  • Fix build.psm1 to find the required .NET SDK version when a higher version is installed (#17299) (#18282)
  • +
  • Update MSI exit message (#18173)
  • +
  • Remove XML files for min-size package (#18274)
  • +
  • Update list of PS team members in release tools (#18171)
  • +
  • Make the link to minimal package blob public during release (#18174)
  • +
  • Add XML reference documents to NuPkg files for SDK (#18172)
  • +
  • Update to use version 2.21.0 of Application Insights (#18271)
  • +
+ +
+ +[7.2.7]: https://github.com/PowerShell/PowerShell/compare/v7.2.6...v7.2.7 + +## [7.2.6] - 2022-08-11 + +### Engine Updates and Fixes + +- Fix `ForEach-Object -Parallel` when passing in script block variable (#16564) + +### General Cmdlet Updates and Fixes + +- Make `Out-String` and `Out-File` keep string input unchanged (#17455) +- Update regular expression used to remove ANSI escape sequences to be more specific to decoration and hyperlinks (#16811) +- Fix legacy `ErrorView` types to use `$host.PrivateData` colors (#17705) +- Fix `Export-PSSession` to not throw error when a rooted path is specified for `-OutputModule` (#17671) + +### Tests + +- Disable RPM SBOM test. (#17532) + +### Build and Packaging Improvements + +
+ + +

Bump .NET SDK to 6.0.8 (Internal 22065)

+

We thank the following contributors!

+

@tamasvajk

+
+ +
    +
  • Update Wix manifest
  • +
  • Add AppX capabilities in MSIX manifest so that PS7 can call the AppX APIs (#17416)
  • +
  • Use Quality only with Channel in dotnet-install (#17847)
  • +
  • Fix build.psm1 to not specify both version and quality for dotnet-install (#17589) (Thanks @tamasvajk!)
  • +
  • Install .NET 3.1 as it is required by the vPack task
  • +
+ +
+ +[7.2.6]: https://github.com/PowerShell/PowerShell/compare/v7.2.5...v7.2.6 + +## [7.2.5] - 2022-06-21 + +### Engine Updates and Fixes + +- Fix native library loading for osx-arm64 (#17495) (Thanks @awakecoding!) + +### Tests + +- Make Assembly Load Native test work on a FX Dependent Linux Install (#17496) +- Enable more tests to be run in a container. (#17294) +- Switch to using GitHub Action to verify Markdown links for PRs (#17281) +- Try to stabilize a few tests that fail intermittently (#17426) +- TLS test fix back-port (#17424) + +### Build and Packaging Improvements + +
+ + + +

Bump .NET SDK to 6.0.301 (Internal 21218)

+ +
+ +
    +
  • Update Wix file (Internal 21242)
  • +
  • Conditionally add output argument
  • +
  • Rename mariner package to cm (#17506)
  • +
  • Backport test fixes for 7.2 (#17494)
  • +
  • Update dotnet-runtime version (#17472)
  • +
  • Update to use windows-latest as the build agent image (#17418)
  • +
  • Publish preview versions of mariner to preview repository (#17464)
  • +
  • Move cgmanifest generation to daily (#17258)
  • +
  • Fix mariner mappings (#17413)
  • +
  • Make sure we execute tests on LTS package for older LTS releases (#17430)
  • +
  • Add a finalize template which causes jobs with issues to fail (#17428)
  • +
  • Make mariner packages Framework dependent (#17425)
  • +
  • Base work for adding mariner amd64 package (#17417)
  • +
+ +
+ +[7.2.5]: https://github.com/PowerShell/PowerShell/compare/v7.2.4...v7.2.5 + +## [7.2.4] - 2022-05-17 + +### Build and Packaging Improvements + +
+ + + +

Bump .NET SDK to 6.0.203

+ +
+ +
    +
  • Add mapping for Ubuntu22.04 Jammy (#17317)
  • +
  • Update to use mcr.microsoft.com (#17272)
  • +
  • Update third party notices
  • +
  • Update global.json and wix
  • +
  • Put Secure supply chain analysis at correct place (#17273)
  • +
  • Fix web cmdlets so that an empty Get does not include a content-length header (#16587)
  • +
  • Update package fallback list for Ubuntu (from those updated for Ubuntu 22.04) (deb) (#17217)
  • +
  • Add sha256 digests to RPM packages (#17215)
  • +
  • Allow multiple installations of dotnet. (#17216)
  • +
+ +
+ +[7.2.4]: https://github.com/PowerShell/PowerShell/compare/v7.2.3...v7.2.4 + +## [7.2.3] - 2022-04-26 + +### Engine Updates and Fixes + +- Fix for partial PowerShell module search paths, that can be resolved to CWD locations (Internal 20126) +- Do not include node names when sending telemetry. (#16981) to v7.2.3 (Internal 20188) + +### Tests + +- Re-enable `PowerShellGet` tests targeting PowerShell gallery (#17062) +- Skip failing scriptblock tests (#17093) + +### Build and Packaging Improvements + +
+ + + +

Bump .NET SDK to 6.0.202

+ +
+ +
    +
  • Making NameObscurerTelemetryInitializer internal - v7.2.3 (Internal 20239)
  • +
  • Updated files.wxs for 7.2.3 (Internal 20211)
  • +
  • Updated ThirdPartyNotices for 7.2.3 (Internal 20199)
  • +
  • Work around issue with notice generation
  • +
  • Replace . in notices container name
  • +
  • Updated cgmanifest.json by findMissingNotices.ps1 in v7.2.3 (Internal 20190)
  • +
  • v7.2.3 - Updated packages using dotnet-outdated global tool (Internal 20170)
  • +
  • Updated to .NET 6.0.4 / SDK 6.0.202 (Internal 20128)
  • +
  • Update dotnet-install script download link (Internal 19951)
  • +
  • Create checksum file for global tools (#17056) (Internal 19935)
  • +
  • Make sure global tool packages are published in stable build (Internal 19625)
  • +
  • Fix release pipeline (Internal 19617)
  • +
+ +
+ +[7.2.3]: https://github.com/PowerShell/PowerShell/compare/v7.2.2...v7.2.3 + +## [7.2.2] - 2022-03-16 + +### Build and Packaging Improvements + +
+ + + +

Bump .NET SDK to 6.0.201

+ +
+ +
    +
  • Update WiX file (Internal 19460)
  • +
  • Update .NET SDK version to 6.0.201 (Internal 19457)
  • +
  • Update experimental feature JSON files (#16838)
  • +
  • Ensure Alpine and ARM SKUs have powershell.config.json file with experimental features enabled (#16823)
  • +
  • Update the vmImage and PowerShell root directory for macOS builds (#16611)
  • +
  • Update macOS build image and root folder for build (#16609)
  • +
  • Remove WiX install (#16834)
  • +
  • Opt-in to build security monitoring (#16911)
  • +
  • Add SBOM manifest for release packages (#16641, #16711)
  • +
  • Add Linux package dependencies for packaging (#16807)
  • +
  • Switch to our custom images for build and release (#16801, #16580)
  • +
  • Remove all references to cmake for the builds in this repository (#16578)
  • +
  • Register NuGet source when generating CGManifest (#16570)
  • +
+ +
+ +[7.2.2]: https://github.com/PowerShell/PowerShell/compare/v7.2.1...v7.2.2 + +## [7.2.1] - 2021-12-14 + +### General Cmdlet Updates and Fixes + +- Remove declaration of experimental features in Utility module manifest as they are stable (#16460) +- Bring back pwsh.exe for framework dependent packages to support Start-Job (#16535) +- Change default for `$PSStyle.OutputRendering` to `Ansi` (Internal 18394) +- Update `HelpInfoUri` for 7.2 release (#16456) +- Fix typo for "privacy" in MSI installer (#16407) + +### Tests + +- Set clean state before testing `UseMU` in the MSI (#16543) + +### Build and Packaging Improvements + +
+ +
    +
  • Add explicit job name for approval tasks in Snap stage (#16579)
  • +
  • Fixing the build by removing duplicate TSAUpload entries (Internal 18399)
  • +
  • Port CGManifest fixes (Internal 18402)
  • +
  • Update CGManifest (Internal 18403)
  • +
  • Updated package dependencies for 7.2.1 (Internal 18388)
  • +
  • Use different containers for different branches (#16434)
  • +
  • Use notice task to generate license assuming CGManifest contains all components (#16340)
  • +
  • Create compliance build (#16286)
  • +
  • Update release instructions with link to new build (#16419)
  • +
  • Add diagnostics used to take corrective action when releasing buildInfoJson (#16404)
  • +
  • vPack release should use buildInfoJson new to 7.2 (#16402)
  • +
  • Add checkout to build json stage to get ci.psm1 (#16399)
  • +
  • Update the usage of metadata.json for getting LTS information (#16381)
  • +
  • Move mapping file into product repository and add Debian 11 (#16316)
  • +
+ +
+ +[7.2.1]: https://github.com/PowerShell/PowerShell/compare/v7.2.0...v7.2.1 + +## [7.2.0] - 2021-11-08 + +### General Cmdlet Updates and Fixes + +- Handle exception when trying to resolve a possible link path (#16310) + +### Tests + +- Fix global tool and SDK tests in release pipeline (#16342) + +### Build and Packaging Improvements + +
+ + + +

We thank the following contributors!

+

@kondratyev-nv

+ +
+ +
    +
  • Add an approval for releasing build-info json (#16351)
  • +
  • Release build info json when it is preview (#16335)
  • +
  • Update metadata.json for v7.2.0 release
  • +
  • Update to the latest notices file and update cgmanifest.json (#16339)(#16325)
  • +
  • Fix issues in release build by updating usage of powershell.exe with pwsh.exe (#16332)
  • +
  • Update feed and analyzer dependency (#16327)
  • +
  • Update to .NET 6 GA build 6.0.100-rtm.21527.11 (#16309)
  • +
  • Add a major-minor build info JSON file (#16301)
  • +
  • Fix Windows build ZIP packaging (#16299) (Thanks @kondratyev-nv!)
  • +
  • Clean up crossgen related build scripts also generate native symbols for R2R images (#16297)
  • +
  • Fix issues reported by code signing verification tool (#16291)
  • +
+ +
+ +[7.2.0]: https://github.com/PowerShell/PowerShell/compare/v7.2.0-rc.1...v7.2.0 + +## [7.2.0-rc.1] - 2021-10-21 + +### General Cmdlet Updates and Fixes + +- Disallow COM calls for AppLocker system lockdown (#16268) +- Configure `Microsoft.ApplicationInsights` to not send cloud role name (#16246) +- Disallow `Add-Type` in NoLanguage mode on a locked down machine (#16245) +- Make property names for color VT100 sequences consistent with documentation (#16212) +- Make moving a directory into itself with `Move-Item` an error (#16198) +- Change `FileSystemInfo.Target` from a `CodeProperty` to an `AliasProperty` that points to `FileSystemInfo.LinkTarget` (#16165) + +### Tests + +- Removed deprecated docker-based tests for PowerShell release packages (#16224) + +### Build and Packaging Improvements + +
+ + +

Bump .NET SDK to 6.0.100-rc.2

+
+ +
    +
  • Update .NET 6 to version 6.0.100-rc.2.21505.57 (#16249)
  • +
  • Fix RPM packaging (Internal 17704)
  • +
  • Update ThirdPartyNotices.txt (#16283)
  • +
  • Update pipeline yaml file to use ubuntu-latest image (#16279)
  • +
  • Add script to generate cgmanifest.json (#16278)
  • +
  • Update version of Microsoft.PowerShell.Native and Microsoft.PowerShell.MarkdownRender packages (#16277)
  • +
  • Add cgmanifest.json for generating correct third party notice file (#16266)
  • +
  • Only upload stable buildinfo for stable releases (#16251)
  • +
  • Don't upload .dep or .tar.gz for RPM because there are none (#16230)
  • +
  • Ensure RPM license is recognized (#16189)
  • +
  • Add condition to only generate release files in local dev build only (#16259)
  • +
  • Ensure psoptions.json and manifest.spdx.json files always exist in packages (#16258)
  • +
  • Fix CI script and split out ARM runs (#16252)
  • +
  • Update vPack task version to 12 (#16250)
  • +
  • Sign third party executables (#16229)
  • +
  • Add Software Bill of Materials to the main packages (#16202)
  • +
  • Upgrade set-value package for Markdown test (#16196)
  • +
  • Fix Microsoft update spelling issue (#16178)
  • +
  • Move vPack build to 1ES Pool (#16169)
  • +
+ +
+ +[7.2.0-rc.1]: https://github.com/PowerShell/PowerShell/compare/v7.2.0-preview.10...v7.2.0-rc.1 + +## [7.2.0-preview.10] - 2021-09-28 + +### Engine Updates and Fixes + +- Remove duplicate remote server mediator code (#16027) + +### General Cmdlet Updates and Fixes + +- Use `PlainText` when writing to a host that doesn't support VT (#16092) +- Remove support for `AppExecLinks` to retrieve target (#16044) +- Move `GetOuputString()` and `GetFormatStyleString()` to `PSHostUserInterface` as public API (#16075) +- Add `isOutputRedirected` parameter to `GetFormatStyleString()` method (#14397) +- Fix `ConvertTo-SecureString` with key regression due to .NET breaking change (#16068) +- Fix regression in `Move-Item` to only fallback to `CopyAndDelete` in specific cases (#16029) +- Set `$?` correctly for command expression with redirection (#16046) +- Use `CurrentCulture` when handling conversions to `DateTime` in `Add-History` (#16005) (Thanks @vexx32!) +- Fix `NullReferenceException` in `Format-Wide` (#15990) (Thanks @DarylGraves!) + +### Code Cleanup + +
+ + + +

We thank the following contributors!

+

@xtqqczze!

+ +
+ +
    +
  • Improve CommandInvocationIntrinsics API documentation and style (#14369)
  • +
  • Use bool?.GetValueOrDefault() in FormatWideCommand (#15988) (Thanks @xtqqczze!)
  • +
+ +
+ +### Tools + +- Fix typo in build.psm1 (#16038) (Thanks @eltociear!) +- Add `.stylecop` to `filetypexml` and format it (#16025) +- Enable sending Teams notification when workflow fails (#15982) + +### Tests + +- Enable two previously disabled `Get-Process` tests (#15845) (Thanks @iSazonov!) + +### Build and Packaging Improvements + +
+ + +Details + + +
    +
  • Add SHA256 hashes to release (#16147)
  • +
  • Update Microsoft.CodeAnalysis.CSharp version (#16138)
  • +
  • Change path for Component Governance for build to the path we actually use to build (#16137)
  • +
  • Bump Microsoft.CodeAnalysis.NetAnalyzers (#16070) (#16045) (#16036) (#16021) (#15985)
  • +
  • Update .NET to 6.0.100-rc.1.21458.32 (#16066)
  • +
  • Update minimum required OS version for macOS (#16088)
  • +
  • Ensure locale is set correctly on Ubuntu 20.04 in CI (#16067) (#16073)
  • +
  • Update .NET SDK version from 6.0.100-preview.6.21355.2 to 6.0.100-rc.1.21455.2 (#16041) (#16028) (#15648)
  • +
  • Fix the GitHub Action for updating .NET daily builds (#16042)
  • +
  • Move from PkgES hosted agents to 1ES hosted agents (#16023)
  • +
  • Update Ubuntu images to use Ubuntu 20.04 (#15906)
  • +
  • Fix the macOS build by updating the pool image name (#16010)
  • +
  • Use Alpine 3.12 for building PowerShell for Alpine Linux (#16008)
  • +
  • Ignore error from Find-Package (#15999)
  • +
  • Find packages separately for each source in UpdateDotnetRuntime.ps1 script (#15998)
  • +
  • Update metadata to start using .NET 6 RC1 builds (#15981)
  • +
+ +
+ +[7.2.0-preview.10]: https://github.com/PowerShell/PowerShell/compare/v7.2.0-preview.9...v7.2.0-preview.10 + +## [7.2.0-preview.9] - 2021-08-23 + +### Breaking Changes + +- Change the default value of `$PSStyle.OutputRendering` to `OutputRendering.Host` and remove `OutputRendering.Automatic` (#15882) +- Fix `CA1052` for public API to make classes static when they only have static methods (#15775) (Thanks @xtqqczze!) +- Update `pwsh.exe -File` to only accept `.ps1` script files on Windows (#15859) + +### Engine Updates and Fixes + +- Update .NET adapter to handle interface static members properly (#15908) +- Catch and handle unauthorized access exception when removing AppLocker test files (#15881) + +### General Cmdlet Updates and Fixes + +- Add `-PassThru` parameter to `Set-Clipboard` (#13713) (Thanks @ThomasNieto!) +- Add `-Encoding` parameter for `Tee-Object` (#12135) (Thanks @Peter-Schneider!) +- Update `ConvertTo-Csv` and `Export-Csv` to handle `IDictionary` objects (#11029) (Thanks @vexx32!) +- Update the parameters `-Exception` and `-ErrorRecord` for `Write-Error` to be position 0 (#13813) (Thanks @ThomasNieto!) +- Don't use `ArgumentList` when creating COM object with `New-Object` as it's not applicable to the COM parameter set (#15915) +- Fix `$PSStyle` list output to correctly show `TableHeader` (#15928) +- Remove the `PSImplicitRemotingBatching` experimental feature (#15863) +- Fix issue with `Get-Process -Module` failing to stop when it's piped to `Select-Object` (#15682) (Thanks @ArmaanMcleod!) +- Make the experimental features `PSUnixFileStat`, `PSCultureInvariantReplaceOperator`, `PSNotApplyErrorActionToStderr`, `PSAnsiRendering`, `PSAnsiProgressFeatureName` stable (#15864) +- Enhance `Remove-Item` to work with OneDrive (#15571) (Thanks @iSazonov!) +- Make global tool entrypoint class static (#15880) +- Update `ServerRemoteHost` version to be same as `PSVersion` (#15809) +- Make the initialization of `HttpKnownHeaderNames` thread safe (#15519) (Thanks @iSazonov!) +- `ConvertTo-Csv`: Quote fields with quotes and newlines when using `-UseQuotes AsNeeded` (#15765) (Thanks @lselden!) +- Forwarding progress stream changes from `Foreach-Object -Parallel` runspaces (#14271) (Thanks @powercode!) +- Add validation to `$PSStyle` to reject printable text when setting a property that only expects ANSI escape sequence (#15825) + +### Code Cleanup + +
+ + + +

We thank the following contributors!

+

@xtqqczze

+ +
+ +
    +
  • Avoid unneeded array allocation in module code (#14329) (Thanks @xtqqczze!)
  • +
  • Enable and fix analysis rules CA1052, CA1067, and IDE0049 (#15840) (Thanks @xtqqczze!)
  • +
  • Avoid unnecessary allocation in formatting code (#15832) (Thanks @xtqqczze!)
  • +
  • Specify the analyzed API surface for all code quality rules (#15778) (Thanks @xtqqczze!)
  • +
+ +
+ +### Tools + +- Enable `/rebase` to automatically rebase a PR (#15808) +- Update `.editorconfig` to not replace tabs with spaces in `.tsv` files (#15815) (Thanks @SethFalco!) +- Update PowerShell team members in the changelog generation script (#15817) + +### Tests + +- Add more tests to validate the current command error handling behaviors (#15919) +- Make `Measure-Object` property test independent of the file system (#15879) +- Add more information when a `syslog` parsing error occurs (#15857) +- Harden logic when looking for `syslog` entries to be sure that we select based on the process ID (#15841) + +### Build and Packaging Improvements + +
+ + + +

We thank the following contributors!

+

@xtqqczze

+ +
+ +
    +
  • Disable implicit namespace imports for test projects (#15895)
  • +
  • Update language version to 10 and fix related issues (#15886)
  • +
  • Update CodeQL workflow to use Ubuntu 18.04 (#15868)
  • +
  • Bump the version of various packages (#15944, #15934, #15935, #15891, #15812, #15822) (Thanks @xtqqczze!)
  • +
+ +
+ +### Documentation and Help Content + +- Update `README` and `metadata files` for release `v7.2.0-preview.8` (#15819) +- Update changelogs for 7.0.7 and 7.1.4 (#15921) +- Fix spelling in XML docs (#15939) (Thanks @slowy07!) +- Update PowerShell Committee members (#15837) + +[7.2.0-preview.9]: https://github.com/PowerShell/PowerShell/compare/v7.2.0-preview.8...v7.2.0-preview.9 + +## [7.2.0-preview.8] - 2021-07-22 + +### Engine Updates and Fixes + +- Add a Windows mode to `$PSNativeCommandArgumentPassing` that allows some commands to use legacy argument passing (#15408) +- Use `nameof` to get parameter names when creating `ArgumentNullException` (#15604) (Thanks @gukoff!) +- Test if a command is 'Out-Default' more thoroughly for transcribing scenarios (#15653) +- Add `Microsoft.PowerShell.Crescendo` to telemetry allow list (#15372) + +### General Cmdlet Updates and Fixes + +- Use `$PSStyle.Formatting.FormatAccent` for `Format-List` and `$PSStyle.Formatting.TableHeader` for `Format-Table` output (#14406) +- Highlight using error color the exception `Message` and underline in `PositionMessage` for `Get-Error` (#15786) +- Implement a completion for View parameter of format cmdlets (#14513) (Thanks @iSazonov!) +- Add support to colorize `FileInfo` filenames (#14403) +- Don't serialize to JSON ETS properties for `DateTime` and `string` types (#15665) +- Fix `HyperVSocketEndPoint.ServiceId` setter (#15704) (Thanks @xtqqczze!) +- Add `DetailedView` to `$ErrorView` (#15609) + +### Code Cleanup + +
+ + + +

We thank the following contributors!

+

@iSazonov, @xtqqczze

+ +
+ +
    +
  • Remove consolehost.proto file (#15741) (Thanks @iSazonov!)
  • +
  • Implement IDisposable for ConvertToJsonCommand (#15787) (Thanks @xtqqczze!)
  • +
  • Fix IDisposable implementation for CommandPathSearch (#15793) (Thanks @xtqqczze!)
  • +
  • Delete IDE dispose analyzer rules (#15798) (Thanks @xtqqczze!)
  • +
  • Seal private classes (#15725) (Thanks @xtqqczze!)
  • +
  • Enable IDE0029: UseCoalesceExpression (#15770) (Thanks @xtqqczze!)
  • +
  • Enable IDE0070: UseSystemHashCode (#15715) (Thanks @xtqqczze!)
  • +
  • Enable IDE0030: UseCoalesceExpressionForNullable (#14289) (Thanks @xtqqczze!)
  • +
  • Fix CA1846 and CA1845 for using AsSpan instead of Substring (#15738)
  • +
  • Use List<T>.RemoveAll to avoid creating temporary list (#15686) (Thanks @xtqqczze!)
  • +
  • Enable IDE0044: MakeFieldReadonly (#13880) (Thanks @xtqqczze!)
  • +
  • Disable IDE0130 (#15728) (Thanks @xtqqczze!)
  • +
  • Make classes sealed (#15675) (Thanks @xtqqczze!)
  • +
  • Enable CA1043: Use integral or string argument for indexers (#14467) (Thanks @xtqqczze!)
  • +
  • Enable CA1812 (#15674) (Thanks @xtqqczze!)
  • +
  • Replace Single with First when we know the element count is 1 (#15676) (Thanks @xtqqczze!)
  • +
  • Skip analyzers for Microsoft.Management.UI.Internal (#15677) (Thanks @xtqqczze!)
  • +
  • Fix CA2243: Attribute string literals should parse correctly (#15622) (Thanks @xtqqczze!)
  • +
  • Enable CA1401 (#15621) (Thanks @xtqqczze!)
  • +
  • Fix CA1309: Use ordinal StringComparison in Certificate Provider (#14352) (Thanks @xtqqczze!)
  • +
  • Fix CA1839: Use Environment.ProcessPath (#15650) (Thanks @xtqqczze!)
  • +
  • Add new analyzer rules (#15620) (Thanks @xtqqczze!)
  • +
+ +
+ +### Tools + +- Add `SkipRoslynAnalyzers` parameter to `Start-PSBuild` (#15640) (Thanks @xtqqczze!) +- Create issue template for issues updating PowerShell through Windows update. (#15700) +- Add `DocumentationAnalyzers` to build (#14336) (Thanks @xtqqczze!) +- Convert GitHub issue templates to modern forms (#15645) + +### Tests + +- Add more tests for `ConvertFrom-Json` (#15706) (Thanks @strawgate!) +- Update `glob-parent` and `hosted-git-info` test dependencies (#15643) + +### Build and Packaging Improvements + +
+ + +Update .NET to version v6.0.0-preview.6 + + +
    +
  • Add new package name for osx-arm64 (#15813)
  • +
  • Prefer version when available for dotnet-install (#15810)
  • +
  • Make warning about MU being required dynamic (#15776)
  • +
  • Add Start-PSBootstrap before running tests (#15804)
  • +
  • Update to .NET 6 Preview 6 and use crossgen2 (#15763)
  • +
  • Enable ARM64 packaging for macOS (#15768)
  • +
  • Make Microsoft Update opt-out/in check boxes work (#15784)
  • +
  • Add Microsoft Update opt out to MSI install (#15727)
  • +
  • Bump NJsonSchema from 10.4.4 to 10.4.5 (#15769)
  • +
  • Fix computation of SHA512 checksum (#15736)
  • +
  • Update the script to use quality parameter for dotnet-install (#15731)
  • +
  • Generate SHA512 checksum file for all packages (#15678)
  • +
  • Enable signing daily release build with lifetime certificate (#15642)
  • +
  • Update metadata and README for 7.2.0-preview.7 (#15593)
  • +
+ +
+ +### Documentation and Help Content + +- Fix broken RFC links (#15807) +- Add to bug report template getting details from `Get-Error` (#15737) +- Update issue templates to link to new docs (#15711) +- Add @jborean93 to Remoting Working Group (#15683) + +[7.2.0-preview.8]: https://github.com/PowerShell/PowerShell/compare/v7.2.0-preview.7...v7.2.0-preview.8 + +## [7.2.0-preview.7] - 2021-06-17 + +### Breaking Changes + +- Remove PSDesiredStateConfiguration v2.0.5 module and published it to the PowerShell Gallery (#15536) + +### Engine Updates and Fixes + +- Fix splatting being treated as positional parameter in completions (#14623) (Thanks @MartinGC94!) +- Prevent PowerShell from crashing when a telemetry mutex can't be created (#15574) (Thanks @gukoff!) +- Ignore all exceptions when disposing an instance of a subsystem implementation (#15511) +- Wait for SSH exit when closing remote connection (#14635) (Thanks @dinhngtu!) + +### Performance + +- Retrieve `ProductVersion` using informational version attribute in `AmsiUtils.Init()` (#15527) (Thanks @Fs00!) + +### General Cmdlet Updates and Fixes + +- Fix retrieving dynamic parameters from provider even if globbed path returns no results (#15525) +- Revert "Enhance Remove-Item to work with OneDrive (#15260)" due to long path issue (#15546) + +### Code Cleanup + +
+ + + +

We thank the following contributors!

+

@octos4murai, @iSazonov, @Fs00

+ +
+ +
    +
  • Correct parameter name passed to exception in PSCommand constructor (#15580) (Thanks @octos4murai!)
  • +
  • Enable nullable: System.Management.Automation.ICommandRuntime (#15566) (Thanks @iSazonov!)
  • +
  • Clean up code regarding AppDomain.CreateDomain and AppDomain.Unload (#15554)
  • +
  • Replace ProcessModule.FileName with Environment.ProcessPath and remove PSUtils.GetMainModule (#15012) (Thanks @Fs00!)
  • +
+ +
+ +### Tests + +- Fix `Start-Benchmarking` to put `TargetPSVersion` and `TargetFramework` in separate parameter sets (#15508) +- Add `win-x86` test package to the build (#15517) + +### Build and Packaging Improvements + +
+ + + +

We thank the following contributors!

+

@schuelermine

+ +
+ +
    +
  • Update README.md and metadata.json for version 7.2.0-preview.6 (#15464)
  • +
  • Make sure GA revision increases from RC and Preview releases (#15558)
  • +
  • Remove SupportsShouldProcess from Start-PSBootstrap in build.psm1 (#15491) (Thanks @schuelermine!)
  • +
  • Update DotnetMetadataRuntime.json next channel to take daily build from .NET preview 5 (#15518)
  • +
  • Fix deps.json update in the release pipeline (#15486)
  • +
+ +
+ +### Documentation and Help Content + +- Add new members to Engine and Cmdlet Working Groups document (#15560) +- Update the `mdspell` command to exclude the folder that should be ignored (#15576) +- Replace 'User Voice' with 'Feedback Hub' in `README.md` (#15557) +- Update Virtual User Group chat links (#15505) (Thanks @Jaykul!) +- Fix typo in `FileSystemProvider.cs` (#15445) (Thanks @eltociear!) +- Add `PipelineStoppedException` notes to PowerShell API (#15324) +- Updated governance on Working Groups (WGs) (#14603) +- Correct and improve XML documentation comments on `PSCommand` (#15568) (Thanks @octos4murai!) + +[7.2.0-preview.7]: https://github.com/PowerShell/PowerShell/compare/v7.2.0-preview.6...v7.2.0-preview.7 + +## [7.2.0-preview.6] - 2021-05-27 + +### Experimental Features + +- [Breaking Change] Update prediction interface to provide additional feedback to a predictor plugin (#15421) + +### Performance + +- Avoid collecting logs in buffer if a pipeline execution event is not going to be logged (#15350) +- Avoid allocation in `LanguagePrimitives.UpdateTypeConvertFromTypeTable` (#15168) (Thanks @xtqqczze!) +- Replace `Directory.GetDirectories` with `Directory.EnumerateDirectories` to avoid array allocations (#15167) (Thanks @xtqqczze!) +- Use `List.ConvertAll` instead of `LINQ` (#15140) (Thanks @xtqqczze!) + +### General Cmdlet Updates and Fixes + +- Use `AllocConsole` before initializing CLR to ensure codepage is correct for WinRM remoting (PowerShell/PowerShell-Native#70) (Thanks @jborean93!) +- Add completions for `#requires` statements (#14596) (Thanks @MartinGC94!) +- Add completions for comment-based help keywords (#15337) (Thanks @MartinGC94!) +- Move cross platform DSC code to a PowerShell engine subsystem (#15127) +- Fix `Minimal` progress view to handle activity that is longer than console width (#15264) +- Handle exception if ConsoleHost tries to set cursor out of bounds because screen buffer changed (#15380) +- Fix `NullReferenceException` in DSC `ClearCache()` (#15373) +- Update `ControlSequenceLength` to handle colon as a virtual terminal parameter separator (#14942) +- Update the summary comment for `StopTranscriptCmdlet.cs` (#15349) (Thanks @dbaileyut!) +- Remove the unusable alias `d` for the `-Directory` parameter from `Get-ChildItem` (#15171) (Thanks @kvprasoon!) +- Fix tab completion for un-localized `about` topics (#15265) (Thanks @MartinGC94!) +- Remove the unneeded SSH stdio handle workaround (#15308) +- Add `LoadAssemblyFromNativeMemory` API to load assemblies from memory in a native PowerShell host (#14652) (Thanks @awakecoding!) +- Re-implement `Remove-Item` OneDrive support (#15260) (Thanks @iSazonov!) +- Kill native processes in pipeline when pipeline is disposed on Unix (#15287) +- Default to MTA on Windows platforms where STA is not supported (#15106) + +### Code Cleanup + +
+ + + +

We thank the following contributors!

+

@xtqqczze, @powercode, @bcwood

+ +
+ +
    +
  • Enable nullable in some classes (#14185, #14177, #14159, #14191, #14162, #14150, #14156, #14161, #14155, #14163, #14181, #14157, #14151) (Thanks @powercode!)
  • +
  • Annotate ThrowTerminatingError with DoesNotReturn attribute (#15352) (Thanks @powercode!)
  • +
  • Use GetValueOrDefault() for nullable PSLanguageMode (#13849) (Thanks @bcwood!)
  • +
  • Enable SA1008: Opening parenthesis should be spaced correctly (#14242) (Thanks @xtqqczze!)
  • +
+ +
+ +### Tools + +- Add `winget` release script (#15050) + +### Tests + +- Enable cross-runtime benchmarking to compare different .NET runtimes (#15387) (Thanks @adamsitnik!) +- Add the performance benchmark project for PowerShell performance testing (#15242) + +### Build and Packaging Improvements + +
+ + +Update .NET to version v6.0.0-preview.4 + + +
    +
  • Suppress prompting when uploading the msixbundle package to blob (#15227)
  • +
  • Update to .NET preview 4 SDK (#15452)
  • +
  • Update AppxManifest.xml with newer OS version to allow PowerShell installed from Windows Store to make system-level changes (#15375)
  • +
  • Ensure the build works when PSDesiredStateConfiguration module is pulled in from PSGallery (#15355)
  • +
  • Make sure daily release tag does not change when retrying failures (#15286)
  • +
  • Improve messages and behavior when there's a problem in finding zip files (#15284)
  • +
+ +
+ +### Documentation and Help Content + +- Add documentation comments section to coding guidelines (#14316) (Thanks @xtqqczze!) + +[7.2.0-preview.6]: https://github.com/PowerShell/PowerShell/compare/v7.2.0-preview.5...v7.2.0-preview.6 + +## [7.2.0-preview.5] - 2021-04-14 + +### Breaking Changes + +- Make PowerShell Linux deb and RPM packages universal (#15109) +- Enforce AppLocker Deny configuration before Execution Policy Bypass configuration (#15035) +- Disallow mixed dash and slash in command-line parameter prefix (#15142) (Thanks @davidBar-On!) + +### Experimental Features + +- `PSNativeCommandArgumentPassing`: Use `ArgumentList` for native executable invocation (breaking change) (#14692) + +### Engine Updates and Fixes + +- Add `IArgumentCompleterFactory` for parameterized `ArgumentCompleters` (#12605) (Thanks @powercode!) + +### General Cmdlet Updates and Fixes + +- Fix SSH remoting connection never finishing with misconfigured endpoint (#15175) +- Respect `TERM` and `NO_COLOR` environment variables for `$PSStyle` rendering (#14969) +- Use `ProgressView.Classic` when Virtual Terminal is not supported (#15048) +- Fix `Get-Counter` issue with `-Computer` parameter (#15166) (Thanks @krishnayalavarthi!) +- Fix redundant iteration while splitting lines (#14851) (Thanks @hez2010!) +- Enhance `Remove-Item -Recurse` to work with OneDrive (#14902) (Thanks @iSazonov!) +- Change minimum depth to 0 for `ConvertTo-Json` (#14830) (Thanks @kvprasoon!) +- Allow `Set-Clipboard` to accept empty string (#14579) +- Turn on and off `DECCKM` to modify keyboard mode for Unix native commands to work correctly (#14943) +- Fall back to `CopyAndDelete()` when `MoveTo()` fails due to an `IOException` (#15077) + +### Code Cleanup + +
+ + + +

We thank the following contributors!

+

@xtqqczze, @iSazonov, @ZhiZe-ZG

+ +
+ +
    +
  • Update .NET to 6.0.0-preview.3 (#15221)
  • +
  • Add space before comma to hosting test to fix error reported by SA1001 (#15224)
  • +
  • Add SecureStringHelper.FromPlainTextString helper method for efficient secure string creation (#14124) (Thanks @xtqqczze!)
  • +
  • Use static lambda keyword (#15154) (Thanks @iSazonov!)
  • +
  • Remove unnecessary Array -> List -> Array conversion in ProcessBaseCommand.AllProcesses (#15052) (Thanks @xtqqczze!)
  • +
  • Standardize grammar comments in Parser.cs (#15114) (Thanks @ZhiZe-ZG!)
  • +
  • Enable SA1001: Commas should be spaced correctly (#14171) (Thanks @xtqqczze!)
  • +
  • Refactor MultipleServiceCommandBase.AllServices (#15053) (Thanks @xtqqczze!)
  • +
+ +
+ +### Tools + +- Use Unix line endings for shell scripts (#15180) (Thanks @xtqqczze!) + +### Tests + +- Add the missing tag in Host Utilities tests (#14983) +- Update `copy-props` version in `package.json` (#15124) + +### Build and Packaging Improvements + +
+ + + +

We thank the following contributors!

+

@JustinGrote

+ +
+ +
    +
  • Fix yarn-lock for copy-props (#15225)
  • +
  • Make package validation regular expression accept universal Linux packages (#15226)
  • +
  • Bump NJsonSchema from 10.4.0 to 10.4.1 (#15190)
  • +
  • Make MSI and EXE signing always copy to fix daily build (#15191)
  • +
  • Sign internals of EXE package so that it works correctly when signed (#15132)
  • +
  • Bump Microsoft.NET.Test.Sdk from 16.9.1 to 16.9.4 (#15141)
  • +
  • Update daily release tag format to work with new Microsoft Update work (#15164)
  • +
  • Feature: Add Ubuntu 20.04 Support to install-powershell.sh (#15095) (Thanks @JustinGrote!)
  • +
  • Treat rebuild branches like release branches (#15099)
  • +
  • Update WiX to 3.11.2 (#15097)
  • +
  • Bump NJsonSchema from 10.3.11 to 10.4.0 (#15092)
  • +
  • Allow patching of preview releases (#15074)
  • +
  • Bump Newtonsoft.Json from 12.0.3 to 13.0.1 (#15084, #15085)
  • +
  • Update the minSize build package filter to be explicit (#15055)
  • +
  • Bump NJsonSchema from 10.3.10 to 10.3.11 (#14965)
  • +
+ +
+ +### Documentation and Help Content + +- Merge `7.2.0-preview.4` changes to master (#15056) +- Update `README` and `metadata.json` (#15046) +- Fix broken links for `dotnet` CLI (#14937) + +[7.2.0-preview.5]: https://github.com/PowerShell/PowerShell/compare/v7.2.0-preview.4...v7.2.0-preview.5 + +## [7.2.0-preview.4] - 2021-03-16 + +### Breaking Changes + +- Fix `Get-Date -UFormat` `%G` and `%g` behavior (#14555) (Thanks @brianary!) + +### Engine Updates and Fixes + +- Update engine script signature validation to match `Get-AuthenticodeSignature` logic (#14849) +- Avoid array allocations from `GetDirectories` and `GetFiles` (#14327) (Thanks @xtqqczze!) + +### General Cmdlet Updates and Fixes + +- Add `UseOSCIndicator` setting to enable progress indicator in terminal (#14927) +- Re-enable VT mode on Windows after running command in `ConsoleHost` (#14413) +- Fix `Move-Item` for `FileSystemProvider` to use copy-delete instead of move for DFS paths (#14913) +- Fix `PromptForCredential()` to add `targetName` as domain (#14504) +- Update `Concise` `ErrorView` to not show line information for errors from script module functions (#14912) +- Remove the 32,767 character limit on the environment block for `Start-Process` (#14111) (Thanks @hbuckle!) +- Don't write possible secrets to verbose stream for web cmdlets (#14788) + +### Tools + +- Update `dependabot` configuration to V2 format (#14882) +- Add tooling issue slots in PR template (#14697) + +### Tests + +- Move misplaced test file to tests directory (#14908) (Thanks @MarianoAlipi!) +- Refactor MSI CI (#14753) + +### Build and Packaging Improvements + +
+ + +Update .NET to version 6.0.100-preview.2.21155.3 + + +
    +
  • Update .NET to version 6.0.100-preview.2.21155.3 (#15007)
  • +
  • Bump Microsoft.PowerShell.Native to 7.2.0-preview.1 (#15030)
  • +
  • Create MSIX Bundle package in release pipeline (#14982)
  • +
  • Build self-contained minimal size package for Guest Config team (#14976)
  • +
  • Bump XunitXml.TestLogger from 3.0.62 to 3.0.66 (#14993) (Thanks @dependabot[bot]!)
  • +
  • Enable building PowerShell for Apple M1 runtime (#14923)
  • +
  • Fix the variable name in the condition for miscellaneous analysis CI (#14975)
  • +
  • Fix the variable usage in CI yaml (#14974)
  • +
  • Disable running Markdown link verification in release build CI (#14971)
  • +
  • Bump Microsoft.CodeAnalysis.CSharp from 3.9.0-3.final to 3.9.0 (#14934) (Thanks @dependabot[bot]!)
  • +
  • Declare which variable group is used for checking the blob in the release build (#14970)
  • +
  • Update metadata and script to enable consuming .NET daily builds (#14940)
  • +
  • Bump NJsonSchema from 10.3.9 to 10.3.10 (#14933) (Thanks @dependabot[bot]!)
  • +
  • Use template that disables component governance for CI (#14938)
  • +
  • Add suppress for nuget multi-feed warning (#14893)
  • +
  • Bump NJsonSchema from 10.3.8 to 10.3.9 (#14926) (Thanks @dependabot[bot]!)
  • +
  • Add exe wrapper to release (#14881)
  • +
  • Bump Microsoft.ApplicationInsights from 2.16.0 to 2.17.0 (#14847)
  • +
  • Bump Microsoft.NET.Test.Sdk from 16.8.3 to 16.9.1 (#14895) (Thanks @dependabot[bot]!)
  • +
  • Bump NJsonSchema from 10.3.7 to 10.3.8 (#14896) (Thanks @dependabot[bot]!)
  • +
  • Disable codesign validation where the file type is not supported (#14885)
  • +
  • Fixing broken Experimental Feature list in powershell.config.json (#14858)
  • +
  • Bump NJsonSchema from 10.3.6 to 10.3.7 (#14855)
  • +
  • Add exe wrapper for Microsoft Update scenarios (#14737)
  • +
  • Install wget on CentOS 7 docker image (#14857)
  • +
  • Fix install-dotnet download (#14856)
  • +
  • Fix Bootstrap step in Windows daily test runs (#14820)
  • +
  • Bump NJsonSchema from 10.3.5 to 10.3.6 (#14818)
  • +
  • Bump NJsonSchema from 10.3.4 to 10.3.5 (#14807)
  • +
+ +
+ +### Documentation and Help Content + +- Update `README.md` and `metadata.json` for upcoming releases (#14755) +- Merge 7.1.3 and 7.0.6 changelog to master (#15009) +- Update `README` and `metadata.json` for releases (#14997) +- Update ChangeLog for `v7.1.2` release (#14783) +- Update ChangeLog for `v7.0.5` release (#14782) (Internal 14479) + +[7.2.0-preview.4]: https://github.com/PowerShell/PowerShell/compare/v7.2.0-preview.3...v7.2.0-preview.4 + +## [7.2.0-preview.3] - 2021-02-11 + +### Breaking Changes + +- Fix `Get-Date -UFormat %u` behavior to comply with ISO 8601 (#14549) (Thanks @brianary!) + +### Engine Updates and Fixes + +- Together with `PSDesiredStateConfiguration` `v3` module allows `Get-DscResource`, `Invoke-DscResource` and DSC configuration compilation on all platforms, supported by PowerShell (using class-based DSC resources). + +### Performance + +- Avoid array allocations from `Directory.GetDirectories` and `Directory.GetFiles`. (#14326) (Thanks @xtqqczze!) +- Avoid `string.ToLowerInvariant()` from `GetEnvironmentVariableAsBool()` to avoid loading libicu at startup (#14323) (Thanks @iSazonov!) +- Get PowerShell version in `PSVersionInfo` using assembly attribute instead of `FileVersionInfo` (#14332) (Thanks @Fs00!) + +### General Cmdlet Updates and Fixes + +- Suppress `Write-Progress` in `ConsoleHost` if output is redirected and fix tests (#14716) +- Experimental feature `PSAnsiProgress`: Add minimal progress bar using ANSI rendering (#14414) +- Fix web cmdlets to properly construct URI from body when using `-NoProxy` (#14673) +- Update the `ICommandPredictor` to provide more feedback and also make feedback easier to be correlated (#14649) +- Reset color after writing `Verbose`, `Debug`, and `Warning` messages (#14698) +- Fix using variable for nested `ForEach-Object -Parallel` calls (#14548) +- When formatting, if collection is modified, don't fail the entire pipeline (#14438) +- Improve completion of parameters for attributes (#14525) (Thanks @MartinGC94!) +- Write proper error messages for `Get-Command ' '` (#13564) (Thanks @jakekerr!) +- Fix typo in the resource string `ProxyURINotSupplied` (#14526) (Thanks @romero126!) +- Add support to `$PSStyle` for strikethrough and hyperlinks (#14461) +- Fix `$PSStyle` blink codes (#14447) (Thanks @iSazonov!) + +### Code Cleanup + +
+ + + +

We thank the following contributors!

+

@xtqqczze, @powercode

+ +
+ +
    +
  • Fix coding style issues: RCS1215, IDE0090, SA1504, SA1119, RCS1139, IDE0032 (#14356, #14341, #14241, #14204, #14442, #14443) (Thanks @xtqqczze!)
  • +
  • Enable coding style checks: CA2249, CA1052, IDE0076, IDE0077, SA1205, SA1003, SA1314, SA1216, SA1217, SA1213 (#14395, #14483, #14494, #14495, #14441, #14476, #14470, #14471, #14472) (Thanks @xtqqczze!)
  • +
  • Enable nullable in PowerShell codebase (#14160, #14172, #14088, #14154, #14166, #14184, #14178) (Thanks @powercode!)
  • +
  • Use string.Split(char) instead of string.Split(string) (#14465) (Thanks @xtqqczze!)
  • +
  • Use string.Contains(char) overload (#14368) (Thanks @xtqqczze!)
  • +
  • Refactor complex if statements (#14398) (Thanks @xtqqczze!)
  • +
+ +
+ +### Tools + +- Update script to use .NET 6 build resources (#14705) +- Fix the daily GitHub Action (#14711) (Thanks @imba-tjd!) +- GitHub Actions: fix deprecated `::set-env` (#14629) (Thanks @imba-tjd!) +- Update Markdown test tools (#14325) (Thanks @RDIL!) +- Upgrade `StyleCopAnalyzers` to `v1.2.0-beta.312` (#14354) (Thanks @xtqqczze!) + +### Tests + +- Remove packaging from daily Windows build (#14749) +- Update link to the Manning book (#14750) +- A separate Windows packaging CI (#14670) +- Update `ini` component version in test `package.json` (#14454) +- Disable `libmi` dependent tests for macOS. (#14446) + +### Build and Packaging Improvements + +
+ +
    +
  • Fix the NuGet feed name and URL for .NET 6
  • +
  • Fix third party signing for files in sub-folders (#14751)
  • +
  • Make build script variable an ArrayList to enable Add() method (#14748)
  • +
  • Remove old .NET SDKs to make dotnet restore work with the latest SDK in CI pipeline (#14746)
  • +
  • Remove outdated Linux dependencies (#14688)
  • +
  • Bump .NET SDK version to 6.0.0-preview.1 (#14719)
  • +
  • Bump NJsonSchema to 10.3.4 (#14714)
  • +
  • Update daily GitHub action to allow manual trigger (#14718)
  • +
  • Bump XunitXml.TestLogger to 3.0.62 (#14702)
  • +
  • Make universal deb package based on the deb package specification (#14681)
  • +
  • Add manual release automation steps and improve changelog script (#14445)
  • +
  • Fix release build to upload global tool packages to artifacts (#14620)
  • +
  • Port changes from the PowerShell v7.0.4 release (#14637)
  • +
  • Port changes from the PowerShell v7.1.1 release (#14621)
  • +
  • Updated README and metadata.json (#14401, #14606, #14612)
  • +
  • Do not push nupkg artifacts to MyGet (#14613)
  • +
  • Use one feed in each nuget.config in official builds (#14363)
  • +
  • Fix path signed RPMs are uploaded from in release build (#14424)
  • +
+ +
+ +### Documentation and Help Content + +- Update distribution support request template to point to .NET 5.0 support document (#14578) +- Remove security GitHub issue template (#14453) +- Add intent for using the Discussions feature in repository (#14399) +- Fix Universal Dashboard to refer to PowerShell Universal (#14437) +- Update document link because of HTTP 301 redirect (#14431) (Thanks @xtqqczze!) + +[7.2.0-preview.3]: https://github.com/PowerShell/PowerShell/compare/v7.2.0-preview.2...v7.2.0-preview.3 + +## [7.2.0-preview.2] - 2020-12-15 + +### Breaking Changes + +- Improve detection of mutable value types (#12495) (Thanks @vexx32!) +- Ensure `-PipelineVariable` is set for all output from script cmdlets (#12766) (Thanks @vexx32!) + +### Experimental Features + +- `PSAnsiRendering`: Enable ANSI formatting via `$PSStyle` and support suppressing ANSI output (#13758) + +### Performance + +- Optimize `IEnumerable` variant of replace operator (#14221) (Thanks @iSazonov!) +- Refactor multiply operation for better performance in two `Microsoft.PowerShell.Commands.Utility` methods (#14148) (Thanks @xtqqczze!) +- Use `Environment.TickCount64` instead of `Datetime.Now` as the random seed for AppLocker test file content (#14283) (Thanks @iSazonov!) +- Avoid unnecessary array allocations when searching in GAC (#14291) (Thanks @xtqqczze!) +- Use `OrdinalIgnoreCase` in `CommandLineParser` (#14303) (Thanks @iSazonov!) +- Use `StringComparison.Ordinal` instead of `StringComparison.CurrentCulture` (#14298) (Thanks @iSazonov!) +- Avoid creating instances of the generated delegate helper class in `-replace` implementation (#14128) + +### General Cmdlet Updates and Fixes + +- Write better error message if config file is broken (#13496) (Thanks @iSazonov!) +- Make AppLocker Enforce mode take precedence over UMCI Audit mode (#14353) +- Add `-SkipLimitCheck` switch to `Import-PowerShellDataFile` (#13672) +- Restrict `New-Object` in NoLanguage mode under lock down (#14140) (Thanks @krishnayalavarthi!) +- The `-Stream` parameter now works with directories (#13941) (Thanks @kyanha!) +- Avoid an exception if file system does not support reparse points (#13634) (Thanks @iSazonov!) +- Enable `CA1012`: Abstract types should not have public constructors (#13940) (Thanks @xtqqczze!) +- Enable `SA1212`: Property accessors should follow order (#14051) (Thanks @xtqqczze!) + +### Code Cleanup + +
+ + + +

We thank the following contributors!

+

@xtqqczze, @matthewjdegarmo, @powercode, @Gimly

+ +
+ +
    +
  • Enable SA1007: Operator keyword should be followed by space (#14130) (Thanks @xtqqczze!)
  • +
  • Expand where alias to Where-Object in Reset-PWSHSystemPath.ps1 (#14113) (Thanks @matthewjdegarmo!)
  • +
  • Fix whitespace issues (#14092) (Thanks @xtqqczze!)
  • +
  • Add StyleCop.Analyzers package (#13963) (Thanks @xtqqczze!)
  • +
  • Enable IDE0041: UseIsNullCheck (#14041) (Thanks @xtqqczze!)
  • +
  • Enable IDE0082: ConvertTypeOfToNameOf (#14042) (Thanks @xtqqczze!)
  • +
  • Remove unnecessary usings part 4 (#14023) (Thanks @xtqqczze!)
  • +
  • Fix PriorityAttribute name (#14094) (Thanks @xtqqczze!)
  • +
  • Enable nullable: System.Management.Automation.Interpreter.IBoxableInstruction (#14165) (Thanks @powercode!)
  • +
  • Enable nullable: System.Management.Automation.Provider.IDynamicPropertyProvider (#14167) (Thanks @powercode!)
  • +
  • Enable nullable: System.Management.Automation.Language.IScriptExtent (#14179) (Thanks @powercode!)
  • +
  • Enable nullable: System.Management.Automation.Language.ICustomAstVisitor2 (#14192) (Thanks @powercode!)
  • +
  • Enable nullable: System.Management.Automation.LanguagePrimitives.IConversionData (#14187) (Thanks @powercode!)
  • +
  • Enable nullable: System.Automation.Remoting.Client.IWSManNativeApiFacade (#14186) (Thanks @powercode!)
  • +
  • Enable nullable: System.Management.Automation.Language.ISupportsAssignment (#14180) (Thanks @powercode!)
  • +
  • Enable nullable: System.Management.Automation.ICommandRuntime2 (#14183) (Thanks @powercode!)
  • +
  • Enable nullable: System.Management.Automation.IOutputProcessingState (#14175) (Thanks @powercode!)
  • +
  • Enable nullable: System.Management.Automation.IJobDebugger (#14174) (Thanks @powercode!)
  • +
  • Enable nullable: System.Management.Automation.Interpreter.IInstructionProvider (#14173) (Thanks @powercode!)
  • +
  • Enable nullable: System.Management.Automation.IHasSessionStateEntryVisibility (#14169) (Thanks @powercode!)
  • +
  • Enable nullable: System.Management.Automation.Tracing.IEtwEventCorrelator (#14168) (Thanks @powercode!)
  • +
  • Fix syntax error in Windows packaging script (#14377)
  • +
  • Remove redundant local assignment in AclCommands (#14358) (Thanks @xtqqczze!)
  • +
  • Enable nullable: System.Management.Automation.Language.IAstPostVisitHandler (#14164) (Thanks @powercode!)
  • +
  • Enable nullable: System.Management.Automation.IModuleAssemblyInitializer (#14158) (Thanks @powercode!)
  • +
  • Use Microsoft.PowerShell.MarkdownRender package from nuget.org (#14090)
  • +
  • Replace GetFiles in TestModuleManifestCommand (#14317) (Thanks @xtqqczze!)
  • +
  • Enable nullable: System.Management.Automation.Provider.IContentWriter (#14152) (Thanks @powercode!)
  • +
  • Simplify getting Encoding in TranscriptionOption.FlushContentToDisk (#13910) (Thanks @Gimly!)
  • +
  • Mark applicable structs as readonly and use in-modifier (#13919) (Thanks @xtqqczze!)
  • +
  • Enable nullable: System.Management.Automation.IArgumentCompleter (#14182) (Thanks @powercode!)
  • +
  • Enable CA1822: Mark private members as static (#13897) (Thanks @xtqqczze!)
  • +
  • Fix IDE0090: Simplify new expression part 6 (#14338) (Thanks @xtqqczze!)
  • +
  • Avoid array allocations from GetDirectories/GetFiles. (#14328) (Thanks @xtqqczze!)
  • +
  • Avoid array allocations from GetDirectories/GetFiles. (#14330) (Thanks @xtqqczze!)
  • +
  • Fix RCS1188: Remove redundant auto-property initialization part 2 (#14262) (Thanks @xtqqczze!)
  • +
  • Enable nullable: System.Management.Automation.Host.IHostSupportsInteractiveSession (#14170) (Thanks @powercode!)
  • +
  • Enable nullable: System.Management.Automation.Provider.IPropertyCmdletProvider (#14176) (Thanks @powercode!)
  • +
  • Fix IDE0090: Simplify new expression part 5 (#14301) (Thanks @xtqqczze!)
  • +
  • Enable IDE0075: SimplifyConditionalExpression (#14078) (Thanks @xtqqczze!)
  • +
  • Remove unnecessary usings part 9 (#14288) (Thanks @xtqqczze!)
  • +
  • Fix StyleCop and MarkdownLint CI failures (#14297) (Thanks @xtqqczze!)
  • +
  • Enable SA1000: Keywords should be spaced correctly (#13973) (Thanks @xtqqczze!)
  • +
  • Fix RCS1188: Remove redundant auto-property initialization part 1 (#14261) (Thanks @xtqqczze!)
  • +
  • Mark private members as static part 10 (#14235) (Thanks @xtqqczze!)
  • +
  • Mark private members as static part 9 (#14234) (Thanks @xtqqczze!)
  • +
  • Fix SA1642 for Microsoft.Management.Infrastructure.CimCmdlets (#14239) (Thanks @xtqqczze!)
  • +
  • Use AsSpan/AsMemory slice constructor (#14265) (Thanks @xtqqczze!)
  • +
  • Fix IDE0090: Simplify new expression part 4.6 (#14260) (Thanks @xtqqczze!)
  • +
  • Fix IDE0090: Simplify new expression part 4.5 (#14259) (Thanks @xtqqczze!)
  • +
  • Fix IDE0090: Simplify new expression part 4.3 (#14257) (Thanks @xtqqczze!)
  • +
  • Fix IDE0090: Simplify new expression part 4.2 (#14256) (Thanks @xtqqczze!)
  • +
  • Fix IDE0090: Simplify new expression part 2 (#14200) (Thanks @xtqqczze!)
  • +
  • Enable SA1643: Destructor summary documentation should begin with standard text (#14236) (Thanks @xtqqczze!)
  • +
  • Fix IDE0090: Simplify new expression part 4.4 (#14258) (Thanks @xtqqczze!)
  • +
  • Use xml documentation child blocks correctly (#14249) (Thanks @xtqqczze!)
  • +
  • Fix IDE0090: Simplify new expression part 4.1 (#14255) (Thanks @xtqqczze!)
  • +
  • Use consistent spacing in xml documentation tags (#14231) (Thanks @xtqqczze!)
  • +
  • Enable IDE0074: Use coalesce compound assignment (#13396) (Thanks @xtqqczze!)
  • +
  • Remove unnecessary finalizers (#14248) (Thanks @xtqqczze!)
  • +
  • Mark local variable as const (#13217) (Thanks @xtqqczze!)
  • +
  • Fix IDE0032: UseAutoProperty part 2 (#14244) (Thanks @xtqqczze!)
  • +
  • Fix IDE0032: UseAutoProperty part 1 (#14243) (Thanks @xtqqczze!)
  • +
  • Mark private members as static part 8 (#14233) (Thanks @xtqqczze!)
  • +
  • Fix CA1822: Mark members as static part 6 (#14229) (Thanks @xtqqczze!)
  • +
  • Fix CA1822: Mark members as static part 5 (#14228) (Thanks @xtqqczze!)
  • +
  • Fix CA1822: Mark members as static part 4 (#14227) (Thanks @xtqqczze!)
  • +
  • Fix CA1822: Mark members as static part 3 (#14226) (Thanks @xtqqczze!)
  • +
  • Fix CA1822: Mark members as static part 2 (#14225) (Thanks @xtqqczze!)
  • +
  • Fix CA1822: Mark members as static part 1 (#14224) (Thanks @xtqqczze!)
  • +
  • Use see keyword in documentation (#14220) (Thanks @xtqqczze!)
  • +
  • Enable CA2211: Non-constant fields should not be visible (#14073) (Thanks @xtqqczze!)
  • +
  • Enable CA1816: Dispose methods should call SuppressFinalize (#14074) (Thanks @xtqqczze!)
  • +
  • Remove incorrectly implemented finalizer (#14246) (Thanks @xtqqczze!)
  • +
  • Fix CA1822: Mark members as static part 7 (#14230) (Thanks @xtqqczze!)
  • +
  • Fix SA1122: Use string.Empty for empty strings (#14218) (Thanks @xtqqczze!)
  • +
  • Fix various xml documentation issues (#14223) (Thanks @xtqqczze!)
  • +
  • Remove unnecessary usings part 8 (#14072) (Thanks @xtqqczze!)
  • +
  • Enable SA1006: Preprocessor keywords should not be preceded by space (#14052) (Thanks @xtqqczze!)
  • +
  • Fix SA1642 for Microsoft.PowerShell.Commands.Utility (#14142) (Thanks @xtqqczze!)
  • +
  • Enable CA2216: Disposable types should declare finalizer (#14089) (Thanks @xtqqczze!)
  • +
  • Wrap and name LoadBinaryModule arguments (#14193) (Thanks @xtqqczze!)
  • +
  • Wrap and name GetListOfFilesFromData arguments (#14194) (Thanks @xtqqczze!)
  • +
  • Enable SA1002: Semicolons should be spaced correctly (#14197) (Thanks @xtqqczze!)
  • +
  • Fix IDE0090: Simplify new expression part 3 (#14201) (Thanks @xtqqczze!)
  • +
  • Enable SA1106: Code should not contain empty statements (#13964) (Thanks @xtqqczze!)
  • +
  • Code performance fixes follow-up (#14207) (Thanks @xtqqczze!)
  • +
  • Remove uninformative comments (#14199) (Thanks @xtqqczze!)
  • +
  • Fix IDE0090: Simplify new expression part 1 (#14027) (Thanks @xtqqczze!)
  • +
  • Enable SA1517: Code should not contain blank lines at start of file (#14131) (Thanks @xtqqczze!)
  • +
  • Enable SA1131: Use readable conditions (#14132) (Thanks @xtqqczze!)
  • +
  • Enable SA1507: Code should not contain multiple blank lines in a row (#14136) (Thanks @xtqqczze!)
  • +
  • Enable SA1516 Elements should be separated by blank line (#14137) (Thanks @xtqqczze!)
  • +
  • Enable IDE0031: Null check can be simplified (#13548) (Thanks @xtqqczze!)
  • +
  • Enable CA1065: Do not raise exceptions in unexpected locations (#14117) (Thanks @xtqqczze!)
  • +
  • Enable CA1000: Do not declare static members on generic types (#14097) (Thanks @xtqqczze!)
  • +
+ +
+ +### Tools + +- Fixing formatting in `Reset-PWSHSystemPath.ps1` (#13689) (Thanks @dgoldman-msft!) + +### Tests + +- Reinstate `Test-Connection` tests (#13324) +- Update Markdown test packages with security fixes (#14145) + +### Build and Packaging Improvements + +
+ +
    +
  • Fix a typo in the Get-ChangeLog function (#14129)
  • +
  • Update README and metadata.json for 7.2.0-preview.1 release (#14104)
  • +
  • Bump NJsonSchema from 10.2.2 to 10.3.1 (#14040)
  • +
  • Move windows package signing to use ESRP (#14060)
  • +
  • Use one feed in each nuget.config in official builds (#14363)
  • +
  • Fix path signed RPMs are uploaded from in release build (#14424)
  • +
  • Add Microsoft.PowerShell.MarkdownRender to the package reference list (#14386)
  • +
  • Fix issue with unsigned build (#14367)
  • +
  • Move macOS and nuget to ESRP signing (#14324)
  • +
  • Fix nuget packaging to scrub NullableAttribute (#14344)
  • +
  • Bump Microsoft.NET.Test.Sdk from 16.8.0 to 16.8.3 (#14310)
  • +
  • Bump Markdig.Signed from 0.22.0 to 0.22.1 (#14305)
  • +
  • Bump Microsoft.ApplicationInsights from 2.15.0 to 2.16.0 (#14031)
  • +
  • Move Linux to ESRP signing (#14210)
  • +
+ +
+ +### Documentation and Help Content + +- Fix example `nuget.config` (#14349) +- Fix a broken link in Code Guidelines doc (#14314) (Thanks @iSazonov!) + +[7.2.0-preview.2]: https://github.com/PowerShell/PowerShell/compare/v7.2.0-preview.1...v7.2.0-preview.2 + +## [7.2.0-preview.1] - 2020-11-17 + +### Engine Updates and Fixes + +- Change the default fallback encoding for `GetEncoding` in `Start-Transcript` to be `UTF8` without a BOM (#13732) (Thanks @Gimly!) + +### General Cmdlet Updates and Fixes + +- Update `pwsh -?` output to match docs (#13748) +- Fix `NullReferenceException` in `Test-Json` (#12942) (Thanks @iSazonov!) +- Make `Dispose` in `TranscriptionOption` idempotent (#13839) (Thanks @krishnayalavarthi!) +- Add additional Microsoft PowerShell modules to the tracked modules list (#12183) +- Relax further `SSL` verification checks for `WSMan` on non-Windows hosts with verification available (#13786) (Thanks @jborean93!) +- Add the `OutputTypeAttribute` to `Get-ExperimentalFeature` (#13738) (Thanks @ThomasNieto!) +- Fix blocking wait when starting file associated with a Windows application (#13750) +- Emit warning if `ConvertTo-Json` exceeds `-Depth` value (#13692) + +### Code Cleanup + +
+ + + +

We thank the following contributors!

+

@xtqqczze, @mkswd, @ThomasNieto, @PatLeong, @paul-cheung, @georgettica

+ +
+ +
    +
  • Fix RCS1049: Simplify boolean comparison (#13994) (Thanks @xtqqczze!)
  • +
  • Enable IDE0062: Make local function static (#14044) (Thanks @xtqqczze!)
  • +
  • Enable CA2207: Initialize value type static fields inline (#14068) (Thanks @xtqqczze!)
  • +
  • Enable CA1837: Use ProcessId and CurrentManagedThreadId from System.Environment (#14063) (Thanks @xtqqczze and @PatLeong!)
  • +
  • Remove unnecessary using directives (#14014, #14017, #14021, #14050, #14065, #14066, #13863, #13860, #13861, #13814) (Thanks @xtqqczze and @ThomasNieto!)
  • +
  • Remove unnecessary usage of LINQ Count method (#13545) (Thanks @xtqqczze!)
  • +
  • Fix SA1518: The code must not contain extra blank lines at the end of the file (#13574) (Thanks @xtqqczze!)
  • +
  • Enable CA1829: Use the Length or Count property instead of Count() (#13925) (Thanks @xtqqczze!)
  • +
  • Enable CA1827: Do not use Count() or LongCount() when Any() can be used (#13923) (Thanks @xtqqczze!)
  • +
  • Enable or fix nullable usage in a few files (#13793, #13805, #13808, #14018, #13804) (Thanks @mkswd and @georgettica!)
  • +
  • Enable IDE0040: Add accessibility modifiers (#13962, #13874) (Thanks @xtqqczze!)
  • +
  • Make applicable private Guid fields readonly (#14000) (Thanks @xtqqczze!)
  • +
  • Fix CA1003: Use generic event handler instances (#13937) (Thanks @xtqqczze!)
  • +
  • Simplify delegate creation (#13578) (Thanks @xtqqczze!)
  • +
  • Fix RCS1033: Remove redundant boolean literal (#13454) (Thanks @xtqqczze!)
  • +
  • Fix RCS1221: Use pattern matching instead of combination of as operator and null check (#13333) (Thanks @xtqqczze!)
  • +
  • Use is not syntax (#13338) (Thanks @xtqqczze!)
  • +
  • Replace magic number with constant in PDH (#13536) (Thanks @xtqqczze!)
  • +
  • Fix accessor order (#13538) (Thanks @xtqqczze!)
  • +
  • Enable IDE0054: Use compound assignment (#13546) (Thanks @xtqqczze!)
  • +
  • Fix RCS1098: Constant values should be on right side of comparisons (#13833) (Thanks @xtqqczze!)
  • +
  • Enable CA1068: CancellationToken parameters must come last (#13867) (Thanks @xtqqczze!)
  • +
  • Enable CA10XX rules with suggestion severity (#13870, #13928, #13924) (Thanks @xtqqczze!)
  • +
  • Enable IDE0064: Make Struct fields writable (#13945) (Thanks @xtqqczze!)
  • +
  • Run dotnet-format to improve formatting of source code (#13503) (Thanks @xtqqczze!)
  • +
  • Enable CA1825: Avoid zero-length array allocations (#13961) (Thanks @xtqqczze!)
  • +
  • Add IDE analyzer rule IDs to comments (#13960) (Thanks @xtqqczze!)
  • +
  • Enable CA1830: Prefer strongly-typed Append and Insert method overloads on StringBuilder (#13926) (Thanks @xtqqczze!)
  • +
  • Enforce code style in build (#13957) (Thanks @xtqqczze!)
  • +
  • Enable CA1836: Prefer IsEmpty over Count when available (#13877) (Thanks @xtqqczze!)
  • +
  • Enable CA1834: Consider using StringBuilder.Append(char) when applicable (#13878) (Thanks @xtqqczze!)
  • +
  • Fix IDE0044: Make field readonly (#13884, #13885, #13888, #13892, #13889, #13886, #13890, #13891, #13887, #13893, #13969, #13967, #13968, #13970, #13971, #13966, #14012) (Thanks @xtqqczze!)
  • +
  • Enable IDE0048: Add required parentheses (#13896) (Thanks @xtqqczze!)
  • +
  • Enable IDE1005: Invoke delegate with conditional access (#13911) (Thanks @xtqqczze!)
  • +
  • Enable IDE0036: Enable the check on the order of modifiers (#13958, #13881) (Thanks @xtqqczze!)
  • +
  • Use span-based String.Concat instead of String.Substring (#13500) (Thanks @xtqqczze!)
  • +
  • Enable CA1050: Declare types in namespace (#13872) (Thanks @xtqqczze!)
  • +
  • Fix minor keyword typo in C# code comment (#13811) (Thanks @paul-cheung!)
  • +
+ +
+ +### Tools + +- Enable `CodeQL` Security scanning (#13894) +- Add global `AnalyzerConfig` with default configuration (#13835) (Thanks @xtqqczze!) + +### Build and Packaging Improvements + +
+ + + +

We thank the following contributors!

+

@mkswd, @xtqqczze

+ +
+ +
    +
  • Bump Microsoft.NET.Test.Sdk to 16.8.0 (#14020)
  • +
  • Bump Microsoft.CodeAnalysis.CSharp to 3.8.0 (#14075)
  • +
  • Remove workarounds for .NET 5 RTM builds (#14038)
  • +
  • Migrate 3rd party signing to ESRP (#14010)
  • +
  • Fixes to release pipeline for GA release (#14034)
  • +
  • Don't do a shallow checkout (#13992)
  • +
  • Add validation and dependencies for Ubuntu 20.04 distribution to packaging script (#13993)
  • +
  • Add .NET install workaround for RTM (#13991)
  • +
  • Move to ESRP signing for Windows files (#13988)
  • +
  • Update PSReadLine version to 2.1.0 (#13975)
  • +
  • Bump .NET to version 5.0.100-rtm.20526.5 (#13920)
  • +
  • Update script to use .NET RTM feeds (#13927)
  • +
  • Add checkout step to release build templates (#13840)
  • +
  • Turn on /features:strict for all projects (#13383) (Thanks @xtqqczze!)
  • +
  • Bump NJsonSchema to 10.2.2 (#13722, #13751)
  • +
  • Add flag to make Linux script publish to production repository (#13714)
  • +
  • Bump Markdig.Signed to 0.22.0 (#13741)
  • +
  • Use new release script for Linux packages (#13705)
  • +
+ +
+ +### Documentation and Help Content + +- Fix links to LTS versions for Windows (#14070) +- Fix `crontab` formatting in example doc (#13712) (Thanks @dgoldman-msft!) + +[7.2.0-preview.1]: https://github.com/PowerShell/PowerShell/compare/v7.1.0...v7.2.0-preview.1 diff --git a/CHANGELOG/7.3.md b/CHANGELOG/7.3.md new file mode 100644 index 00000000000..25da137b1c2 --- /dev/null +++ b/CHANGELOG/7.3.md @@ -0,0 +1,1307 @@ +# 7.3 Changelog + +## [7.3.12] - 2024-04-11 + +### Build and Packaging Improvements + +
+ + + +

Bump to .NET 7.0.18

+ +
+ +
    +
  • Update SDK, dependencies and cgmanifest for 7.3.12
  • +
  • Revert changes to packaging.psm1
  • +
  • Verify environment variable for OneBranch before we try to copy (#21441)
  • +
  • Multiple fixes in official build pipeline (#21408)
  • +
  • PowerShell co-ordinated build OneBranch pipeline (#21364)
  • +
  • Add dotenv install as latest version does not work with current Ruby version (#21239)
  • +
  • Remove surrogateFile setting of APIScan (#21238)
  • +
+ +
+ +[7.3.12]: https://github.com/PowerShell/PowerShell/compare/v7.3.11...v7.3.12 + +## [7.3.11] - 2024-01-11 + +### Build and Packaging Improvements + +
+ + + +

Bump .NET to 7.0.405

+ +
+ +
    +
  • Update cgmanifest.json for v7.3.11 release (Internal 29160)
  • +
  • Update .NET SDK to 7.0.405 (Internal 29140)
  • +
  • Back port 3 build changes to apiscan.yml (#21035)
  • +
  • Set the ollForwardOnNoCandidateFx in runtimeconfig.json to roll forward only on minor and patch versions (#20689)
  • +
  • Remove the ref folder before running compliance (#20373)
  • +
  • Fix the tab completion tests (#20867)
  • +
+ +
+ +[7.3.11]: https://github.com/PowerShell/PowerShell/compare/v7.3.10...v7.3.11 + +## [7.3.10] - 2023-11-16 + +### General Cmdlet Updates and Fixes + +- Redact Auth header content from ErrorRecord (Internal 28410) + +### Build and Packaging Improvements + +
+ + + +

Update .NET to 7.0.404

+ +
+ +
    +
  • Add internal .NET SDK URL parameter to release pipeline (Internal 28505)
  • +
  • Fix release build by making the internal SDK parameter optional (#20658) (Internal 28440)
  • +
  • Make internal .NET SDK URL as a parameter for release builld (#20655) (Internal 28428)
  • +
  • Update the Notices file and cgmanifest (Internal 28500)
  • +
  • Update .NET to 7.0.404 (Internal 28485)
  • +
  • Copy azure blob with PowerShell global tool to private blob and move to CDN during release (Internal 28448)
  • +
+ +
+ +[7.3.10]: https://github.com/PowerShell/PowerShell/compare/v7.3.9...v7.3.10 + +## [7.3.9] - 2023-10-26 + +### Build and Packaging Improvements + +
+ + + +

Bump .NET 7 to version 7.0.403

+ +
+ +
    +
  • Use correct agent pool for downloading from Azure blob
  • +
  • Remove a timeout value from ADO pipeline stage to resolve a syntax issue
  • +
  • Update .NET 7 and manifests (Internal 28148)
  • +
  • Add SBOM for release pipeline (#20519) (#20573)
  • +
  • Increase timeout when publishing packages to pacakages.microsoft.com (#20470) (#20572)
  • +
  • Use fxdependent-win-desktop runtime for compliance runs (#20326) (#20571)
  • +
+ +
+ +[7.3.9]: https://github.com/PowerShell/PowerShell/compare/v7.3.8...v7.3.9 + +## [7.3.8] - 2023-10-10 + +### Security Fixes + +- Block getting help from network locations in restricted remoting sessions (Internal 27698) + +### Build and Packaging Improvements + +
+ + + +

Build infrastructure maintenance

+ +
+ +
    +
  • Release build: Change the names of the PATs (#20316)
  • +
  • Add mapping for mariner arm64 stable (#20310)
  • +
  • Switch to GitHub Action for linting markdown (#20308)
  • +
  • Put the calls to Set-AzDoProjectInfo and Set-AzDoAuthToken` in the right order (#20311)
  • +
+ +
+ +[7.3.8]: https://github.com/PowerShell/PowerShell/compare/v7.3.7...v7.3.8 + +## [7.3.7] - 2023-09-18 + +### Build and Packaging Improvements + +
+ + + +

Bump .NET SDK version to 7.0.401

+ +
+ +
    +
  • Update 'ThirdPartyNotices.txt' (Internal 27602)
  • +
  • Update to use .NET SDK 7.0.401 (Internal 27591)
  • +
  • Remove HostArchitecture dynamic parameter for osxpkg (#19917)
  • +
  • Remove spelling CI in favor of GitHub Action (#20248)
  • +
  • Enable vPack provenance data (#20253)
  • +
  • Start using new packages.microsoft.com cli (#20252)
  • +
  • Add mariner arm64 to PMC release (#20251)
  • +
  • Add mariner arm64 package build to release build (#20250)
  • +
  • Make PR creation tool use --web because it is more reliable (#20247)
  • +
  • Update variable used to bypass the blocking check for multiple NuGet feeds (#20246)
  • +
  • Publish rpm package for rhel9 (#20245)
  • +
  • Add runtime and packaging type info for mariner2 arm64 (#20244)
  • +
+ +
+ +### Documentation and Help Content + +- Update man page to match current help for pwsh (#20249) + +[7.3.7]: https://github.com/PowerShell/PowerShell/compare/v7.3.6...v7.3.7 + +## [7.3.6] - 2023-07-13 + +### Build and Packaging Improvements + +
+ + + +

Bump .NET to 7.0.306

+ +
+ +
    +
  • Update Notices file
  • +
  • Don't publish notice on failure because it prevents retry
  • +
  • Bump .NET to 7.0.306 (#19945)
  • +
  • Remove the property disabling optimization (#19952)
  • +
  • Add ProductCode in registry for MSI install (#19951)
  • +
  • Update variable used to bypass the blocking check for multiple NuGet feeds (#19953)
  • +
  • Change System.Security.AccessControl preview version to stable version (#19931)
  • +
+ +
+ +### Documentation and Help Content + +- Update the link for getting started in `README.md` (#19947) + +[7.3.6]: https://github.com/PowerShell/PowerShell/compare/v7.3.5...v7.3.6 + +## [7.3.5] - 2023-06-27 + +### Build and Packaging Improvements + +
+ + + +

Bump to use .NET 7.0.305

+ +
+ +
    +
  • Update the ThirdPartyNotice (Internal 26372)
  • +
  • Add PoolNames variable group to compliance pipeline (#19408)
  • +
  • Update cgmanifest.json
  • +
  • Update to .NET 7.0.304 (#19807)
  • +
  • Disable SBOM signing for CI and add extra files for packaging tests (#19729)
  • +
  • Increase timeout to make subsystem tests more reliable (#18380)
  • +
  • Increase the timeout when waiting for the event log (#19264)
  • +
  • Implement IDisposable in NamedPipeClient (#18341) (Thanks @xtqqczze!)
  • +
  • Always regenerate files wxs fragment (#19196)
  • +
  • Bump Microsoft.PowerShell.MarkdownRender (#19751)
  • +
  • Delete symbols on Linux as well (#19735)
  • +
  • Add prompt to fix conflict during backport (#19583)
  • +
  • Add backport function to release tools (#19568)
  • +
  • Add an explicit manual stage for changelog update (#19551)
  • +
  • Update the team member list in releaseTools.psm1 (#19544)
  • +
  • Verify that packages have license data (#19543)
  • +
  • Fix the regex used for package name check in vPack build (#19511)
  • +
  • Make the vPack PAT library more obvious (#19505)
  • +
  • Update the metadata.json to mark 7.3 releases as latest for stable channel (#19565)
  • +
+ +
+ +[7.3.5]: https://github.com/PowerShell/PowerShell/compare/v7.3.4...v7.3.5 + +## [7.3.4] - 2023-04-12 + +### Engine Updates and Fixes + +- Add instrumentation to `AmsiUtil` and make the `init` variable readonly (#18727) +- Fix support for `NanoServer` due to the lack of AMSI (#18882) +- Adding missing guard for telemetry optout to avoid `NullReferenceException` when importing modules (#18949) (Thanks @powercode!) +- Fix `VtSubstring` helper method to correctly check chars copied (#19240) +- Fix `ConciseView` to handle custom `ParserError` error records (#19239) + +### Build and Packaging Improvements + +
+ + + +

Bump to use .NET 7.0.5

+ +
+ +
    +
  • Update ThirdPartyNotices.txt
  • +
  • Update cgmanifest.json
  • +
  • Fix the template that creates nuget package
  • +
  • Update the wix file
  • +
  • Update to .NET SDK 7.0.203
  • +
  • Skip VT100 tests on Windows Server 2012R2 as console does not support it (#19413)
  • +
  • Improve package management acceptance tests by not going to the gallery (#19412)
  • +
  • Fix stage dependencies and typo in release build (#19353)
  • +
  • Fix issues in release build and release pipeline (#19338)
  • +
  • Restructure the package build to simplify signing and packaging stages (#19321)
  • +
  • Test fixes for stabilizing tests (#19068)
  • +
  • Add stage for symbols job in Release build (#18937)
  • +
  • Use reference assemblies generated by dotnet (#19302)
  • +
  • Add URL for all distributions (#19159)
  • +
+ +
+ +[7.3.4]: https://github.com/PowerShell/PowerShell/compare/v7.3.3...v7.3.4 + +## [7.3.3] - 2023-02-23 + +### Build and Packaging Improvements + +
+ + + +

Bump to use .NET 7.0.3

+ +
+ +
    +
  • Update third party notices for v7.3.3 (Internal 24353)
  • +
  • Add tool to trigger license information gathering for NuGet modules (#18827)
  • +
  • Update global.json to 7.0.200 for v7.3.3 (Internal 24334)
  • +
  • Update cgmanifest for v7.3.3 (Internal 24338)
  • +
+ +
+ +[7.3.3]: https://github.com/PowerShell/PowerShell/compare/v7.3.2...v7.3.3 + +## [7.3.2] - 2023-01-24 + +### Engine Updates and Fixes + +- Fix `SuspiciousContentChecker.Match` to detect a predefined string when the text starts with it (#18916) +- Fix for JEA session leaking functions (Internal 23820) + +### General Cmdlet Updates and Fixes + +- Fix `Start-Job` to check the existence of working directory using the PowerShell way (#18917) +- Fix `Switch-Process` error to include the command that is not found (#18650) + +### Tests + +- Allow system lock down test debug hook to work with new `WLDP` API (fixes system lock down tests) (#18962) + +### Build and Packaging Improvements + +
+ + + +

Bump to use .NET 7.0.2

+ +
+ +
    +
  • Update dependencies for .NET release (Internal 23818)
  • +
  • Remove unnecessary reference to System.Runtime.CompilerServices.Unsafe (#18918)
  • +
  • Add bootstrap after SBOM task to re-install .NET (#18891)
  • +
+ +
+ +[7.3.2]: https://github.com/PowerShell/PowerShell/compare/v7.3.1...v7.3.2 + +## [7.3.1] - 2022-12-13 + +### Engine Updates and Fixes + +- Remove TabExpansion for PSv2 from remote session configuration (Internal 23331) +- Add `sqlcmd` to list to use legacy argument passing (#18645 #18646) +- Change `exec` from alias to function to handle arbitrary args (#18644) +- Fix `Switch-Process` to copy the current env to the new process (#18632) +- Fix issue when completing the first command in a script with an empty array expression (#18355) +- Fix `Switch-Process` to set `termios` appropriate for child process (#18572) +- Fix native access violation (#18571) + +### Tests + +- Backport CI fixed from #18508 (#18626) +- Mark charset test as pending (#18609) + +### Build and Packaging Improvements + +
+ + + +

We thank the following contributors!

+ +
+ +
    +
  • Update packages (Internal 23330)
  • +
  • Apply expected file permissions to linux files after authenticode signing (#18647)
  • +
  • Bump System.Data.SqlClient (#18573)
  • +
  • Don't install based on build-id for RPM (#18570)
  • +
  • Work around args parsing issue (#18607)
  • +
  • Fix package download in vPack job
  • +
+ +
+ +[7.3.1]: https://github.com/PowerShell/PowerShell/compare/v7.3.0...v7.3.1 + +## [7.3.0] - 2022-11-08 + +### General Cmdlet Updates and Fixes + +- Correct calling cmdlet `New-PSSessionOption` in script for `Restart-Computer` (#18374) + +### Tests + +- Add test for framework dependent package in release pipeline (Internal 23139) + +### Build and Packaging Improvements + +
+ + + +

Bump to use internal .NET 7 GA build (Internal 23096)

+ +
+ +
    +
  • Fix issues with building test artifacts (Internal 23116)
  • +
  • Use AzFileCopy task instead of AzCopy.exe
  • +
  • Remove AzCopy installation from msixbundle step
  • +
  • Add TSAUpload for APIScan (#18446)
  • +
  • Add authenticode signing for assemblies on Linux builds (#18440)
  • +
  • Do not remove penimc_cor3.dll from build (#18438)
  • +
  • Allow two-digit revisions in vPack package validation pattern (#18392)
  • +
  • Bump Microsoft.PowerShell.Native from 7.3.0-rc.1 to 7.3.0 (#18413)
  • +
+ +
+ +[7.3.0]: https://github.com/PowerShell/PowerShell/compare/v7.3.0-rc.1...v7.3.0 + +## [7.3.0-rc.1] - 2022-10-26 + +### Breaking Change + +- Update to use `ComputeCore.dll` for PowerShell Direct (#18194) + +### Engine Updates and Fixes + +- On Unix, explicitly terminate the native process during cleanup only if it's not running in background (#18215) + +### General Cmdlet Updates and Fixes + +- Remove the `ProcessorArchitecture` portion from the full name as it's obsolete (#18320) + +### Tests + +- Add missing `-Tag 'CI'` to describe blocks. (#18317) + +### Build and Packaging Improvements + +
+ + +

Bump to .NET 7 to 7.0.100-rc.2.22477.20 (#18328)(#18286)

+
+ +
    +
  • Update ThirdPartyNotices (Internal 22987)
  • +
  • Remove API sets (#18304) (#18376)
  • +
  • Do not cleanup pwsh.deps.json for framework dependent packages (#18300)
  • +
  • Bump Microsoft.PowerShell.Native from 7.3.0-preview.1 to 7.3.0-rc.1 (#18217)
  • +
  • Remove unnecessary native dependencies from the package (#18213)
  • +
  • Make the link to minimal package blob public during release (#18158)
  • +
  • Create tasks to collect and publish hashes for build files. (#18276)(#18277)
  • +
  • Add branch counter to compliance build (#18214)
  • +
  • Move APIScan to compliance build (#18191)
  • +
  • Update MSI exit message (#18137)
  • +
  • Remove XML files for min-size package (#18189)
  • +
+ +
+ +[7.3.0-rc.1]: https://github.com/PowerShell/PowerShell/compare/v7.3.0-preview.8...v7.3.0-rc.1 + +## [7.3.0-preview.8] - 2022-09-20 + +### General Cmdlet Updates and Fixes + +- Filter out compiler generated types for `Add-Type -PassThru` (#18095) +- Fix error formatting to use color defined in `$PSStyle.Formatting` (#17987) +- Handle `PSObject` argument specially in method invocation logging (#18060) +- Revert the experimental feature `PSStrictModeAssignment` (#18040) +- Make experimental feature `PSAMSIMethodInvocationLogging` stable (#18041) +- Make experimental feature `PSAnsiRenderingFileInfo` stable (#18042) +- Make experimental feature `PSCleanBlock` stable (#18043) +- Make experimental feature `PSNativeCommandArgumentPassing` stable (#18044) +- Make experimental feature `PSExec` stable (#18045) +- Make experimental feature `PSRemotingSSHTransportErrorHandling` stable (#18046) +- Add the `ConfigurationFile` option to the PowerShell help content (#18093) + +### Build and Packaging Improvements + + +

Bump .NET SDK to version `7.0.100-rc.1`

+
+ +
+
    +
  • Update ThirdPartyNotices.txt for 7.3.0-preview.8 (Internal 22553)
  • +
  • Update cgmanifest.json for 7.3.0-preview.8 (Internal 22551)
  • +
  • Re-enable building with Ready-to-Run (#18107)
  • +
  • Make sure Security.types.ps1xml gets signed in release build (#17930)
  • +
  • Update DotnetRuntimeMetadata.json for .NET 7 RC1 build (#18106)
  • +
  • Add XML reference documents to NuPkg files for SDK (#18017)
  • +
  • Make Register MU timeout (#17995)
  • +
  • Bump Microsoft.NET.Test.Sdk from 17.2.0 to 17.3.0 (#17924)
  • +
  • Update list of PS team members in release tools (#17928)
  • +
  • Update to use version 2.21.0 of Application Insights (#17927)
  • +
  • Complete ongoing Write-Progress in test (#17922)
  • +
+
+ +[7.3.0-preview.8]: https://github.com/PowerShell/PowerShell/compare/v7.3.0-preview.7...v7.3.0-preview.8 + +## [7.3.0-preview.7] - 2022-08-09 + +### Breaking Changes + +- Move the type data definition of `System.Security.AccessControl.ObjectSecurity` to the `Microsoft.PowerShell.Security` module (#16355) (Thanks @iSazonov!) + +### Engine Updates and Fixes + +- Enable searching for assemblies in `GAC_Arm64` on Windows (#17816) +- Fix parser exception in using statements with empty aliases (#16745) (Thanks @MartinGC94!) +- Do not always collapse space between parameter and value for native arguments. (#17708) +- Remove `PSNativePSPathResolution` experimental feature (#17670) + +### General Cmdlet Updates and Fixes + +- Fix for deserializing imported ordered dictionary (#15545) (Thanks @davidBar-On!) +- Make generated implicit remoting modules backward compatible with PowerShell 5.1 (#17227) (Thanks @Tadas!) +- Re-enable IDE0031: Use Null propagation (#17811) (Thanks @fflaten!) +- Allow commands to still be executed even if the current working directory no longer exists (#17579) +- Stop referencing `Microsoft.PowerShell.Security` when the core snapin is used (#17771) +- Add support for HTTPS with `Set-AuthenticodeSignature -TimeStampServer` (#16134) (Thanks @Ryan-Hutchison-USAF!) +- Add type accelerator `ordered` for `OrderedDictionary` (#17804) (Thanks @fflaten!) +- Fix the definition of the `PDH_COUNTER_INFO` struct (#17779) +- Adding Virtualization Based Security feature names to Get-ComputerInfo (#16415) (Thanks @mattifestation!) +- Fix `FileSystemProvider` to work with volume and pipe paths (#15873) +- Remove pre-parse for array-based JSON (#15684) (Thanks @strawgate!) +- Improve type inference for `$_` (#17716) (Thanks @MartinGC94!) +- Prevent braces from being removed when completing variables (#17751) (Thanks @MartinGC94!) +- Fix type inference for `ICollection` (#17752) (Thanks @MartinGC94!) +- Fix `Test-Json` not handling non-object types at root (#17741) (Thanks @dkaszews!) +- Change `Get-ChildItem` to treat trailing slash in path as indicating a directory when used with `-Recurse` (#17704) +- Add `find.exe` to legacy argument binding behavior for Windows (#17715) +- Add completion for index expressions for dictionaries (#17619) (Thanks @MartinGC94!) +- Fix enum-ranges for `ValidateRange` in proxy commands (#17572) (Thanks @fflaten!) +- Fix type completion for attribute tokens (#17484) (Thanks @MartinGC94!) +- Add `-noprofileloadtime` switch to `pwsh` (#17535) (Thanks @rkeithhill!) +- Fix legacy `ErrorView` types to use `$host.PrivateData` colors (#17705) +- Improve dynamic parameter tab completion (#17661) (Thanks @MartinGC94!) +- Avoid binding positional parameters when completing parameter in front of value (#17693) (Thanks @MartinGC94!) +- Render decimal numbers in a table using current culture (#17650) + +### Code Cleanup + +
+ + + +

We thank the following contributors!

+

@fflaten, @Molkree, @eltociear

+ +
+ +
    +
  • Fix other path constructions using Path.Join (#17825)
  • +
  • Use null propagation (#17787)(#17789)(#17790)(#17791)(#17792)(#17795) (Thanks @fflaten!)
  • +
  • Re-enable compound assignment preference (#17784) (Thanks @Molkree!)
  • +
  • Use null-coalescing assignment (#17719)(#17720)(#17721)(#17722)(#17723)(#17724)(#17725)(#17726)(#17727)(#17728)(#17729) (Thanks @Molkree!)
  • +
  • Disable the warning IDE0031 to take .NET 7 Preview 7 (#17770)
  • +
  • Fix typo in ModuleCmdletBase.cs (#17714) (Thanks @eltociear!)
  • +
+ +
+ +### Tests + +- Re-enable tests because the corresponding dotnet issues were fixed (#17839) +- Add test for `LanguageMode` using remoting (#17803) (Thanks @fflaten!) +- Fix test perf by stopping ongoing `write-progress` (#17749) (Thanks @fflaten!) +- Re-enable the test `TestLoadNativeInMemoryAssembly` (#17738) + +### Build and Packaging Improvements + +
+ + + +

We thank the following contributors!

+

@varunsh-coder, @dkaszews, @Molkree, @ChuckieChen945

+ +
+ +
    +
  • Update release pipeline to use Approvals and automate some manual tasks (#17837)
  • +
  • Add GitHub token permissions for workflows (#17781) (Thanks @varunsh-coder!)
  • +
  • Bump actions/github-script from 3 to 6 (#17842)
  • +
  • Bump cirrus-actions/rebase from 1.6 to 1.7 (#17843)
  • +
  • Remove unneeded verbose message in build (#17840)
  • +
  • Detect default runtime using dotnet --info in build.psm1 (#17818) (Thanks @dkaszews!)
  • +
  • Bump actions/checkout from 2 to 3 (#17828)
  • +
  • Bump actions/download-artifact from 2 to 3 (#17829)
  • +
  • Bump github/codeql-action from 1 to 2 (#17830)
  • +
  • Bump peter-evans/create-pull-request from 3 to 4 (#17831)
  • +
  • Bump actions/upload-artifact from 2 to 3 (#17832)
  • +
  • Enable Dependabot for GitHub Actions (#17775) (Thanks @Molkree!)
  • +
  • Update .NET SDK version from 7.0.100-preview.6.22352.1 to 7.0.100-preview.7.22377.5 (#17776)
  • +
  • Fix a bug in install-powershell.ps1 (#17794) (Thanks @ChuckieChen945!)
  • +
  • Bump xunit from 2.4.1 to 2.4.2 (#17817)
  • +
  • Update how to update homebrew (#17798)
  • +
  • Don't run link check on forks (#17797)
  • +
  • Update dotnetmetadata.json to start consuming .NET 7 preview 7 builds (#17736)
  • +
  • Bump PackageManagement from 1.4.7 to 1.4.8.1 (#17709)
  • +
  • Exclude ARM images from running in CI (#17713)
  • +
+ +
+ +### Documentation and Help Content + +- Update the comment about why R2R is disabled (#17850) +- Update changelog and `.spelling` for `7.3.0-preview.6` release (#17835) +- Updated `ADOPTERS.md` for Power BI (#17766) +- Update README.md with the current Fedora version (#15717) (Thanks @ananya26-vishnoi!) +- Update `README` and `metadata.json` for next release (#17676) (Thanks @SeeminglyScience!) + +[7.3.0-preview.7]: https://github.com/PowerShell/PowerShell/compare/v7.3.0-preview.6...v7.3.0-preview.7 + +## [7.3.0-preview.6] - 2022-07-18 + +### General Cmdlet Updates and Fixes + +- Fix `Export-PSSession` to not throw error when a rooted path is specified for `-OutputModule` (#17671) +- Change `ConvertFrom-Json -AsHashtable` to use ordered hashtable (#17405) +- Remove potential ANSI escape sequences in strings before using in `Out-GridView` (#17664) +- Add the `-Milliseconds` parameter to `New-TimeSpan` (#17621) (Thanks @NoMoreFood!) +- Update `Set-AuthenticodeSignature` to use `SHA256` as the default (#17560) (Thanks @jborean93!) +- Fix tab completion regression when completing `ValidateSet` values (#17628) (Thanks @MartinGC94!) +- Show optional parameters as such when displaying method definition and overloads (#13799) (Thanks @eugenesmlv!) + +### Code Cleanup + +
+ + + +

We thank the following contributors!

+

@sethvs, @MartinGC94, @eltociear

+ +
+ +
    +
  • Fix comment in InternalCommands.cs (#17669) (Thanks @sethvs!)
  • +
  • Use discards for unused variables (#17620) (Thanks @MartinGC94!)
  • +
  • Fix typo in CommonCommandParameters.cs (#17524) (Thanks @eltociear!)
  • +
+ +
+ +### Tests + +- Fix SDK tests for release build (#17678) + +### Build and Packaging Improvements + +
+ + + +

We thank the following contributors!

+

@tamasvajk

+ +
+ +
    +
  • Create test artifacts for Windows ARM64 (#17675)
  • +
  • Update to the latest NOTICES file (#17607)
  • +
  • Update .NET SDK version from 7.0.100-preview.5.22307.18 to 7.0.100-preview.6.22352.1 (#17634)
  • +
  • Set the compound assignment preference to false (#17632)
  • +
  • Update DotnetMetadata.json to start consuming .NET 7 Preview 6 builds (#17630)
  • +
  • Install .NET 3.1 as it is required by the vPack task (#17600)
  • +
  • Update to use PSReadLine v2.2.6 (#17595)
  • +
  • Fix build.psm1 to not specify both version and quality for dotnet-install (#17589) (Thanks @tamasvajk!)
  • +
  • Bump Newtonsoft.Json in /test/perf/dotnet-tools/Reporting (#17592)
  • +
  • Bump Newtonsoft.Json in /test/perf/dotnet-tools/ResultsComparer (#17566)
  • +
  • Disable RPM SBOM test. (#17532)
  • +
+ +
+ +### Documentation and Help Content + +- Remove `katacoda.com` from doc as it now returns 404 (#17625) +- Update changelog for `v7.2.5` and `v7.3.0-preview.5` (#17565) +- Update `README.md` and `metadata.json` for upcoming releases (#17526) + +[7.3.0-preview.6]: https://github.com/PowerShell/PowerShell/compare/v7.3.0-preview.5...v7.3.0-preview.6 + +## [7.3.0-preview.5] - 2022-06-21 + +### Engine Updates and Fixes + +- Improve type inference and completions (#16963) (Thanks @MartinGC94!) +- Make `Out-String` and `Out-File` keep string input unchanged (#17455) +- Make `AnsiRegex` able to capture Hyperlink ANSI sequences (#17442) +- Add the `-ConfigurationFile` command-line parameter to `pwsh` to support local session configuration (#17447) +- Fix native library loading for `osx-arm64` (#17365) (Thanks @awakecoding!) +- Fix formatting to act appropriately when the style of table header or list label is empty string (#17463) + +### General Cmdlet Updates and Fixes + +- Fix various completion issues inside the `param` block (#17489) (Thanks @MartinGC94!) +- Add Amended switch to `Get-CimClass` cmdlet (#17477) (Thanks @iSazonov!) +- Improve completion on operators (#17486) (Thanks @MartinGC94!) +- Improve array element completion for command arguments (#17078) (Thanks @matt9ucci!) +- Use AST extent for `PSScriptRoot` path completion (#17376) +- Add type inference support for generic methods with type parameters (#16951) (Thanks @MartinGC94!) +- Write out OSC indicator only if the `stdout` is not redirected (#17419) +- Remove the assert and use a relatively larger capacity to cover possible increase of .NET reference assemblies (#17423) +- Increase reference assembly count to 161 (#17420) + +### Code Cleanup + +
+ + + +

We thank the following contributors!

+

@Yulv-git, @eltociear

+ +
+ +
    +
  • Fix some typos in source code (#17481) (Thanks @Yulv-git!)
  • +
  • Fix typo in `AsyncResult.cs` (#17396) (Thanks @eltociear!)
  • +
+ +
+ +### Tools + +- Update script to pin to .NET 7 preview 5 version (#17448) +- Start-PSPester: argument completer for `-Path` (#17334) (Thanks @powercode!) +- Add reminder workflows (#17387) +- Move to configuring the fabric bot via JSON (#17411) +- Update Documentation Issue Template URL (#17410) (Thanks @michaeltlombardi!) +- Update script to automatically take new preview prerelease builds (#17375) + +### Tests + +- Make Assembly Load Native test work on a FX Dependent Linux Install (#17380) +- Update `Get-Error` test to not depend on DNS APIs (#17471) + +### Build and Packaging Improvements + +
+ +
    +
  • Update .NET SDK version from 7.0.100-preview.4.22252.9 to 7.0.100-preview.5.22307.18 (#17402)
  • +
  • Downgrade the Microsoft.CodeAnalysis.NetAnalyzers package to 7.0.0-preview1.22217.1 (#17515)
  • +
  • Rename mariner package to cm (#17505)
  • +
  • Bump Microsoft.CodeAnalysis.NetAnalyzers (#17476)
  • +
  • Bump NJsonSchema from 10.7.1 to 10.7.2 (#17475)
  • +
  • Publish preview versions of mariner to preview repo (#17451)
  • +
  • Update to the latest NOTICES file (#17421)
  • +
  • Do not publish package for Mariner 1.0 (#17415)
  • +
  • Add AppX capabilities in MSIX manifest so that PS7 can call the AppX APIs (#17416)
  • +
  • Update to the latest NOTICES file (#17401)
  • +
  • Fix mariner mappings (#17413)
  • +
  • Update the cgmanifest (#17393)
  • +
  • Bump `NJsonSchema` from `10.7.0` to `10.7.1` (#17381)
  • +
+ +
+ +### Documentation and Help Content + +- Update to the latest NOTICES file (#17493) (Thanks @github-actions[bot]!) +- Update the cgmanifest (#17478) (Thanks @github-actions[bot]!) +- Correct spelling in Comments and tests (#17480) (Thanks @Yulv-git!) +- Fix spelling errors introduced in changelog (#17414) +- Update changelog for v7.3.0-preview.4 release (#17412) +- Update readme and metadata for 7.3.0-preview.4 release (#17378) + +[7.3.0-preview.5]: https://github.com/PowerShell/PowerShell/compare/v7.3.0-preview.4...v7.3.0-preview.5 + +## [7.3.0-preview.4] - 2022-05-23 + +### Engine Updates and Fixes + +
    +
  • Remove the use of BinaryFormatter in PSRP serialization (#17133) (Thanks @jborean93!)
  • +
  • Update telemetry collection removing unused data and adding some new data (#17304)
  • +
  • Fix the word wrapping in formatting to handle escape sequences properly (#17316)
  • +
  • Fix the error message in Hashtable-to-object conversion (#17329)
  • +
  • Add support for new WDAC API (#17247)
  • +
  • On Windows, reset cursor visibility back to previous state when rendering progress (#16782)
  • +
  • Fix the list view to not leak VT decorations (#17262)
  • +
  • Fix formatting truncation to handle strings with VT sequences (#17251)
  • +
  • Fix line breakpoints for return statements without a value (#17179)
  • +
  • Fix for partial PowerShell module search paths, that can be resolved to CWD locations (#17231) (Internal 20126)
  • +
  • Change logic in the testing helper module for determining whether PSHOME is writable (#17218)
  • +
  • Make a variable assignment in a ParenExpression to return the variable value (#17174)
  • +
  • Use new Windows signature APIs from Microsoft.Security.Extensions package (#17159)
  • +
  • Do not include node names when sending telemetry. (#16981)
  • +
  • Support forward slashes in network share (UNC path) completion (#17111) (#17117) (Thanks @sba923!)
  • +
  • Do not generate clean block in proxy function when the feature is disabled (#17112)
  • +
  • Ignore failure attempting to set console window title (#16948)
  • +
  • Update regex used to remove ANSI escape sequences to be more specific to decoration and CSI sequences (#16811)
  • +
  • Improve member auto completion (#16504) (Thanks @MartinGC94!)
  • +
  • Prioritize ValidateSet completions over Enums for parameters (#15257) (Thanks @MartinGC94!)
  • +
  • Add Custom Remote Connections Feature (#17011)
  • +
+ +### General Cmdlet Updates and Fixes + +
    +
  • Add check for ScriptBlock wrapped in PSObject to $using used in ForEach-Object -Parallel (#17234) (Thanks @ryneandal!)
  • +
  • Fix ForEach method to set property on a scalar object (#17213)
  • +
  • Fix Sort-Object -Stable -Unique to actually do stable sorting (#17189) (Thanks @m1k0net!)
  • +
  • Add OutputType attribute to various commands (#16962) (Thanks @MartinGC94!)
  • +
  • Make Stop-Service only request needed privileges when not setting SDDL. (#16663) (Thanks @kvprasoon!)
  • +
+ +### Code Cleanup + +
    +
  • Remove EventLogLogProvider and its related legacy code (#17027)
  • +
  • Fix typos in names of method (#17003) (Thanks @al-cheb!)
  • +
  • SemanticChecks: Avoid repeated type resolution of [ordered] (#17328) (Thanks IISResetMe!)
  • +
  • Redo the change that was reverted by #15853 (#17357)
  • +
  • Correct spelling of pseudo in Compiler.cs (#17285) (Thanks @eltociear!)
  • +
  • MakeNameObscurerTelemetryInitializer internal (#17214)
  • +
  • Make NameObscurerTelemetryInitializer internal (#17167)
  • +
  • Correct Typo in the resource string PathResolvedToMultiple (#17098) (Thanks @charltonstanley!)
  • +
  • Fix typo in ComRuntimeHelpers.cs (#17104) (Thanks @eltociear!)
  • +
+ +### Documentation and Help Content + +
    +
  • Update link to PowerShell remoting in depth video (#17166)
  • +
+ +### Tests + +
    +
  • Add -because to the failing test to aid in debugging (#17030)
  • +
  • Simplify Enum generator for the -bnot operator test (#17014)
  • +
  • Improve unique naming for tests (#17043)
  • +
  • Use a random string for the missing help topic to improve the chances that the help topic really won't be found. (#17042)
  • +
+ +### Build and Packaging Improvements + +
    +
  • Update README.md and metadata.json for v7.3.0-preview.3 release (#17029)
  • +
  • Do not pull dotnet updates from internal feed (#17007)
  • +
  • Simplify Get-WSManSupport based on current .NET Distro Support (#17356)
  • +
  • Update to the latest NOTICES file (#17372, #17332, #17311, #17275)
  • +
  • Run on every PR and let the action skip (#17366)
  • +
  • Make sure verbose message is not null (#17363)
  • +
  • Release changelogs (#17364)
  • +
  • Update build versions (#17318)
  • +
  • Add Daily Link Check GitHub Workflow (#17351)
  • +
  • Update the cgmanifest (#17361, #17344, #17324, #17302, #17268)
  • +
  • Bump NJsonSchema from 10.6.10 to 10.7.0 (#17350)
  • +
  • Disable broken macOS CI job, which is unused (#17221)
  • +
  • Have rebase workflow Post a message when it starts (#17341)
  • +
  • Update DotnetRuntimeMetadata.json for .NET 7 Preview 4 (#17336)
  • +
  • Update Ubuntu 22 to be detected as not supported WSMan (#17338)
  • +
  • Bump xunit.runner.visualstudio from 2.4.3 to 2.4.5 (#17274)
  • +
  • Make sure we execute tests on LTS package for older LTS releases (#17326)
  • +
  • Bump Microsoft.NET.Test.Sdk from 17.1.0 to 17.2.0 (#17320)
  • +
  • Add fedora to the OS's that can't run WSMan (#17325)
  • +
  • Add sles15 support to install-powershell.sh (#16984)
  • +
  • Start rotating through all images (#17315)
  • +
  • Update .NET SDK version from 7.0.100-preview.2.22153.17 to 7.0.100-preview.4.22252.9 (#17061)
  • +
  • Disable release security analysis for SSH CI (#17303)
  • +
  • Add a finalize template which causes jobs with issues to fail (#17314)
  • +
  • Add mapping for ubuntu22.04 jammy (#17317)
  • +
  • Enable more tests to be run in a container. (#17294)
  • +
  • Fix build.psm1 to find the required .NET SDK version when a higher version is installed (#17299)
  • +
  • Improve how Linux container CI builds are identified (#17295)
  • +
  • Only inject NuGet security analysis if we are using secure nuget.config (#17293)
  • +
  • Reduce unneeded verbose message from build.psm1 (#17291)
  • +
  • Switch to using GitHub action to verify Markdown links for PRs (#17281)
  • +
  • Put Secure supply chain analysis at correct place (#17273)
  • +
  • Fix build id variable name when selecting CI container (#17279)
  • +
  • Add rotation between the two mariner images (#17277)
  • +
  • Update to use mcr.microsoft.com (#17272)
  • +
  • Update engine working group members (#17271)
  • +
  • Bump PSReadLine from 2.2.2 to 2.2.5 in /src/Modules (#17252)
  • +
  • Update timeout for daily (#17263)
  • +
  • Bump NJsonSchema from 10.6.9 to 10.6.10 (#16902)
  • +
  • Update the cgmanifest (#17260)
  • +
  • Fix Generate checksum file for packages build failure - v7.1.7 (#17219) (Internal 20274)
  • +
  • Move cgmanifest generation to daily (#17258)
  • +
  • Bump Microsoft.CodeAnalysis.NetAnalyzers (#17245)
  • +
  • Update to the latest notice file (#17238)
  • +
  • Add container to Linux CI (#17233)
  • +
  • Mark Microsoft.Management.Infrastructure.Runtime.Win as a developer dependency to hide in notice file (#17230)
  • +
  • Fixing dotnet SDK version parsing in build.psm1 (#17198) (Thanks @powercode!)
  • +
  • Fixed package names verification to support multi-digit versions (#17220)
  • +
  • Bump Microsoft.CodeAnalysis.CSharp from 4.2.0-1.final to 4.2.0-4.final (#17210)
  • +
  • Add backport action (#17212)
  • +
  • Updated changelogs for v7.0.9 / v7.0.10 / v7.1.6 / v7.1.7 / v7.2.2 / v7.2.3 (#17207)
  • +
  • Updated metadata.json and README.md for v7.2.3 and v7.0.10 (#17158)
  • +
  • Update package fallback list for ubuntu (from those updated for ubuntu 22.04) (deb) (#17180)
  • +
  • Update wix to include security extensions package (#17171)
  • +
  • Update rebase.yml (#17170)
  • +
  • Adds sha256 digests to RPM packages (#16896) (Thanks @ngharo!)
  • +
  • Make mariner packages Framework dependent (#17151)
  • +
  • Update to the latest notice file (#17169)
  • +
  • Update to the latest notice file (#17146)
  • +
  • Replace . in notices container name (#17154)
  • +
  • Allow multiple installations of dotnet. (#17141)
  • +
  • Bump Microsoft.CodeAnalysis.NetAnalyzers (#17105)
  • +
  • Update to the latest notice file (#16437)
  • +
  • Skip failing scriptblock tests (#17093)
  • +
  • Update dotnet-install script download link (#17086)
  • +
  • Fix the version of the Microsoft.CodeAnalysis.NetAnalyzers package (#17075)
  • +
  • Update dotnetmetadata.json to accept .NET 7 preview 3 builds (#17063)
  • +
  • Re-enable PowerShellGet tests targeting PowerShell gallery (#17062)
  • +
  • Add mariner 1.0 amd64 package (#17057)
  • +
  • Create checksum file for global tools (#17056)
  • +
  • Bump Microsoft.CodeAnalysis.NetAnalyzers (#17065)
  • +
  • Use new cask format (#17064)
  • +
+ +[7.3.0-preview.4]: https://github.com/PowerShell/PowerShell/compare/v7.3.0-preview.3...v7.3.0-preview.4 + +## [7.3.0-preview.3] - 2022-03-21 + +### Engine Updates and Fixes + +- Fix the parsing code for .NET method generic arguments (#16937) +- Allow the `PSGetMemberBinder` to get value of `ByRef` property (#16956) +- Allow a collection that contains `Automation.Null` elements to be piped to pipeline (#16957) + +### General Cmdlet Updates and Fixes + +- Add the module `CompatPowerShellGet` to the allow-list of telemetry modules (#16935) +- Fix `Enter-PSHostProcess` and `Get-PSHostProcessInfo` cmdlets by handling processes that have exited (#16946) +- Improve Hashtable completion in multiple scenarios (#16498) (Thanks @MartinGC94!) + +### Code Cleanup + +- Fix a typo in `CommandHelpProvider.cs` (#16949) (Thanks @eltociear!) + +### Tests + +- Update a few tests to make them more stable in CI (#16944) +- Roll back Windows images used in testing to Windows Server 2019 (#16958) + +### Build and Packaging Improvements + +
+ + +

Update .NET SDK to 7.0.0-preview.2

+
+ +
    +
  • Update .NET to 7.0.0-preview.2 build (#16930)
  • +
  • Update AzureFileCopy task and fix the syntax for specifying pool (#17013)
  • +
+ +
+ +[7.3.0-preview.3]: https://github.com/PowerShell/PowerShell/compare/v7.3.0-preview.2...v7.3.0-preview.3 + +## [7.3.0-preview.2] - 2022-02-24 + +### Engine Updates and Fixes + +- Fix the `clean` block for generated proxy function (#16827) +- Add support to allow invoking method with generic type arguments (#12412 and #16822) (Thanks @vexx32!) +- Report error when PowerShell built-in modules are missing (#16628) + +### General Cmdlet Updates and Fixes + +- Prevent command completion if the word to complete is a single dash (#16781) (Thanks @ayousuf23!) +- Use `FindFirstFileW` instead of `FindFirstFileExW` to correctly handle Unicode filenames on FAT32 (#16840) (Thanks @iSazonov!) +- Add completion for loop labels after Break/Continue (#16438) (Thanks @MartinGC94!) +- Support OpenSSH options for `PSRP` over SSH commands (#12802) (Thanks @BrannenGH!) +- Adds a `.ResolvedTarget` Property to `File-System` Items to Reflect a Symlink's Target as `FileSystemInfo` (#16490) (Thanks @hammy3502!) +- Use `NotifyEndApplication` to re-enable VT mode (#16612) +- Add new parameter to `Start-Sleep`: `[-Duration] ` (#16185) (Thanks @IISResetMe!) +- Add lock and null check to remoting internals (#16542) (#16683) (Thanks @SergeyZalyadeev!) +- Make `Measure-Object` ignore missing properties unless running in strict mode (#16589) (Thanks @KiwiThePoodle!) +- Add `-StrictMode` to `Invoke-Command` to allow specifying strict mode when invoking command locally (#16545) (Thanks @Thomas-Yu!) +- Fix `$PSNativeCommandArgPassing` = `Windows` to handle empty args correctly (#16639) +- Reduce the amount of startup banner text (#16516) (Thanks @rkeithhill!) +- Add `exec` cmdlet for bash compatibility (#16462) +- Add AMSI method invocation logging as experimental feature (#16496) +- Fix web cmdlets so that an empty `Get` does not include a `content-length` header (#16587) +- Update `HelpInfoUri` for 7.3 release (#16646) +- Fix parsing `SemanticVersion` build label from version string (#16608) +- Fix `ForEach-Object -Parallel` when passing in script block variable (#16564) + +### Code Cleanup + +
+ + + +

We thank the following contributors!

+

@eltociear, @iSazonov, @xtqqczze

+ +
+ +
    +
  • Fix typo in PowerShellExecutionHelper.cs (#16776) (Thanks @eltociear!)
  • +
  • Use more efficient platform detection API (#16760) (Thanks @iSazonov!)
  • +
  • Seal ClientRemotePowerShell (#15802) (Thanks @xtqqczze!)
  • +
  • Fix the DSC overview URL in a Markdown file and some small cleanup changes (#16629)
  • +
+ +
+ +### Tools + +- Fix automation to update experimental JSON files in GitHub action (#16837) + +### Tests + +- Update `markdownlint` to the latest version (#16825) +- Bump the package `path-parse` from `1.0.6` to `1.0.7` (#16820) +- Remove assert that is incorrect and affecting our tests (#16588) + +### Build and Packaging Improvements + +
+ + + +

We thank the following contributors!

+

@dahlia

+ +
+ +
    +
  • Update NuGet Testing to not re-install dotnet, +when not needed and dynamically determine the DOTNET_ROOT (Internal 19268, 19269, 19272, 19273, and 19274)
  • +
  • Remove SkipExperimentalFeatureGeneration when building alpine (Internal 19248)
  • +
  • Revert .NET 7 changes, Update to the latest .NET 6 and Update WXS file due to blocking issue in .NET 7 Preview 1
  • +
  • Install and Find AzCopy
  • +
  • Use Start-PSBootStrap for installing .NET during nuget packaging
  • +
  • Fix pool syntax for deployments (Internal 19189)
  • +
  • Bump NJsonSchema from 10.5.2 to 10.6.9 (#16888)
  • +
  • Update projects and scripts to use .NET 7 preview 1 prerelease builds (#16856)
  • +
  • Add warning messages when package precheck fails (#16867)
  • +
  • Refactor Global Tool packaging to include SBOM generation (#16860)
  • +
  • Update to use windows-latest as the build agent image (#16831)
  • +
  • Ensure alpine and arm SKUs have powershell.config.json file with experimental features enabled (#16823)
  • +
  • Update experimental feature json files (#16838) (Thanks @github-actions[bot]!)
  • +
  • Remove WiX install (#16834)
  • +
  • Add experimental json update automation (#16833)
  • +
  • Update .NET SDK to 6.0.101 and fix Microsoft.PowerShell.GlobalTool.Shim.csproj (#16821)
  • +
  • Add SBOM manifest to nuget packages (#16711)
  • +
  • Improve logic for updating .NET in CI (#16808)
  • +
  • Add Linux package dependencies for packaging (#16807)
  • +
  • Switch to our custom images for build and release (#16801)
  • +
  • Remove all references to cmake for the builds in this repo (#16578)
  • +
  • Fix build for new InvokeCommand attributes (#16800)
  • +
  • Let macOS installer run without Rosetta on Apple Silicon (#16742) (Thanks @dahlia!)
  • +
  • Update the expect .NET SDK quality to GA for installing dotnet (#16784)
  • +
  • Change nuget release yaml to use UseDotNet task (#16701)
  • +
  • Bump Microsoft.ApplicationInsights from 2.19.0 to 2.20.0 (#16642)
  • +
  • Register NuGet source when generating CGManifest (#16570)
  • +
  • Update Images used for release (#16580)
  • +
  • Update SBOM generation (#16641)
  • +
  • Bring changes from 7.3.0-preview.1 (#16640)
  • +
  • Update the vmImage and PowerShell root directory for macOS builds (#16611)
  • +
  • Update macOS build image and root folder for build (#16609)
  • +
  • Disabled Yarn cache in markdown.yml (#16599)
  • +
  • Update cgmanifest (#16600)
  • +
  • Fix broken links in Markdown (#16598)
  • +
+ +
+ +### Documentation and Help Content + +- Add newly joined members to their respective Working Groups (#16849) +- Update Engine Working Group members (#16780) +- Replace the broken link about pull request (#16771) +- Update changelog to remove a broken URL (#16735) +- Updated `README.md` and `metadata.json` for `v7.3.0-preview.1` release (#16627) +- Updating changelog for `7.2.1` (#16616) +- Updated `README.md` and `metadata.json` for `7.2.1` release (#16586) + +[7.3.0-preview.2]: https://github.com/PowerShell/PowerShell/compare/v7.3.0-preview.1...v7.3.0-preview.2 + +## [7.3.0-preview.1] - 2021-12-16 + +### Breaking Changes + +- Add `clean` block to script block as a peer to `begin`, `process`, and `end` to allow easy resource cleanup (#15177) +- Change default for `$PSStyle.OutputRendering` to `Ansi` (Internal 18449) + +### Engine Updates and Fixes + +- Remove duplicate remote server mediator code (#16027) +- Fix `PSVersion` parameter version checks and error messages for PowerShell 7 remoting (#16228) +- Use the same temporary home directory when `HOME` env variable is not set (#16263) +- Fix parser to generate error when array has more than 32 dimensions (#16276) + +### Performance + +- Avoid validation for built-in file extension and color VT sequences (#16320) (Thanks @iSazonov!) + +### General Cmdlet Updates and Fixes + +- Update `README.md` and `metadata.json` for next preview release (#16107) +- Use `PlainText` when writing to a host that doesn't support VT (#16092) +- Remove support for `AppExeCLinks` to retrieve target (#16044) +- Move `GetOuputString()` and `GetFormatStyleString()` to `PSHostUserInterface` as public API (#16075) +- Fix `ConvertTo-SecureString` with key regression due to .NET breaking change (#16068) +- Fix regression in `Move-Item` to only fallback to `copy and delete` in specific cases (#16029) +- Set `$?` correctly for command expression with redirections (#16046) +- Use `CurrentCulture` when handling conversions to `DateTime` in `Add-History` (#16005) (Thanks @vexx32!) +- Fix link header parsing to handle unquoted `rel` types (#15973) (Thanks @StevenLiekens!) +- Fix a casting error when using `$PSNativeCommandUsesErrorActionPreference` (#15993) +- Format-Wide: Fix `NullReferenceException` (#15990) (Thanks @DarylGraves!) +- Make the native command error handling optionally honor `ErrorActionPreference` (#15897) +- Remove declaration of experimental features in Utility module manifest as they are stable (#16460) +- Fix race condition between `DisconnectAsync` and `Dispose` (#16536) (Thanks @i3arnon!) +- Fix the `Max_PATH` condition check to handle long path correctly (#16487) (Thanks @Shriram0908!) +- Update `HelpInfoUri` for 7.2 release (#16456) +- Fix tab completion within the script block specified for the `ValidateScriptAttribute`. (#14550) (Thanks @MartinGC94!) +- Update `README.md` to specify gathered telemetry (#16379) +- Fix typo for "privacy" in MSI installer (#16407) +- Remove unneeded call to `File.ResolveLinkTarget` from `IsWindowsApplication` (#16371) (Thanks @iSazonov!) +- Add `-HttpVersion` parameter to web cmdlets (#15853) (Thanks @hayhay27!) +- Add support to web cmdlets for open-ended input tags (#16193) (Thanks @farmerau!) +- Add more tests to `Tee-Object -Encoding` (#14539) (Thanks @rpolley!) +- Don't throw exception when trying to resolve a possible link path (#16310) +- Fix `ConvertTo-Json -Depth` to allow 100 at maximum (#16197) (Thanks @KevRitchie!) +- Fix for SSH remoting when banner is enabled on SSHD endpoint (#16205) +- Disallow all COM for AppLocker system lock down (#16268) +- Configure `ApplicationInsights` to not send cloud role name (#16246) +- Disallow `Add-Type` in NoLanguage mode on a locked down machine (#16245) +- Specify the executable path as `TargetObect` for non-zero exit code `ErrorRecord` (#16108) (Thanks @rkeithhill!) +- Don't allow `Move-Item` with FileSystemProvider to move a directory into itself (#16198) +- Make property names for the color VT sequences consistent with documentations (#16212) +- Fix `PipelineVariable` to set variable in the right scope (#16199) +- Invoke-Command: improve handling of variables with $using: expression (#16113) (Thanks @dwtaber!) +- Change `Target` from a `CodeProperty` to be an `AliasProperty` that points to `FileSystemInfo.LinkTarget` (#16165) + +### Code Cleanup + +
+ + + +

We thank the following contributors!

+

@xtqqczze, @eltociear, @iSazonov

+ +
+ +
    +
  • Improve CommandInvocationIntrinsics API documentation and style (#14369)
  • +
  • Use bool?.GetValueOrDefault() in FormatWideCommand (#15988) (Thanks @xtqqczze!)
  • +
  • Remove 4 assertions which cause debug build test runs to fail (#15963)
  • +
  • Fix typo in `Job.cs` (#16454) (Thanks @eltociear!)
  • +
  • Remove unnecessary call to `ToArray` (#16307) (Thanks @iSazonov!)
  • +
  • Remove the unused `FollowSymLink` function (#16231)
  • +
  • Fix typo in `TypeTable.cs` (#16220) (Thanks @eltociear!)
  • +
  • Fixes #16176 - replace snippet tag with code tag in comments (#16177)
  • +
+ +
+ +### Tools + +- Fix typo in build.psm1 (#16038) (Thanks @eltociear!) +- Add `.stylecop` to `filetypexml` and format it (#16025) +- Enable sending Teams notification when workflow fails (#15982) +- Use `Convert-Path` for unknown drive in `Build.psm1` (#16416) (Thanks @matt9ucci!) + +### Tests + +- Add benchmark to test compiler performance (#16083) +- Enable two previously disabled `Get-Process` tests (#15845) (Thanks @iSazonov!) +- Set clean state before testing `UseMU` in the MSI (#16543) +- Fix global tool and SDK tests in release pipeline (#16342) +- Remove the outdated test (#16269) +- Removed old not-used-anymore docker-based tests for PS release packages (#16224) + +### Build and Packaging Improvements + +
+ + + +

We thank the following contributors!

+

@github-actions[bot], @kondratyev-nv

+ +
+ +
    +
  • fix issue with hash file getting created before we have finished get-childitem (#16170)
  • +
  • Add sha256 hashes to release (#16147)
  • +
  • Change path for Component Governance for build to the path we actually use to build (#16137)
  • +
  • Update Microsoft.CodeAnalysis.CSharp version (#16138)
  • +
  • Bump Microsoft.CodeAnalysis.NetAnalyzers (#16070)
  • +
  • Update .NET to 6.0.100-rc.1.21458.32 (#16066)
  • +
  • Update minimum required OS version for macOS (#16088)
  • +
  • Set locale correctly on Linux CI (#16073)
  • +
  • Ensure locale is set correctly on Ubuntu 20.04 in CI (#16067)
  • +
  • Bump Microsoft.CodeAnalysis.NetAnalyzers (#16045)
  • +
  • Update .NET SDK version from `6.0.100-rc.1.21430.44` to `6.0.100-rc.1.21455.2` (#16041) (Thanks @github-actions[bot]!)
  • +
  • Fix the GitHub Action for updating .NET daily builds (#16042)
  • +
  • Bump Microsoft.CodeAnalysis.CSharp from 4.0.0-3.final to 4.0.0-4.21430.4 (#16036)
  • +
  • Bump .NET to `6.0.100-rc.1.21430.44` (#16028)
  • +
  • Move from PkgES hosted agents to 1ES hosted agents (#16023)
  • +
  • Bump Microsoft.CodeAnalysis.NetAnalyzers (#16021)
  • +
  • Update Ubuntu images to use Ubuntu 20.04 (#15906)
  • +
  • Fix the mac build by updating the pool image name (#16010)
  • +
  • Use Alpine 3.12 for building PowerShell for alpine (#16008)
  • +
  • Update .NET SDK version from `6.0.100-preview.6.21355.2` to `6.0.100-rc.1.21426.1` (#15648) (Thanks @github-actions[bot]!)
  • +
  • Ignore error from Find-Package (#15999)
  • +
  • Find packages separately for each source in UpdateDotnetRuntime.ps1 script (#15998)
  • +
  • Update metadata to start using .NET 6 RC1 builds (#15981)
  • +
  • Bump Microsoft.CodeAnalysis.NetAnalyzers (#15985)
  • +
  • Merge the v7.2.0-preview.9 release branch back to GitHub master (#15983)
  • +
  • Publish global tool package for stable releases (#15961)
  • +
  • Bump Microsoft.CodeAnalysis.NetAnalyzers to newer version (#15962)
  • +
  • Disabled Yarn cache in markdown.yml (#16599)
  • +
  • Update cgmanifest (#16600)
  • +
  • Fix broken links in Markdown (#16598)
  • +
  • Add explicit job name for approval tasks in Snap stage (#16579)
  • +
  • Bring back pwsh.exe for framework dependent packages to support Start-Job (#16535)
  • +
  • Fix NuGet package generation in release build (#16509)
  • +
  • Add `Microsoft.PowerShell.Commands.SetStrictModeCommand.ArgumentToPSVersionTransformationAttribute` to list of patterns to remove for generated ref assembly (#16489)
  • +
  • Bump Microsoft.CodeAnalysis.CSharp from `4.0.0-6.final` to `4.0.1` (#16423)
  • +
  • use different containers for different branches (#16434)
  • +
  • Add import so we can use common GitHub workflow function. (#16433)
  • +
  • Remove prerelease .NET 6 build sources (#16418)
  • +
  • Update release instructions with link to new build (#16419)
  • +
  • Bump Microsoft.ApplicationInsights from 2.18.0 to 2.19.0 (#16413)
  • +
  • Update metadata.json to make 7.2.0 the latest LTS (#16417)
  • +
  • Make static CI a matrix (#16397)
  • +
  • Update metadata.json in preparation on 7.3.0-preview.1 release (#16406)
  • +
  • Update cgmanifest (#16405)
  • +
  • Add diagnostics used to take corrective action when releasing `buildInfoJson` (#16404)
  • +
  • `vPack` release should use `buildInfoJson` new to 7.2 (#16402)
  • +
  • Update the usage of metadata.json for getting LTS information (#16381)
  • +
  • Add checkout to build json stage to get `ci.psm1` (#16399)
  • +
  • Update CgManifest.json for 6.0.0 .NET packages (#16398)
  • +
  • Add current folder to the beginning of the module import (#16353)
  • +
  • Increment RC MSI build number by 100 (#16354)
  • +
  • Bump XunitXml.TestLogger from 3.0.66 to 3.0.70 (#16356)
  • +
  • Move PR Quantifier config to subfolder (#16352)
  • +
  • Release build info json when it is preview (#16335)
  • +
  • Add an approval for releasing build-info json (#16351)
  • +
  • Generate manifest with latest public version of the packages (#16337)
  • +
  • Update to the latest notices file (#16339) (Thanks @github-actions[bot]!)
  • +
  • Use notice task to generate license assuming cgmanifest contains all components (#16340)
  • +
  • Refactor cgmanifest generator to include all components (#16326)
  • +
  • Fix issues in release build (#16332)
  • +
  • Update feed and analyzer dependency (#16327)
  • +
  • Bump Microsoft.NET.Test.Sdk from 16.11.0 to 17.0.0 (#16312)
  • +
  • Update license and cgmanifest (#16325) (Thanks @github-actions[bot]!)
  • +
  • Fix condition in cgmanifest logic (#16324)
  • +
  • Add GitHub Workflow to keep notices up to date (#16284)
  • +
  • Update to latest .NET 6 GA build 6.0.100-rtm.21527.11 (#16309)
  • +
  • Create compliance build (#16286)
  • +
  • Move mapping file into product repo and add Debian 11 (#16316)
  • +
  • Add a major-minor build info JSON file (#16301)
  • +
  • Clean up crossgen related build scripts also generate native symbols for R2R images (#16297)
  • +
  • Fix Windows build ZIP packaging (#16299) (Thanks @kondratyev-nv!)
  • +
  • Revert "Update to use .NET 6 GA build (#16296)" (#16308)
  • +
  • Add wget as a dependency for Bootstrap script (#16303) (Thanks @kondratyev-nv!)
  • +
  • Fix issues reported by code signing verification tool (#16291)
  • +
  • Update to use .NET 6 GA build (#16296)
  • +
  • Revert "add GH workflow to keep the cgmanifest up to date." (#16294)
  • +
  • Update ChangeLog for 7.2.0-rc.1 and also fix RPM packaging (#16290)
  • +
  • Bump Microsoft.CodeAnalysis.NetAnalyzers (#16271)
  • +
  • add GH workflow to keep the cgmanifest up to date.
  • +
  • Update ThirdPartyNotices.txt (#16283)
  • +
  • Update `testartifacts.yml` to use ubuntu-latest image (#16279)
  • +
  • Update version of Microsoft.PowerShell.Native and Microsoft.PowerShell.MarkdownRender packages (#16277)
  • +
  • Add script to generate cgmanifest.json (#16278)
  • +
  • Add cgmanifest.json for generating correct third party notice file (#16266)
  • +
  • Bump Microsoft.CodeAnalysis.NetAnalyzers from `6.0.0-rtm.21504.2` to `6.0.0-rtm.21516.1` (#16264)
  • +
  • Only upload stable buildinfo for stable releases (#16251)
  • +
  • Make RPM license recognized (#16189)
  • +
  • Don't upload dep or tar.gz for RPM because there are none. (#16230)
  • +
  • Add condition to generate release files in local dev build only (#16259)
  • +
  • Update .NET 6 to version 6.0.100-rc.2.21505.57 (#16249)
  • +
  • change order of try-catch-finally and split out arm runs (#16252)
  • +
  • Ensure psoptions.json and manifest.spdx.json files always exist in packages (#16258)
  • +
  • Update to vPack task version to 12 (#16250)
  • +
  • Remove unneeded `NuGetConfigFile` resource string (#16232)
  • +
  • Add Software Bill of Materials to the main packages (#16202)
  • +
  • Sign third party exes (#16229)
  • +
  • Upgrade set-value package for Markdown test (#16196)
  • +
  • Use Ubuntu 20.04 for SSH remoting test (#16225)
  • +
  • Bump Microsoft.CodeAnalysis.NetAnalyzers (#16194)
  • +
  • Bump `Microsoft.CodeAnalysis.NetAnalyzers` from `6.0.0-rc2.21458.5` to `6.0.0-rtm.21480.8` (#16183)
  • +
  • Move vPack build to 1ES Pool (#16169)
  • +
  • Fix Microsoft update spelling issue. (#16178)
  • +
+ +
+ +### Documentation and Help Content + +- Update Windows PowerShell issues link (#16105) (Thanks @andschwa!) +- Remove Joey from Committee and WG membership (#16119) +- Update more docs for `net6.0` TFM (#16102) (Thanks @xtqqczze!) +- Change `snippet` tag to `code` tag in XML comments (#16106) +- Update build documentation to reflect .NET 6 (#15751) (Thanks @Kellen-Stuart!) +- Update `README.md` about the changelogs (#16471) (Thanks @powershellpr0mpt!) +- Update changelog for 7.2.0 (#16401) +- Update `metadata.json` and `README.md` for 7.2.0 release (#16395) +- Update `README.md` and `metadata.json` files for `v7.2.0-rc.1` release (#16285) +- Update the changelogs for `v7.0.8` and `v7.1.5` releases (#16248) + +[7.3.0-preview.1]: https://github.com/PowerShell/PowerShell/compare/v7.2.0-preview.10...v7.3.0-preview.1 diff --git a/CHANGELOG/7.4.md b/CHANGELOG/7.4.md new file mode 100644 index 00000000000..89a3f64e37c --- /dev/null +++ b/CHANGELOG/7.4.md @@ -0,0 +1,1537 @@ +# 7.4 Changelog + +## [7.4.13] + +### Build and Packaging Improvements + +
+ + + +

Update .NET SDK to 8.0.415

+ +
+ +
    +
  • [release/v7.4] Update StableRelease to not be the latest (#26042)
  • +
  • [release/v7.4] Update Ev2 Shell Extension Image to AzureLinux 3 for PMC Release (#26033)
  • +
  • [release/v7.4] Add 7.4.12 Changelog (#26018)
  • +
  • [release/v7.4] Fix variable reference for release environment in pipeline (#26014)
  • +
  • Backport Release Pipeline Changes (Internal 37169)
  • +
  • [release/v7.4] Update branch for release (#26194)
  • +
  • [release/v7.4] Mark the 3 consistently failing tests as pending to unblock PRs (#26197)
  • +
  • [release/v7.4] Remove UseDotnet task and use the dotnet-install script (#26170)
  • +
  • [release/v7.4] Automate Store Publishing (#26163)
  • +
  • [release/v7.4] add CodeQL suppresion for NativeCommandProcessor (#26174)
  • +
  • [release/v7.4] add CodeQL suppressions for UpdatableHelp and NativeCommandProcessor methods (#26172)
  • +
  • [release/v7.4] Suppress false positive PSScriptAnalyzer warnings in tests and build scripts (#26058)
  • +
  • [release/v7.4] Ensure that socket timeouts are set only during the token validation (#26080)
  • +
+ +
+ +[7.4.13]: https://github.com/PowerShell/PowerShell/compare/v7.4.12...v7.4.13 + +## [7.4.12] + +### Tools + +- Add CodeQL suppressions (#25973) + +### Build and Packaging Improvements + +
+ + + +

Update .NET SDK to 8.0.413

+ +
+ +
    +
  • Add LinuxHost Network configuration to PowerShell Packages pipeline (#26003)
  • +
  • Update container images to use mcr.microsoft.com for Linux and Azure Linux (#25987)
  • +
  • Update SDK to 8.0.413 (#25993)
  • +
  • Make logical template name consistent between pipelines (#25992)
  • +
  • Remove AsyncSDL from Pipelines Toggle Official/NonOfficial Runs (#25965)
  • +
+ +
+ +### Documentation and Help Content + +- Update third-party library versions to `8.0.19` for `ObjectPool`, Windows Compatibility, and `System.Drawing.Common` (#26001) + +[7.4.12]: https://github.com/PowerShell/PowerShell/compare/v7.4.11...v7.4.12 + +## [7.4.11] - 2025-06-17 + +### Engine Updates and Fixes + +- Move .NET method invocation logging to after the needed type conversion is done for method arguments (#25568) + +### Build and Packaging Improvements + +
+ + + +

Update .NET SDK to 8.0.411

+ +
+ +
    +
  • Correct Capitalization Referencing Templates (#25672)
  • +
  • Manually update SqlClient in TestService
  • +
  • Update cgmanifest
  • +
  • Update package references
  • +
  • Update .NET SDK to latest version
  • +
  • Change linux packaging tests to ubuntu latest (#25640)
  • +
+ +
+ +### Documentation and Help Content + +- Update Third Party Notices (#25524, #25659) + +[7.4.11]: https://github.com/PowerShell/PowerShell/compare/v7.4.10...v7.4.11 + + +## [7.4.10] + +### Engine Updates and Fixes + +- Fallback to AppLocker after `WldpCanExecuteFile` (#25229) + +### Code Cleanup + +
+ +
    +
  • Remove obsolete template from Windows Packaging CI (#25405)
  • +
  • Cleanup old release pipelines (#25404)
  • +
+ +
+ +### Tools + +- Do not run labels workflow in the internal repository (#25411) + +### Build and Packaging Improvements + +
+ + + +

Update .NET SDK to 8.0.408

+ +
+ +
    +
  • Update branch for release (#25518)
  • +
  • Move MSIXBundle to Packages and Release to GitHub (#25516)
  • +
  • Add CodeQL suppressions for PowerShell intended behavior (#25376)
  • +
  • Enhance path filters action to set outputs for all changes when not a PR (#25378)
  • +
  • Fix Merge Errors from #25401 and Internal 33077 (#25478)
  • +
  • Fix MSIX artifact upload, vPack template, changelog hashes, git tag command (#25476)
  • +
  • Fix Conditional Parameter to Skip NuGet Publish (#25475)
  • +
  • Use new variables template for vPack (#25474)
  • +
  • Add Windows Store Signing to MSIX bundle (#25472)
  • +
  • Update test result processing to use NUnitXml format and enhance logging for better clarity (#25471)
  • +
  • Fix the expected path of .NET after using UseDotnet 2 task to install (#25470)
  • +
  • Update Microsoft.PowerShell.PSResourceGet to 1.1.0 (#25469)
  • +
  • Combine GitHub and Nuget Release Stage (#25473)
  • +
  • Make GitHub Workflows work in the internal mirror (#25409)
  • +
  • Add default .NET install path for SDK validation (#25339)
  • +
  • Update APIScan to use new symbols server (#25400)
  • +
  • Use GitHubReleaseTask (#25401)
  • +
  • Migrate MacOS Signing to OneBranch (#25412)
  • +
  • Remove call to NuGet (#25410)
  • +
  • Restore a script needed for build from the old release pipeline cleanup (#25201) (#25408)
  • +
  • Switch to ubuntu-latest for CI (#25406)
  • +
  • Update GitHub Actions to work in private GitHub repository (#25403)
  • +
  • Simplify PR Template (#25407)
  • +
  • Disable SBOM generation on set variables job in release build (#25341)
  • +
  • Update package pipeline windows image version (#25192)
  • +
+ +
+ +[7.4.10]: https://github.com/PowerShell/PowerShell/compare/v7.4.9...v7.4.10 + +## [7.4.9] + +### Notes + +_This release is internal only. It is not available for download._ + +### Tools + +- Check GH token availability for `Get-Changelog` (#25156) + +### Build and Packaging Improvements + +
+ + + +

Update .NET SDK to 8.0.407

+ +
+ +
    +
  • Update branch for release (#25101)
  • +
  • Only build Linux for packaging changes (#25161)
  • +
  • Skip additional packages when generating component manifest (#25160)
  • +
  • Remove Az module installs and AzureRM uninstalls in pipeline (#25157)
  • +
  • Add GitHub Actions workflow to verify PR labels (#25158)
  • +
  • Update security extensions (#25099)
  • +
  • Make Component Manifest Updater use neutral target in addition to RID target (#25100)
  • +
+ +
+ +[7.4.9]: https://github.com/PowerShell/PowerShell/compare/v7.4.8...v7.4.9 + +## [7.4.8] + +### Notes + +_This release is internal only. It is not available for download._ + +### Build and Packaging Improvements + +
+ + + +

Update .NET SDK to 8.0.406

+ +
+ +
    +
  • Update branch for release (#25085) (#24884)
  • +
  • Add UseDotnet task for installing dotnet (#25080)
  • +
  • Add Justin Chung as PowerShell team member in releaseTools.psm1 (#25074)
  • +
  • Fix V-Pack download package name (#25078)
  • +
  • Fix MSIX stage in release pipeline (#25079)
  • +
  • Give the pipeline runs meaningful names (#25081)
  • +
  • Make sure the vPack pipeline does not produce an empty package (#25082)
  • +
  • Update CODEOWNERS (#25083)
  • +
  • Add setup dotnet action to the build composite action (#25084)
  • +
  • Remove AzDO credscan as it is now in GitHub (#25077)
  • +
  • Use workload identity service connection to download makeappx tool from storage account (#25075)
  • +
  • Update .NET SDK (#24993)
  • +
  • Fix GitHub Action filter overmatching (#24957)
  • +
  • Fix release branch filters (#24960)
  • +
  • Convert powershell/PowerShell-CI-macos to GitHub Actions (#24955)
  • +
  • Convert powershell/PowerShell-CI-linux to GitHub Actions (#24945)
  • +
  • Convert powershell/PowerShell-Windows-CI to GitHub Actions (#24932)
  • +
  • PMC parse state correctly from update command's response (#24860)
  • +
  • Add EV2 support for publishing PowerShell packages to PMC (#24857)
  • +
+ +
+ +[7.4.8]: https://github.com/PowerShell/PowerShell/compare/v7.4.7...v7.4.8 + +## [7.4.7] + +### Build and Packaging Improvements + +
+ + + +

Update .NET SDK to 8.0.405

+ +
+ +
    +
  • Update branch for release - Transitive - true - minor (#24546)
  • +
  • Fix backport mistake in #24429 (#24545)
  • +
  • Fix seed max value for Container Linux CI (#24510) (#24543)
  • +
  • Add a way to use only NuGet feed sources (#24528) (#24542)
  • +
  • Bump Microsoft.PowerShell.PSResourceGet to 1.0.6 (#24419)
  • +
  • Update path due to pool change (Internal 33083)
  • +
  • Update pool for "Publish BuildInfo" job (Internal 33082)
  • +
  • Add missing backports and new fixes (Internal 33077)
  • +
  • Port copy blob changes (Internal 33055)
  • +
  • Update firewall to monitor (Internal 33048)
  • +
  • Fix typo in release-MakeBlobPublic.yml (Internal 33046)
  • +
  • Update change log for 7.4.6 (Internal 33040)
  • +
  • Update changelog for v7.4.6 release (Internal 32983)
  • +
  • Fix backport issues with release pipeline (#24835)
  • +
  • Remove duplicated parameter (#24832)
  • +
  • Make the AssemblyVersion not change for servicing releases 7.4.7 and onward (#24821)
  • +
  • Add *.props and sort path filters for windows CI (#24822) (#24823)
  • +
  • Take the newest windows signature nuget packages (#24818)
  • +
  • Use work load identity service connection to download makeappx tool from storage account (#24817) (#24820)
  • +
  • Update path filters for Windows CI (#24809) (#24819)
  • +
  • Fixed release pipeline errors and switched to KS3 (#24751) (#24816)
  • +
  • Update branch for release - Transitive - true - minor (#24806)
  • +
  • Add ability to capture MSBuild Binary logs when restore fails (#24128) (#24799)
  • +
  • Download package from package build for generating vpack (#24481) (#24801)
  • +
  • Add a parameter that skips verify packages step (#24763) (#24803)
  • +
  • Fix Changelog content grab during GitHub Release (#24788) (#24804)
  • +
  • Add tool package download in publish nuget stage (#24790) (#24805)
  • +
  • Add CodeQL scanning to APIScan build (#24303) (#24800)
  • +
  • Deploy Box Update (#24632) (#24802)
  • +
+ +
+ +### Documentation and Help Content + +- Update notices file (#24810) + +[7.4.7]: https://github.com/PowerShell/PowerShell/compare/v7.4.6...v7.4.7 + +## [7.4.6] - 2024-10-22 + +### Build and Packaging Improvements + +
+ + + +

Bump .NET SDK to 8.0.403

+ +
+ +
    +
  • Copy to static site instead of making blob public (#24269) (#24473)
  • +
  • Add ability to capture MSBuild Binary logs when restore fails (#24128)
  • +
  • Keep the roff file when gzipping it. (#24450)
  • +
  • Update PowerShell-Coordinated_Packages-Official.yml (#24449)
  • +
  • Update and add new NuGet package sources for different environments. (#24440)
  • +
  • Add PMC mapping for Debian 12 (bookworm) (#24413)
  • +
  • Fixes to Azure Public feed usage (#24429)
  • +
  • Delete assets/AppImageThirdPartyNotices.txt (#24256)
  • +
  • Delete demos directory (#24258)
  • +
  • Add specific path for issues in tsaconfig (#24244)
  • +
  • Checkin generated manpage (#24423)
  • +
  • Add updated libicu dependency for Debian packages (#24301)
  • +
  • Add mapping to azurelinux repo (#24290)
  • +
  • Update vpack pipeline (#24281)
  • +
  • Add BaseUrl to buildinfo JSON file (#24376)
  • +
  • Delete the msix blob if it's already there (#24353)
  • +
  • Make some release tests run in a hosted pools (#24270)
  • +
  • Create new pipeline for compliance (#24252)
  • +
  • Use Managed Identity for APIScan authentication (#24243)
  • +
  • Check Create and Submit in vPack build by default (#24181)
  • +
  • Capture environment better (#24148)
  • +
  • Refactor Nuget package source creation to use New-NugetPackageSource function (#24104)
  • +
  • Make Microsoft feeds the default (#24426)
  • +
  • Bump to .NET 8.0.403 and update dependencies (#24405)
  • +
+ +
+ +[7.4.6]: https://github.com/PowerShell/PowerShell/compare/v7.4.5...v7.4.6 + +## [7.4.5] - 2024-08-20 + +### General Cmdlet Updates and Fixes + +- Fix WebCmdlets when `-Body` is specified but `ContentType` is not (#24145) + +### Tests + +- Rewrite the mac syslog tests to make them less flaky (#24152) + +### Build and Packaging Improvements + +
+ + + +

Bump .NET SDK to 8.0.400

+ +
+ +
    +
  • Add feature flags for removing network isolation (Internal 32126)
  • +
  • Update ThirdPartyNotices.txt for v7.4.5 (#24160)
  • +
  • Update cgmanifest.json for v7.4.5 (#24159)
  • +
  • Update .NET SDK to 8.0.400 (#24151)
  • +
  • Cleanup unused csproj (#24146)
  • +
  • Remember installation options and used them to initialize options for the next installation (#24143)
  • +
  • Fix failures in GitHub action markdown-link-check (#24142)
  • +
  • Use correct signing certificates for RPM and DEBs (#21522)
  • +
+ +
+ +### Documentation and Help Content + +- Update docs sample nuget.config (#24147) +- Fix up broken links in Markdown files (#24144) + +[7.4.5]: https://github.com/PowerShell/PowerShell/compare/v7.4.4...v7.4.5 + +## [7.4.4] - 2024-07-18 + +### Engine Updates and Fixes + +- Resolve paths correctly when importing files or files referenced in the module manifest (Internal 31780) + +### Build and Packaging Improvements + +
+ + + +

Bump .NET to 8.0.303

+ +
+ +
    +
  • Enumerate over all signed zip packages in macos signing
  • +
  • Update TPN for the v7.4.4 release (Internal 31793)
  • +
  • Add update cgmanifest (Internal 31789)
  • +
  • Add macos signing for package files (#24015) (#24059)
  • +
  • Update .NET SDK to 8.0.303 (#24038)
  • +
+ +
+ +[7.4.4]: https://github.com/PowerShell/PowerShell/compare/v7.4.3...v7.4.4 + +## [7.4.3] - 2024-06-18 + +### General Cmdlet Updates and Fixes + +- Fix the error when using `Start-Process -Credential` without the admin privilege (#21393) (Thanks @jborean93!) +- Fix `Test-Path -IsValid` to check for invalid path and filename characters (#21358) + +### Engine Updates and Fixes + +- Fix generating `OutputType` when running in Constrained Language Mode (#21605) +- Expand `~` to `$home` on Windows with tab completion (#21529) +- Make sure both stdout and stderr can be redirected from a native executable (#20997) + +### Build and Packaging Improvements + +
+ + + +

Update to .NET 8.0.6

+

We thank the following contributors!

+

@ForNeVeR!

+ +
+ +
    +
  • Fixes for change to new Engineering System.
  • +
  • Fix argument passing in GlobalToolShim (#21333) (Thanks @ForNeVeR!)
  • +
  • Create powershell.config.json for PowerShell.Windows.x64 global tool (#23941)
  • +
  • Remove markdown link check on release branches (#23937)
  • +
  • Update to .NET 8.0.6 (#23936)
  • +
  • Fix error in the vPack release, debug script that blocked release (#23904)
  • +
  • Add branch counter variables for daily package builds (#21523)
  • +
  • Updates to package and release pipelines (#23800)
  • +
  • Fix exe signing with third party signing for WiX engine (#23878)
  • +
  • Use PSScriptRoot to find path to Wix module (#21611)
  • +
  • [StepSecurity] Apply security best practices (#21480)
  • +
  • Fix build failure due to missing reference in GlobalToolShim.cs (#21388)
  • +
  • Update installation on Wix module (#23808)
  • +
  • Use feed with Microsoft Wix toolset (#21651)
  • +
  • Create the Windows.x64 global tool with shim for signing (#21559)
  • +
  • Generate MSI for win-arm64 installer (#20516)
  • +
  • update wix package install (#21537)
  • +
  • Add a PAT for fetching PMC cli (#21503)
  • +
  • Official PowerShell Package pipeline (#21504)
  • +
+ +
+ +[7.4.3]: https://github.com/PowerShell/PowerShell/compare/v7.4.2...v7.4.3 + +## [7.4.2] - 2024-04-11 + +### General Cmdlet Updates and Fixes + +- Revert "Adjust PUT method behavior to POST one for default content type in WebCmdlets" (#21049) +- Fix regression with `Get-Content` when `-Tail 0` and `-Wait` are both used (#20734) (Thanks @CarloToso!) +- Fix `Get-Error` serialization of array values (#21085) (Thanks @jborean93!) +- Fix a regression in `Format-Table` when header label is empty (#21156) + +### Engine Updates and Fixes + +- Revert the PR #17856 (Do not preserve temporary results when no need to do so) (#21368) +- Make sure the assembly/library resolvers are registered at early stage (#21361) +- Handle the case that `Runspace.DefaultRunspace` is `null` when logging for WDAC Audit (#21344) +- Fix PowerShell class to support deriving from an abstract class with abstract properties (#21331) +- Fix the regression when doing type inference for `$_` (#21223) (Thanks @MartinGC94!) + +### Build and Packaging Improvements + +
+ + + +

Bump to .NET 8.0.4

+ +
+ +
    +
  • Revert analyzer package back to stable
  • +
  • Update SDK, deps and cgmanifest for 7.4.2
  • +
  • Revert changes to packaging.psm1
  • +
  • Update PSResourceGet version from 1.0.2 to 1.0.4.1 (#21439)
  • +
  • Verify environment variable for OneBranch before we try to copy (#21441)
  • +
  • Remove surrogateFile setting of APIScan (#21238)
  • +
  • Add dotenv install as latest version does not work with current Ruby version (#21239)
  • +
  • Multiple fixes in official build pipeline (#21408)
  • +
  • Add back 2 transitive dependency packages (#21415)
  • +
  • Update PSReadLine to v2.3.5 for the next v7.4.x servicing release (#21414)
  • +
  • PowerShell co-ordinated build OneBranch pipeline (#21364)
  • +
+ +
+ +[7.4.2]: https://github.com/PowerShell/PowerShell/compare/v7.4.1...v7.4.2 + +## [7.4.1] - 2024-01-11 + +### General Cmdlet Updates and Fixes + +- Fix `Group-Object` output using interpolated strings (#20745) (Thanks @mawosoft!) +- Fix `Start-Process -PassThru` to make sure the `ExitCode` property is accessible for the returned `Process` object (#20749) (#20866) (Thanks @CodeCyclone!) +- Fix rendering of DisplayRoot for network PSDrive (#20793) (#20863) + +### Engine Updates and Fixes + +- Ensure filename is not null when logging WDAC ETW events (#20910) (Thanks @jborean93!) +- Fix four regressions introduced by WDAC audit logging feature (#20913) + +### Build and Packaging Improvements + +
+ + + +Bump .NET 8 to version 8.0.101 + + + +
    +
  • Update .NET SDK and dependencies for v7.4.1 (Internal 29142)
  • +
  • Update cgmanifest for v7.4.1 (#20874)
  • +
  • Update package dependencies for v7.4.1 (#20871)
  • +
  • Set the rollForwardOnNoCandidateFx in runtimeconfig.json to roll forward only on minor and patch versions (#20689) (#20865)
  • +
  • Remove RHEL7 publishing to packages.microsoft.com as it's no longer supported (#20849) (#20864)
  • +
  • Fix the tab completion tests (#20867)
  • +
+ +
+ +[7.4.1]: https://github.com/PowerShell/PowerShell/compare/v7.4.0...v7.4.1 + +## [7.4.0] - 2023-11-16 + +### General Cmdlet Updates and Fixes + +- Added a missing `ConfigureAwait(false)` call to webcmdlets so they don't block (#20622) +- Fix `Group-Object` so output uses current culture (#20623) +- Block getting help from network locations in restricted remoting sessions (#20615) + +### Build and Packaging Improvements + +
+ + + +

Bump .NET 8 to 8.0.0 RTM build

+ +
+ +
    +
  • Add internal .NET SDK URL parameter to release pipeline (Internal 28474)
  • +
  • Update the CGManifest file for v7.4.0 release (Internal 28457)
  • +
  • Fix repository root for the nuget.config (Internal 28456)
  • +
  • Add internal nuget feed to compliance build (Internal 28449)
  • +
  • Copy azure blob with PowerShell global tool to private blob and move to CDN during release (Internal 28438)
  • +
  • Fix release build by making the internal SDK parameter optional (#20658) (Internal 28440)
  • +
  • Make internal .NET SDK URL as a parameter for release builld (#20655) (Internal 28428)
  • +
  • Update PSResourceGet version for 1.0.1 release (#20652) (Internal 28427)
  • +
  • Bump .NET 8 to 8.0.0 RTM build (Internal 28360)
  • +
  • Remove Auth header content from ErrorRecord (Internal 28409)
  • +
  • Fix setting of variable to consume internal SDK source (Internal 28354)
  • +
  • Bump Microsoft.Management.Infrastructure to v3.0.0 (Internal 28352)
  • +
  • Bump Microsoft.PowerShell.Native to v7.4.0 (#20617) (#20624)
  • +
+ +
+ +[7.4.0]: https://github.com/PowerShell/PowerShell/compare/v7.4.0-rc.1...v7.4.0 + +## [7.4.0-rc.1] - 2023-10-24 + +### General Cmdlet Updates and Fixes + +- Fix `Test-Connection` due to .NET 8 changes (#20369) (#20531) +- Add telemetry to check for specific tags when importing a module (#20371) (#20540) +- Fix `Copy-Item` progress to only show completed when all files are copied (#20517) (#20544) +- Fix `unixmode` to handle `setuid` and `sticky` when file is not an executable (#20366) (#20537) +- Fix UNC path completion regression (#20419) (#20541) +- Fix implicit remoting proxy cmdlets to act on common parameters (#20367) (#20530) +- Fix `Get-Service` non-terminating error message to include category (#20276) (#20529) +- Fixing regression in DSC (#20268) (#20528) + +### Build and Packaging Improvements + +
+ + + +

We thank the following contributors!

+ +
+ +
    +
  • Update ThirdPartyNotices.txt file (Internal 28110)
  • +
  • Update CGManifest for release
  • +
  • Fix package version for .NET nuget packages (#20551) (#20552)
  • +
  • Only registry App Path for release package (#20478) (#20549)
  • +
  • Bump PSReadLine from 2.2.6 to 2.3.4 (#20305) (#20533)
  • +
  • Bump Microsoft.Management.Infrastructure (#20511) (#20512) (#20433) (#20434) (#20534) (#20535) (#20545) (#20547)
  • +
  • Bump to .NET 8 RC2 (#20510) (#20543)
  • + +
  • Add SBOM for release pipeline (#20519) (#20548)
  • +
  • Bump version of Microsoft.PowerShell.PSResourceGet to v1.0.0 (#20485) (#20538)
  • +
  • Bump xunit.runner.visualstudio from 2.5.1 to 2.5.3 (#20486) (#20542)
  • +
  • Bump JsonSchema.Net from 5.2.5 to 5.2.6 (#20421) (#20532)
  • +
  • Fix alpine tar package name and do not crossgen alpine fxdependent package (#20459) (#20536)
  • +
  • Increase timeout when publishing packages to packages.microsoft.com (#20470) (#20539)
  • +
  • Block any preview vPack release (#20243) (#20526)
  • +
  • Add surrogate file for compliance scanning (#20423)
  • +
+ +
+ +[7.4.0-rc.1]: https://github.com/PowerShell/PowerShell/compare/v7.4.0-preview.6...v7.4.0-rc.1 + +## [7.4.0-preview.6] - 2023-09-28 + +### General Cmdlet Updates and Fixes + +- Set approved experimental features to stable for 7.4 release (#20362) +- Revert changes to continue using `BinaryFormatter` for `Out-GridView` (#20360) +- Remove the comment trigger from feedback provider (#20346) + +### Tests + +- Continued improvement to tests for release automation (#20259) +- Skip the test on x86 as `InstallDate` is not visible on `Wow64` (#20255) +- Harden some problematic release tests (#20254) + +### Build and Packaging Improvements + +
+ + + +

Move to .NET 8.0.100-rc.1.23463.5

+ +
+ +
    +
  • Update the regex for package name validation (Internal 27783, 27795)
  • +
  • Update ThirdPartyNotices.txt (Internal 27772)
  • +
  • Remove the ref folder before running compliance (#20375)
  • +
  • Updates RIDs used to generate component Inventory (#20372)
  • +
  • Bump Microsoft.CodeAnalysis.CSharp from 4.7.0 to 4.8.0-2.final (#20368)
  • +
  • Fix the release build by moving to the official .NET 8-rc.1 release build version (#20365)
  • +
  • Update the experimental feature JSON files (#20363)
  • +
  • Bump XunitXml.TestLogger from 3.1.11 to 3.1.17 (#20364)
  • +
  • Update Microsoft.PowerShell.PSResourceGet to 0.9.0-rc1 (#20361)
  • +
  • Update .NET SDK to version 8.0.100-rc.1.23455.8 (#20358)
  • +
  • Use fxdependent-win-desktop runtime for compliance runs (#20359)
  • +
  • Add mapping for mariner arm64 stable (#20348)
  • +
  • Bump xunit.runner.visualstudio from 2.5.0 to 2.5.1 (#20357)
  • +
  • Bump JsonSchema.Net from 5.2.1 to 5.2.5 (#20356)
  • +
  • Bump Microsoft.NET.Test.Sdk from 17.7.1 to 17.7.2 (#20355)
  • +
  • Bump Markdig.Signed from 0.32.0 to 0.33.0 (#20354)
  • +
  • Bump JsonSchema.Net from 5.1.3 to 5.2.1 (#20353)
  • +
  • Bump actions/checkout from 3 to 4 (#20352)
  • +
  • Bump Microsoft.NET.Test.Sdk from 17.7.0 to 17.7.1 (#20351)
  • +
  • Bump Microsoft.CodeAnalysis.CSharp from 4.7.0-2.final to 4.7.0 (#20350)
  • +
  • Release build: Change the names of the PATs (#20349)
  • +
  • Put the calls to Set-AzDoProjectInfo and Set-AzDoAuthToken` in the right order (#20347)
  • +
  • Bump Microsoft.Management.Infrastructure (continued) (#20262)
  • +
  • Bump Microsoft.Management.Infrastructure to 3.0.0-preview.2 (#20261)
  • +
  • Enable vPack provenance data (#20260)
  • +
  • Start using new packages.microsoft.com cli (#20258)
  • +
  • Add mariner arm64 to PMC release (#20257)
  • +
  • Fix typo donet to dotnet in build scripts and pipelines (#20256)
  • +
+ +
+ +[7.4.0-preview.6]: https://github.com/PowerShell/PowerShell/compare/v7.4.0-preview.5...v7.4.0-preview.6 + +## [7.4.0-preview.5] - 2023-08-21 + +### Breaking Changes + +- Change how relative paths in `Resolve-Path` are handled when using the `RelativeBasePath` parameter (#19755) (Thanks @MartinGC94!) + +### Engine Updates and Fixes + +- Fix dynamic parameter completion (#19510) (Thanks @MartinGC94!) +- Use `OrdinalIgnoreCase` to lookup script breakpoints (#20046) (Thanks @fflaten!) +- Guard against `null` or blank path components when adding to module path (#19922) (Thanks @stevenebutler!) +- Fix deadlock when piping to shell associated file extension (#19940) +- Fix completion regression for filesystem paths with custom `PSDrive` names (#19921) (Thanks @MartinGC94!) +- Add completion for variables assigned by the `Data` statement (#19831) (Thanks @MartinGC94!) +- Fix a null reference crash in completion code (#19916) (Thanks @MartinGC94!) + +### General Cmdlet Updates and Fixes + +- Fix `Out-GridView` by implementing `Clone()` method to replace old use of binary format serialization (#20050) +- Support Unix domain socket in WebCmdlets (#19343) (Thanks @CarloToso!) +- Wait-Process: add `-Any` and `-PassThru` parameters (#19423) (Thanks @dwtaber!) +- Added the switch parameter `-CaseInsensitive` to `Select-Object` and `Get-Unique` cmdlets (#19683) (Thanks @ArmaanMcleod!) +- `Restore-Computer` and `Stop-Computer` should fail with error when not running via `sudo` on Unix (#19824) +- Add Help proxy function for non-Windows platforms (#19972) +- Remove input text from the error message resulted by `SecureString` and `PSCredential` conversion failure (#19977) (Thanks @ArmaanMcleod!) +- Add `Microsoft.PowerShell.PSResourceGet` to the telemetry module list (#19926) + +### Code Cleanup + +
+ + + +

We thank the following contributors!

+

@eltociear, @Molkree, @MartinGC94

+ +
+ +
    +
  • Fix use of ThrowIf where the arguments were reversed (#20052)
  • +
  • Fix typo in Logging.Tests.ps1 (#20048) (Thanks @eltociear!)
  • +
  • Apply the InlineAsTypeCheck in the engine code - 2nd pass (#19694) (Thanks @Molkree!)
  • +
  • Apply the InlineAsTypeCheck rule in the engine code - 1st pass (#19692) (Thanks @Molkree!)
  • +
  • Remove unused string completion code (#19879) (Thanks @MartinGC94!)
  • +
+ +
+ +### Tools + +- Give the `assignPRs` workflow write permissions (#20021) + +### Tests + +- Additional test hardening for tests which fail in release pass. (#20093) +- Don't use a completion which has a space in it (#20064) +- Fixes for release tests (#20028) +- Remove spelling CI in favor of GitHub Action (#19973) +- Hide expected error for negative test on windows for script extension (#19929) +- Add more debugging to try to determine why these test fail in release build. (#19829) + +### Build and Packaging Improvements + +
    +
  • Update ThirdPartyNotices for 7.4.0-preview.5
  • +
  • Update PSResourceGet to 0.5.24-beta24 (#20118)
  • +
  • Fix build after the change to remove win-arm32 (#20102)
  • +
  • Add comment about pinned packages (#20096)
  • +
  • Bump to .NET 8 Preview 7 (#20092)
  • +
  • Remove Win-Arm32 from release build. (#20095)
  • +
  • Add alpine framework dependent package (#19995)
  • +
  • Bump JsonSchema.Net from 4.1.8 to 5.1.3 (#20089)
  • +
  • Bump Microsoft.NET.Test.Sdk from 17.6.3 to 17.7.0 (#20088)
  • +
  • Move build to .NET 8 preview 6 (#19991)
  • +
  • Bump Microsoft.Management.Infrastructure from 2.0.0 to 3.0.0-preview.1 (#20081)
  • +
  • Bump Markdig.Signed from 0.31.0 to 0.32.0 (#20076)
  • +
  • Auto assign PR Maintainer (#20020)
  • +
  • Delete rule that was supposed to round-robin assign a maintainer (#20019)
  • +
  • Update the cgmanifest (#20012)
  • +
  • Update the cgmanifest (#20008)
  • +
  • Bump JsonSchema.Net from 4.1.7 to 4.1.8 (#20006)
  • +
  • Bump JsonSchema.Net from 4.1.6 to 4.1.7 (#20000)
  • +
  • Add mariner arm64 package build to release build (#19946)
  • +
  • Check for pre-release packages when it's a stable release (#19939)
  • +
  • Make PR creation tool use --web because it is more reliable (#19944)
  • +
  • Update to the latest NOTICES file (#19971)
  • +
  • Update variable used to bypass the blocking check for multiple NuGet feeds for release pipeline (#19963)
  • +
  • Update variable used to bypass the blocking check for multiple NuGet feeds (#19967)
  • +
  • Update README.md and metadata.json for release v7.2.13 and v7.3.6 (#19964)
  • +
  • Don't publish notice on failure because it prevent retry (#19955)
  • +
  • Change variable used to bypass nuget security scanning (#19954)
  • +
  • Update the cgmanifest (#19924)
  • +
  • Publish rpm package for rhel9 (#19750)
  • +
  • Bump XunitXml.TestLogger from 3.0.78 to 3.1.11 (#19900)
  • +
  • Bump JsonSchema.Net from 4.1.5 to 4.1.6 (#19885)
  • +
  • Bump xunit from 2.4.2 to 2.5.0 (#19902)
  • +
  • Remove HostArchitecture dynamic parameter for osxpkg (#19917)
  • +
  • FabricBot: Onboarding to GitOps.ResourceManagement because of FabricBot decommissioning (#19905)
  • +
  • Change variable used to bypass nuget security scanning (#19907)
  • +
  • Checkout history for markdown lint check (#19908)
  • +
  • Switch to GitHub Action for linting markdown (#19899)
  • +
  • Bump xunit.runner.visualstudio from 2.4.5 to 2.5.0 (#19901)
  • +
  • Add runtime and packaging type info for mariner2 arm64 (#19450)
  • +
  • Update to the latest NOTICES file (#19856)
  • +
+ + + +### Documentation and Help Content + +- Update `README.md` and `metadata.json` for `7.4.0-preview.4` release (#19872) +- Fix grammatical issue in `ADOPTERS.md` (#20037) (Thanks @nikohoffren!) +- Replace docs.microsoft.com URLs in code with FWLinks (#19996) +- Change `docs.microsoft.com` to `learn.microsoft.com` (#19994) +- Update man page to match current help for pwsh (#19993) +- Merge `7.3.5`, `7.3.6`, `7.2.12` and `7.2.13` changelogs (#19968) +- Fix ///-comments that violate the docs schema (#19957) +- Update the link for getting started in `README.md` (#19932) +- Migrate user docs to the PowerShell-Docs repository (#19871) + +[7.4.0-preview.5]: https://github.com/PowerShell/PowerShell/compare/v7.4.0-preview.4...v7.4.0-preview.5 + +## [7.4.0-preview.4] - 2023-06-29 + +### Breaking Changes + +- `Test-Json`: Use `JsonSchema.Net` (`System.Text.Json`) instead of `NJsonSchema` (`Newtonsoft.Json`) (#18141) (Thanks @gregsdennis!) +- `Test-Connection`: Increase output detail when performing a TCP test (#11452) (Thanks @jackdcasey!) + +### Engine Updates and Fixes + +- Fix native executables not redirecting to file (#19842) +- Add a new experimental feature to control native argument passing style on Windows (#18706) +- Fix `TabExpansion2` variable leak when completing variables (#18763) (Thanks @MartinGC94!) +- Enable completion of variables across ScriptBlock scopes (#19819) (Thanks @MartinGC94!) +- Fix completion of the `foreach` statement variable (#19814) (Thanks @MartinGC94!) +- Fix variable type inference precedence (#18691) (Thanks @MartinGC94!) +- Fix member completion for PowerShell Enum class (#19740) (Thanks @MartinGC94!) +- Fix parsing for array literals in index expressions in method calls (#19224) (Thanks @MartinGC94!) +- Fix incorrect string to type conversion (#19560) (Thanks @MartinGC94!) +- Fix slow execution when many breakpoints are used (#14953) (Thanks @nohwnd!) +- Add a public API for getting locations of `PSModulePath` elements (#19422) +- Add WDAC Audit logging (#19641) +- Improve path completion (#19489) (Thanks @MartinGC94!) +- Fix an indexing out of bound error in `CompleteInput` for empty script input (#19501) (Thanks @MartinGC94!) +- Improve variable completion performance (#19595) (Thanks @MartinGC94!) +- Allow partial culture matching in `Update-Help` (#18037) (Thanks @dkaszews!) +- Fix the check when reading input in `NativeCommandProcessor` (#19614) +- Add support of respecting `$PSStyle.OutputRendering` on the remote host (#19601) +- Support byte stream piping between native commands and file redirection (#17857) + +### General Cmdlet Updates and Fixes + +- Disallow negative values for `Get-Content` cmdlet parameters `-Head` and `-Tail` (#19715) (Thanks @CarloToso!) +- Make `Update-Help` throw proper error when current culture is not associated with a language (#19765) (Thanks @josea!) +- Do not require activity when creating a completed progress record (#18474) (Thanks @MartinGC94!) +- WebCmdlets: Add alias for `-TimeoutSec` to `-ConnectionTimeoutSeconds` and add `-OperationTimeoutSeconds` (#19558) (Thanks @stevenebutler!) +- Avoid checking screen scraping on non-Windows platforms before launching native app (#19812) +- Add reference to PSResourceGet (#19597) +- Add `FileNameStar` to `MultipartFileContent` in WebCmdlets (#19467) (Thanks @CarloToso!) +- Add `ParameterSetName` for the `-Detailed` parameter of `Test-Connection` (#19727) +- Remove the property disabling optimization (#19701) +- Filter completion for enum parameter against `ValidateRange` attributes (#17750) (Thanks @fflaten!) +- Small cleanup `Invoke-RestMethod` (#19490) (Thanks @CarloToso!) +- Fix wildcard globbing in root of device paths (#19442) (Thanks @MartinGC94!) +- Add specific error message that creating Junctions requires absolute path (#19409) +- Fix array type parsing in generic types (#19205) (Thanks @MartinGC94!) +- Improve the verbose message of WebCmdlets to show correct HTTP version (#19616) (Thanks @CarloToso!) +- Fix HTTP status from 409 to 429 for WebCmdlets to get retry interval from Retry-After header. (#19622) (Thanks @mkht!) +- Remove minor versions from `PSCompatibleVersions` (#18635) (Thanks @xtqqczze!) +- Update `JsonSchema.Net` version to 4.1.0 (#19610) (Thanks @gregsdennis!) +- Allow combining of `-Skip` and `-SkipLast` parameters in `Select-Object` cmdlet. (#18849) (Thanks @ArmaanMcleod!) +- Fix constructing `PSModulePath` if a sub-path has trailing separator (#13147) +- Add `Get-SecureRandom` cmdlet (#19587) +- Fix `New-Item` to re-create `Junction` when `-Force` is specified (#18311) (Thanks @GigaScratch!) +- Improve Hashtable key completion for type constrained variable assignments, nested Hashtables and more (#17660) (Thanks @MartinGC94!) +- `Set-Clipboard -AsOSC52` for remote usage (#18222) (Thanks @dkaszews!) +- Refactor `MUIFileSearcher.AddFiles` in the help related code (#18825) (Thanks @xtqqczze!) +- Set `SetLastError` to `true` for symbolic and hard link native APIs (#19566) +- Fix `Get-AuthenticodeSignature -Content` to not roundtrip the bytes to a Unicode string and then back to bytes (#18774) (Thanks @jborean93!) +- WebCmdlets: Rename `-TimeoutSec` to `-ConnectionTimeoutSeconds` (with alias) and add `-OperationTimeoutSeconds` (#19558) (Thanks @stevenebutler!) + +### Code Cleanup + +
+ + + +

We thank the following contributors!

+

@eltociear, @ArmaanMcleod, @turbedi, @CarloToso, @Molkree, @xtqqczze

+ +
+ +
    +
  • Fix typo in NativeCommandProcessor.cs (#19846) (Thanks @eltociear!)
  • +
  • Rename file from PingPathCommand.cs to TestPathCommand.cs (#19782) (Thanks @ArmaanMcleod!)
  • +
  • Make use of the new Random.Shared property (#18417) (Thanks @turbedi!)
  • +
  • six files (#19695) (Thanks @CarloToso!)
  • +
  • Apply IDE0019: InlineAsTypeCheck in Microsoft.PowerShell.Commands (#19688)(#19690)(#19687)(#19689) (Thanks @Molkree!)
  • +
  • Remove PSv2CompletionCompleter as part of the PowerShell v2 code cleanup (#18337) (Thanks @xtqqczze!)
  • +
  • Enable more nullable annotations in WebCmdlets (#19359) (Thanks @CarloToso!)
  • +
+ +
+ +### Tools + +- Add Git mailmap for Andy Jordan (#19469) +- Add backport function to release tools (#19568) + +### Tests + +- Improve reliability of the `Ctrl+c` tests for WebCmdlets (#19532) (Thanks @stevenebutler!) +- Fix logic for `Import-CliXml` test (#19805) +- Add some debugging to the transcript test for `SilentlyContinue` (#19770) +- Re-enable `Get-ComputerInfo` pending tests (#19746) +- Update syslog parser to handle modern formats. (#19737) +- Pass `-UserScope` as required by `RunUpdateHelpTests` (#13400) (Thanks @yecril71pl!) +- Change how `isPreview` is determined for default cmdlets tests (#19650) +- Skip file signature tests on 2012R2 where PKI cmdlet do not work (#19643) +- Change logic for testing missing or extra cmdlets. (#19635) +- Fix incorrect test cases in `ExecutionPolicy.Tests.ps1` (#19485) (Thanks @xtqqczze!) +- Fixing structure typo in test setup (#17458) (Thanks @powercode!) +- Fix test failures on Windows for time zone and remoting (#19466) +- Harden 'All approved Cmdlets present' test (#19530) + +### Build and Packaging Improvements + +
+ + +

Updated to .NET 8 Preview 4 +

We thank the following contributors!

+

@krishnayalavarthi

+ +
+ +
    +
  • Update to the latest NOTICES file (#19537)(#19820)(#19784)(#19720)(#19644)(#19620)(#19605)(#19546)
  • +
  • Bump Microsoft.NET.Test.Sdk from 17.5.0 to 17.6.3 (#19867)(#19762)(#19733)(#19668)(#19613)
  • +
  • Update the cgmanifest (#19847)(#19800)(#19792)(#19776)(#19763)(#19697)(#19631)
  • +
  • Bump StyleCop.Analyzers from 1.2.0-beta.406 to 1.2.0-beta.507 (#19837)
  • +
  • Bump Microsoft.CodeAnalysis.CSharp from 4.6.0-1.final to 4.7.0-2.final (#19838)(#19667)
  • +
  • Update to .NET 8 Preview 4 (#19696)
  • +
  • Update experimental-feature JSON files (#19828)
  • +
  • Bump JsonSchema.Net from 4.1.1 to 4.1.5 (#19790)(#19768)(#19788)
  • +
  • Update group to assign PRs in fabricbot.json (#19759)
  • +
  • Add retry on failure for all upload tasks in Azure Pipelines (#19761)
  • +
  • Bump Microsoft.PowerShell.MarkdownRender from 7.2.0 to 7.2.1 (#19751)(#19752)
  • +
  • Delete symbols on Linux as well (#19735)
  • +
  • Update windows.json packaging BOM (#19728)
  • +
  • Disable SBOM signing for CI and add extra files for packaging tests (#19729)
  • +
  • Update experimental-feature JSON files (#19698(#19588))
  • +
  • Add ProductCode in registry for MSI install (#19590)
  • +
  • Runas format changed (#15434) (Thanks @krishnayalavarthi!)
  • +
  • For Preview releases, add pwsh-preview.exe alias to MSIX package (#19602)
  • +
  • Add prompt to fix conflict during backport (#19583)
  • +
  • Add comment in wix detailing use of UseMU (#19371)
  • +
  • Verify that packages have license data (#19543)
  • +
  • Add an explicit manual stage for changelog update (#19551)
  • +
  • Update the team member list in releaseTools.psm1 (#19544)
  • +
+ +
+ +### Documentation and Help Content + +- Update `metadata.json` and `README.md` for upcoming releases (#19863)(#19542) +- Update message to use the actual parameter name (#19851) +- Update `CONTRIBUTING.md` to include Code of Conduct enforcement (#19810) +- Update `working-group-definitions.md` (#19809)(#19561) +- Update `working-group.md` to add section about reporting working group members (#19758) +- Correct capitalization in readme (#19666) (Thanks @Aishat452!) +- Updated the public dashboard link (#19634) +- Fix a typo in `serialization.cs` (#19598) (Thanks @eltociear!) + +[7.4.0-preview.4]: https://github.com/PowerShell/PowerShell/compare/v7.4.0-preview.3...v7.4.0-preview.4 + +## [7.4.0-preview.3] - 2023-04-20 + +### Breaking Changes + +- Remove code related to `#requires -pssnapin` (#19320) + +### Engine Updates and Fixes + +- Change the arrow used in feedback suggestion to a more common Unicode character (#19534) +- Support trigger registration in feedback provider (#19525) +- Update the `ICommandPredictor` interface to reduce boilerplate code from predictor implementation (#19414) +- Fix a crash in the type inference code (#19400) (Thanks @MartinGC94!) + +### Performance + +- Speed up `Resolve-Path` relative path resolution (#19171) (Thanks @MartinGC94!) + +### General Cmdlet Updates and Fixes + +- Infer external application output as strings (#19193) (Thanks @MartinGC94!) +- Fix a race condition in `Add-Type` (#19471) +- Detect insecure `https-to-http` redirect only if both URIs are absolute (#19468) (Thanks @CarloToso!) +- Support `Ctrl+c` when connection hangs while reading data in WebCmdlets (#19330) (Thanks @stevenebutler!) +- Enable type conversion of `AutomationNull` to `$null` for assignment (#19415) +- Add the parameter `-Environment` to `Start-Process` (#19374) +- Add the parameter `-RelativeBasePath` to `Resolve-Path` (#19358) (Thanks @MartinGC94!) +- Exclude redundant parameter aliases from completion results (#19382) (Thanks @MartinGC94!) +- Allow using a folder path in WebCmdlets' `-OutFile` parameter (#19007) (Thanks @CarloToso!) + +### Code Cleanup + +
+ + + +

We thank the following contributors!

+

@eltociear, @CarloToso

+ +
+ +
    +
  • Fix typo in typeDataXmlLoader.cs (#19319) (Thanks @eltociear!)
  • +
  • Fix typo in Compiler.cs (#19491) (Thanks @eltociear!)
  • +
  • Inline the GetResponseObject method (#19380) (Thanks @CarloToso!)
  • +
  • Simplify ContentHelper methods (#19367) (Thanks @CarloToso!)
  • +
  • Initialize regex lazily in BasicHtmlWebResponseObject (#19361) (Thanks @CarloToso!)
  • +
  • Fix codefactor issue in if-statement (part 5) (#19286) (Thanks @CarloToso!)
  • +
  • Add nullable annotations in WebRequestSession.cs (#19291) (Thanks @CarloToso!)
  • +
+ +
+ +### Tests + +- Harden the default command test (#19416) +- Skip VT100 tests on Windows Server 2012R2 as console does not support it (#19413) +- Improve package management acceptance tests by not going to the gallery (#19412) + +### Build and Packaging Improvements + +
+ + + +

We thank the following contributors!

+

@dkattan

+ +
+ +
    +
  • Fixing MSI checkbox (#19325)
  • +
  • Update the experimental feature JSON files (#19297)
  • +
  • Update the cgmanifest (#19459, #19465)
  • +
  • Update .NET SDK version to 8.0.100-preview.3.23178.7 (#19381)
  • +
  • Force updating the transitive dependency on Microsoft.CSharp (#19514)
  • +
  • Update DotnetRuntimeMetadata.json to consume the .NET 8.0.0-preview.3 release (#19529)
  • +
  • Move PSGallery sync to a pool (#19523)
  • +
  • Fix the regex used for package name check in vPack build (#19511)
  • +
  • Make the vPack PAT library more obvious (#19505)
  • +
  • Change Microsoft.CodeAnalysis.CSharp back to 4.5.0 (#19464) (Thanks @dkattan!)
  • +
  • Update to the latest NOTICES file (#19332)
  • +
  • Add PoolNames variable group to compliance pipeline (#19408)
  • +
  • Fix stage dependencies and typo in release build (#19353)
  • +
  • Fix issues in release build and release pipeline (#19338)
  • +
+ +
+ +[7.4.0-preview.3]: https://github.com/PowerShell/PowerShell/compare/v7.4.0-preview.2...v7.4.0-preview.3 + +## [7.4.0-preview.2] - 2023-03-14 + +### Breaking Changes + +- Update some PowerShell APIs to throw `ArgumentException` instead of `ArgumentNullException` when the argument is an empty string (#19215) (Thanks @xtqqczze!) +- Add the parameter `-ProgressAction` to the common parameters (#18887) + +### Engine Updates and Fixes + +- Fix `PlainText` output to correctly remove the `Reset` VT sequence without number (#19283) +- Fix `ConciseView` to handle custom `ParserError` error records (#19239) +- Fix `VtSubstring` helper method to correctly check characters copied (#19240) +- Update the `FeedbackProvider` interface to return structured data (#19133) +- Make the exception error in PowerShell able to associate with the right history entry (#19095) +- Fix for JEA session leaking functions (#19024) +- Add WDAC events and system lockdown notification (#18893) +- Fix support for nanoserver due to lack of AMSI (#18882) + +### Performance + +- Use interpolated strings (#19002)(#19003)(#18977)(#18980)(#18996)(#18979)(#18997)(#18978)(#18983)(#18992)(#18993)(#18985)(#18988) (Thanks @CarloToso!) + +### General Cmdlet Updates and Fixes + +- Fix completion for `PSCustomObject` variable properties (#18682) (Thanks @MartinGC94!) +- Improve type inference for `Get-Random` (#18972) (Thanks @MartinGC94!) +- Make `-Encoding` parameter able to take `ANSI` encoding in PowerShell (#19298) (Thanks @CarloToso!) +- Telemetry improvements for tracking experimental feature opt out (#18762) +- Support HTTP persistent connections in Web Cmdlets (#19249) (Thanks @stevenebutler!) +- Fix using XML `-Body` in webcmdlets without an encoding (#19281) (Thanks @CarloToso!) +- Add the `Statement` property to `$MyInvocation` (#19027) (Thanks @IISResetMe!) +- Fix `Start-Process` `-Wait` with `-Credential` (#19096) (Thanks @jborean93!) +- Adjust `PUT` method behavior to `POST` one for default content type in WebCmdlets (#19152) (Thanks @CarloToso!) +- Improve verbose message in web cmdlets when content length is unknown (#19252) (Thanks @CarloToso!) +- Preserve `WebSession.MaximumRedirection` from changes (#19190) (Thanks @CarloToso!) +- Take into account `ContentType` from Headers in WebCmdlets (#19227) (Thanks @CarloToso!) +- Use C# 11 UTF-8 string literals (#19243) (Thanks @turbedi!) +- Add property assignment completion for enums (#19178) (Thanks @MartinGC94!) +- Fix class member completion for classes with base types (#19179) (Thanks @MartinGC94!) +- Add `-Path` and `-LiteralPath` parameters to `Test-Json` cmdlet (#19042) (Thanks @ArmaanMcleod!) +- Allow to preserve the original HTTP method by adding `-PreserveHttpMethodOnRedirect` to Web cmdlets (#18894) (Thanks @CarloToso!) +- Webcmdlets display an error on HTTPS to http redirect (#18595) (Thanks @CarloToso!) +- Build the relative URI for links from the response in `Invoke-WebRequest` (#19092) (Thanks @CarloToso!) +- Fix redirection for `-CustomMethod` `POST` in WebCmdlets (#19111) (Thanks @CarloToso!) +- Dispose previous response in Webcmdlets (#19117) (Thanks @CarloToso!) +- Improve `Invoke-WebRequest` XML and JSON errors format (#18837) (Thanks @CarloToso!) +- Fix error formatting to remove the unneeded leading newline for concise view (#19080) +- Add `-NoHeader` parameter to `ConvertTo-Csv` and `Export-Csv` cmdlets (#19108) (Thanks @ArmaanMcleod!) +- Fix `Start-Process -Credential -Wait` to work on Windows (#19082) +- Add `ValidateNotNullOrEmpty` to `OutFile` and `InFile` parameters of WebCmdlets (#19044) (Thanks @CarloToso!) +- Correct spelling of "custom" in event (#19059) (Thanks @spaette!) +- Ignore expected error for file systems not supporting alternate streams (#19065) +- Adding missing guard for telemetry opt out to avoid `NullReferenceException` when importing modules (#18949) (Thanks @powercode!) +- Fix progress calculation divide by zero in Copy-Item (#19038) +- Add progress to `Copy-Item` (#18735) +- WebCmdlets parse XML declaration to get encoding value, if present. (#18748) (Thanks @CarloToso!) +- `HttpKnownHeaderNames` update headers list (#18947) (Thanks @CarloToso!) +- Fix bug with managing redirection and `KeepAuthorization` in Web cmdlets (#18902) (Thanks @CarloToso!) +- Fix `Get-Error` to work with strict mode (#18895) +- Add `AllowInsecureRedirect` switch to Web cmdlets (#18546) (Thanks @CarloToso!) +- `Invoke-RestMethod` `-FollowRelLink` fix links containing commas (#18829) (Thanks @CarloToso!) +- Prioritize the default parameter set when completing positional arguments (#18755) (Thanks @MartinGC94!) +- Add `-CommandWithArgs` parameter to pwsh (#18726) +- Enable creating composite subsystem implementation in modules (#18888) +- Fix `Format-Table -RepeatHeader` for property derived tables (#18870) +- Add `StatusCode` to `HttpResponseException` (#18842) (Thanks @CarloToso!) +- Fix type inference for all scope variables (#18758) (Thanks @MartinGC94!) +- Add completion for Using keywords (#16514) (Thanks @MartinGC94!) + +### Code Cleanup + +
+ + + +

We thank the following contributors!

+

@CarloToso, @iSazonov, @xtqqczze, @turbedi, @syntax-tm, @eltociear, @ArmaanMcleod

+ +
+ +
    +
  • Small cleanup in the WebCmdlet code (#19299) (Thanks @CarloToso!)
  • +
  • Remove unused GUID detection code from console host (#18871) (Thanks @iSazonov!)
  • +
  • Fix CodeFactor issues in the code base - part 4 (#19270) (Thanks @CarloToso!)
  • +
  • Fix codefactor if part 3 (#19269) (Thanks @CarloToso!)
  • +
  • Fix codefactor if part 2 (#19267) (Thanks @CarloToso!)
  • +
  • Fix codefactor if part 1 (#19266) (Thanks @CarloToso!)
  • +
  • Remove comment and simplify condition in WebCmdlets (#19251) (Thanks @CarloToso!)
  • +
  • Small style changes (#19241) (Thanks @CarloToso!)
  • +
  • Use ArgumentException.ThrowIfNullOrEmpty as appropriate [part 1] (#19215) (Thanks @xtqqczze!)
  • +
  • Use using variable to reduce the nested level (#19229) (Thanks @CarloToso!)
  • +
  • Use ArgumentException.ThrowIfNullOrEmpty() in more places (#19213) (Thanks @CarloToso!)
  • +
  • Replace BitConverter.ToString with Convert.ToHexString where appropriate (#19216) (Thanks @turbedi!)
  • +
  • Replace Requires.NotNullOrEmpty(string) with ArgumentException.ThrowIfNullOrEmpty (#19197) (Thanks @xtqqczze!)
  • +
  • Use ArgumentOutOfRangeException.ThrowIfNegativeOrZero when applicable (#19201) (Thanks @xtqqczze!)
  • +
  • Use CallerArgumentExpression on Requires.NotNull (#19200) (Thanks @xtqqczze!)
  • +
  • Revert a few change to not use 'ArgumentNullException.ThrowIfNull' (#19151)
  • +
  • Corrected some minor spelling mistakes (#19176) (Thanks @syntax-tm!)
  • +
  • Fix a typo in InitialSessionState.cs (#19177) (Thanks @eltociear!)
  • +
  • Fix a typo in pwsh help content (#19153)
  • +
  • Revert comment changes in WebRequestPSCmdlet.Common.cs (#19136) (Thanks @CarloToso!)
  • +
  • Small cleanup webcmdlets (#19128) (Thanks @CarloToso!)
  • +
  • Merge partials in WebRequestPSCmdlet.Common.cs (#19126) (Thanks @CarloToso!)
  • +
  • Cleanup WebCmdlets comments (#19124) (Thanks @CarloToso!)
  • +
  • Added minor readability and refactoring fixes to Process.cs (#19123) (Thanks @ArmaanMcleod!)
  • +
  • Small changes in Webcmdlets (#19109) (Thanks @CarloToso!)
  • +
  • Rework SetRequestContent in WebCmdlets (#18964) (Thanks @CarloToso!)
  • +
  • Small cleanup WebCmdlets (#19030) (Thanks @CarloToso!)
  • +
  • Update additional interpolated string changes (#19029)
  • +
  • Revert some of the interpolated string changes (#19018)
  • +
  • Cleanup StreamHelper.cs, WebRequestPSCmdlet.Common.cs and InvokeRestMethodCommand.Common.cs (#18950) (Thanks @CarloToso!)
  • +
  • Small cleanup common code of webcmdlets (#18946) (Thanks @CarloToso!)
  • +
  • Simplification of GetHttpMethod and HttpMethod in WebCmdlets (#18846) (Thanks @CarloToso!)
  • +
  • Fix typo in ModuleCmdletBase.cs (#18933) (Thanks @eltociear!)
  • +
  • Fix regression in RemoveNulls (#18881) (Thanks @iSazonov!)
  • +
  • Replace all NotNull with ArgumentNullException.ThrowIfNull (#18820) (Thanks @CarloToso!)
  • +
  • Cleanup InvokeRestMethodCommand.Common.cs (#18861) (Thanks @CarloToso!)
  • +
+ +
+ +### Tools + +- Add a Mariner install script (#19294) +- Add tool to trigger license information gathering for NuGet modules (#18827) + +### Tests + +- Update and enable the test for the type of `$input` (#18968) (Thanks @MartinGC94!) +- Increase the timeout for creating the `WebListener` (#19268) +- Increase the timeout when waiting for the event log (#19264) +- Add Windows ARM64 CI (#19040) +- Change test so output does not include newline (#19026) +- Allow system lock down test debug hook to work with new WLDP API (#18962) +- Add tests for `Allowinsecureredirect` parameter in Web cmdlets (#18939) (Thanks @CarloToso!) +- Enable `get-help` pattern tests on Unix (#18855) (Thanks @xtqqczze!) +- Create test to check if WebCmdlets decompress brotli-encoded data (#18905) (Thanks @CarloToso!) + +### Build and Packaging Improvements + +
+ + + +

We thank the following contributors!

+

@bergmeister, @xtqqczze

+ +
+ +
    +
  • Restructure the package build to simplify signing and packaging stages (#19321)
  • +
  • Bump Microsoft.CodeAnalysis.CSharp from 4.4.0 to 4.6.0-2.23152.6 (#19306)(#19233)
  • +
  • Test fixes for stabilizing tests (#19068)
  • +
  • Bump Newtonsoft.Json from 13.0.2 to 13.0.3 (#19290)(#19289)
  • +
  • Fix mariner sudo detection (#19304)
  • +
  • Add stage for symbols job in Release build (#18937)
  • +
  • Bump .NET to Preview 2 version (#19305)
  • +
  • Move workflows that create PRs to private repo (#19276)
  • +
  • Use reference assemblies generated by dotnet (#19302)
  • +
  • Update the cgmanifest (#18814)(#19165)(#19296)
  • +
  • Always regenerate files WXS fragment (#19196)
  • +
  • MSI installer: Add checkbox and MSI property DISABLE_TELEMETRY to optionally disable telemetry. (#10725) (Thanks @bergmeister!)
  • +
  • Add -Force to Move-Item to fix the GitHub workflow (#19262)
  • +
  • Update and remove outdated docs to fix the URL link checks (#19261)
  • +
  • Bump Markdig.Signed from 0.30.4 to 0.31.0 (#19232)
  • +
  • Add pattern to replace for reference API generation (#19214)
  • +
  • Split test artifact build into windows and non-windows (#19199)
  • +
  • Set LangVersion compiler option to 11.0 (#18877) (Thanks @xtqqczze!)
  • +
  • Update to .NET 8 preview 1 build (#19194)
  • +
  • Simplify Windows Packaging CI Trigger YAML (#19160)
  • +
  • Bump Microsoft.NET.Test.Sdk from 17.4.0 to 17.5.0 (#18823)(#19191)
  • +
  • Add URL for all distributions (#19159)
  • +
  • Bump Microsoft.Extensions.ObjectPool from 7.0.1 to 7.0.3 (#18925)(#19155)
  • +
  • Add verification of R2R at packaging (#19129)
  • +
  • Allow cross compiling windows (#19119)
  • +
  • Update CodeQL build agent (#19113)
  • +
  • Bump XunitXml.TestLogger from 3.0.70 to 3.0.78 (#19066)
  • +
  • Bump Microsoft.CodeAnalysis.Analyzers from 3.3.3 to 3.3.4 (#18975)
  • +
  • Bump BenchmarkDotNet to 0.13.3 (#18878) (Thanks @xtqqczze!)
  • +
  • Bump Microsoft.PowerShell.Native from 7.4.0-preview.1 to 7.4.0-preview.2 (#18910)
  • +
  • Add checks for Windows 8.1 and Server 2012 in the MSI installer (#18904)
  • +
  • Update build to include WinForms / WPF in all Windows builds (#18859)
  • +
+ +
+ +### Documentation and Help Content + +- Update to the latest NOTICES file (#19169)(#19309)(#19086)(#19077) +- Update supported distros in readme (#18667) (Thanks @techguy16!) +- Remove the 'Code Coverage Status' badge (#19265) +- Pull in changelogs for `v7.2.10` and `v7.3.3` releases (#19219) +- Update tools `metadata` and `README` (#18831)(#19204)(#19014) +- Update a broken link in the `README.md` (#19187) +- Fix typos in comments (#19064) (Thanks @spaette!) +- Add `7.2` and `7.3` changelogs (#19025) +- typos (#19058) (Thanks @spaette!) +- Fix typo in `dotnet-tools/README.md` (#19021) (Thanks @spaette!) +- Fix up all comments to be in the proper order with proper spacing (#18619) +- Changelog for `v7.4.0-preview.1` release (#18835) + +[7.4.0-preview.2]: https://github.com/PowerShell/PowerShell/compare/v7.4.0-preview.1...v7.4.0-preview.2 + +## [7.4.0-preview.1] - 2022-12-20 + +### Engine Updates and Fixes + +- Add Instrumentation to `AmsiUtil` and make the init variable readonly (#18727) +- Fix typo in `OutOfProcTransportManager.cs` (#18766) (Thanks @eltociear!) +- Allow non-default encodings to be used in user's script/code (#18605) +- Add `Dim` and `DimOff` to `$PSStyle` (#18653) +- Change `exec` from alias to function to handle arbitrary arguments (#18567) +- The command prefix should also be in the error color for `NormalView` (#18555) +- Skip cloud files marked as "not on disk" during command discovery (#18152) +- Replace `UTF8Encoding(false)` with `Encoding.Default` (#18356) (Thanks @xtqqczze!) +- Fix `Switch-Process` to set `termios` appropriate for child process (#18467) +- On Unix, only explicitly terminate the native process if not in background (#18215) +- Treat `[NullString]::Value` as the string type when resolving methods (#18080) +- Improve pseudo binding for dynamic parameters (#18030) (Thanks @MartinGC94!) +- Make experimental feature `PSAnsiRenderingFileInfo` stable (#18042) +- Update to use version `2.21.0` of Application Insights. (#17903) +- Do not preserve temporary results when no need to do so (#17856) + +### Performance + +- Remove some static constants from `Utils.Separators` (#18154) (Thanks @iSazonov!) +- Avoid using regular expression when unnecessary in `ScriptWriter` (#18348) +- Use source generator for `PSVersionInfo` to improve startup time (#15603) (Thanks @iSazonov!) +- Skip evaluating suggestions at startup (#18232) +- Avoid using `Regex` when not necessary (#18210) + +### General Cmdlet Updates and Fixes + +- Update to use `ComputeCore.dll` for PowerShell Direct (#18194) +- Replace `ArgumentNullException(nameof())` with `ArgumentNullException.ThrowIfNull()` (#18792)(#18784) (Thanks @CarloToso!) +- Remove `TabExpansion` from remote session configuration (#18795) (Internal 23331) +- WebCmdlets get Retry-After from headers if status code is 429 (#18717) (Thanks @CarloToso!) +- Implement `SupportsShouldProcess` in `Stop-Transcript` (#18731) (Thanks @JohnLBevan!) +- Fix `New-Item -ItemType Hardlink` to resolve target to absolute path and not allow link to itself (#18634) +- Add output types to Format commands (#18746) (Thanks @MartinGC94!) +- Fix the process `CommandLine` on Linux (#18710) (Thanks @jborean93!) +- Fix `SuspiciousContentChecker.Match` to detect a predefined string when the text starts with it (#18693) +- Switch `$PSNativeCommandUseErrorActionPreference` to `$true` when feature is enabled (#18695) +- Fix `Start-Job` to check the existence of working directory using the PowerShell way (#18675) +- Webcmdlets add 308 to redirect codes and small cleanup (#18536) (Thanks @CarloToso!) +- Ensure `HelpInfo.Category` is consistently a string (#18254) +- Remove `gcloud` from the legacy list because it's resolved to a .ps1 script (#18575) +- Add `gcloud` and `sqlcmd` to list to use legacy argument passing (#18559) +- Fix native access violation (#18545) (#18547) (Thanks @chrullrich!) +- Fix issue when completing the first command in a script with an empty array expression (#18355) (Thanks @MartinGC94!) +- Improve type inference of hashtable keys (#17907) (Thanks @MartinGC94!) +- Fix `Switch-Process` to copy the current env to the new process (#18452) +- Fix `Switch-Process` error to include the command that is not found (#18443) +- Update `Out-Printer` to remove all decorating ANSI escape sequences from PowerShell formatting (#18425) +- Web cmdlets set default charset encoding to `UTF8` (#18219) (Thanks @CarloToso!) +- Fix incorrect cmdlet name in the script used by `Restart-Computer` (#18374) (Thanks @urizen-source!) +- Add the function `cd~` (#18308) (Thanks @GigaScratch!) +- Fix type inference error for empty return statements (#18351) (Thanks @MartinGC94!) +- Fix the exception reporting in `ConvertFrom-StringData` (#18336) (Thanks @GigaScratch!) +- Implement `IDisposable` in `NamedPipeClient` (#18341) (Thanks @xtqqczze!) +- Replace command-error suggestion with new implementation based on subsystem plugin (#18252) +- Remove the `ProcessorArchitecture` portion from the full name as it's obsolete (#18320) +- Make the fuzzy searching flexible by passing in the fuzzy matcher (#18270) +- Add `-FuzzyMinimumDistance` parameter to `Get-Command` (#18261) +- Improve startup time by triggering initialization of additional types on background thread (#18195) +- Fix decompression in web cmdlets (#17955) (Thanks @iSazonov!) +- Add `CustomTableHeaderLabel` formatting to differentiate table header labels that are not property names (#17346) +- Remove the extra new line form List formatting (#18185) +- Minor update to the `FileInfo` table formatting on Unix to make it more concise (#18183) +- Fix Parent property on processes with complex name (#17545) (Thanks @jborean93!) +- Make PowerShell class not affiliate with `Runspace` when declaring the `NoRunspaceAffinity` attribute (#18138) +- Complete the progress bar rendering in `Invoke-WebRequest` when downloading is complete or cancelled (#18130) +- Display download progress in human readable format for `Invoke-WebRequest` (#14611) (Thanks @bergmeister!) +- Update `WriteConsole` to not use `stackalloc` for buffer with too large size (#18084) +- Filter out compiler generated types for `Add-Type -PassThru` (#18095) +- Fixing `CA2014` warnings and removing the warning suppression (#17982) (Thanks @creative-cloud!) +- Make experimental feature `PSNativeCommandArgumentPassing` stable (#18044) +- Make experimental feature `PSAMSIMethodInvocationLogging` stable (#18041) +- Handle `PSObject` argument specially in method invocation logging (#18060) +- Fix typos in `EventResource.resx` (#18063) (Thanks @eltociear!) +- Make experimental feature `PSRemotingSSHTransportErrorHandling` stable (#18046) +- Make experimental feature `PSExec` stable (#18045) +- Make experimental feature `PSCleanBlock` stable (#18043) +- Fix error formatting to use color defined in `$PSStyle.Formatting` (#17987) +- Remove unneeded use of `chmod 777` (#17974) +- Support mapping foreground/background `ConsoleColor` values to VT escape sequences (#17938) +- Make `pwsh` server modes implicitly not show banner (#17921) +- Add output type attributes for `Get-WinEvent` (#17948) (Thanks @MartinGC94!) +- Remove 1 second minimum delay in `Invoke-WebRequest` for small files, and prevent file-download-error suppression. (#17896) (Thanks @AAATechGuy!) +- Add completion for values in comparisons when comparing Enums (#17654) (Thanks @MartinGC94!) +- Fix positional argument completion (#17796) (Thanks @MartinGC94!) +- Fix member completion in attribute argument (#17902) (Thanks @MartinGC94!) +- Throw when too many parameter sets are defined (#17881) (Thanks @fflaten!) +- Limit searching of `charset` attribute in `meta` tag for HTML to first 1024 characters in webcmdlets (#17813) +- Fix `Update-Help` failing silently with implicit non-US culture. (#17780) (Thanks @dkaszews!) +- Add the `ValidateNotNullOrWhiteSpace` attribute (#17191) (Thanks @wmentha!) +- Improve enumeration of inferred types in pipeline (#17799) (Thanks @MartinGC94!) + +### Code Cleanup + +
+ + + +

We thank the following contributors!

+

@MartinGC94, @CarloToso, @iSazonov, @xtqqczze, @turbedi, @trossr32, @eltociear, @AtariDreams, @jborean93

+ +
+ +
    +
  • Add TSAUpload for APIScan (#18446)
  • +
  • Use Pattern matching in ast.cs (#18794) (Thanks @MartinGC94!)
  • +
  • Cleanup webrequestpscmdlet.common.cs (#18596) (Thanks @CarloToso!)
  • +
  • Unify CreateFile pinvoke in SMA (#18751) (Thanks @iSazonov!)
  • +
  • Cleanup webresponseobject.common (#18785) (Thanks @CarloToso!)
  • +
  • InvokeRestMethodCommand.Common cleanup and merge partials (#18736) (Thanks @CarloToso!)
  • +
  • Replace GetDirectories in CimDscParser (#14319) (Thanks @xtqqczze!)
  • +
  • WebResponseObject.Common merge partials atomic commits (#18703) (Thanks @CarloToso!)
  • +
  • Enable pending test for Start-Process (#18724) (Thanks @iSazonov!)
  • +
  • Remove one CreateFileW (#18732) (Thanks @iSazonov!)
  • +
  • Replace DllImport with LibraryImport for WNetAddConnection2 (#18721) (Thanks @iSazonov!)
  • +
  • Use File.OpenHandle() instead CreateFileW pinvoke (#18722) (Thanks @iSazonov!)
  • +
  • Replace DllImport with LibraryImport for WNetGetConnection (#18690) (Thanks @iSazonov!)
  • +
  • Replace DllImport with LibraryImport - 1 (#18603) (Thanks @iSazonov!)
  • +
  • Replace DllImport with LibraryImport in SMA 3 (#18564) (Thanks @iSazonov!)
  • +
  • Replace DllImport with LibraryImport in SMA - 7 (#18594) (Thanks @iSazonov!)
  • +
  • Use static DateTime.UnixEpoch and RandomNumberGenerator.Fill() (#18621) (Thanks @turbedi!)
  • +
  • Rewrite Get-FileHash to use static HashData methods (#18471) (Thanks @turbedi!)
  • +
  • Replace DllImport with LibraryImport in SMA 8 (#18599) (Thanks @iSazonov!)
  • +
  • Replace DllImport with LibraryImport in SMA 4 (#18579) (Thanks @iSazonov!)
  • +
  • Remove NativeCultureResolver as dead code (#18582) (Thanks @iSazonov!)
  • +
  • Replace DllImport with LibraryImport in SMA 6 (#18581) (Thanks @iSazonov!)
  • +
  • Replace DllImport with LibraryImport in SMA 2 (#18543) (Thanks @iSazonov!)
  • +
  • Use standard SBCS detection (#18593) (Thanks @iSazonov!)
  • +
  • Remove unused pinvokes in RemoteSessionNamedPipe (#18583) (Thanks @iSazonov!)
  • +
  • Replace DllImport with LibraryImport in SMA 5 (#18580) (Thanks @iSazonov!)
  • +
  • Remove SafeRegistryHandle (#18597) (Thanks @iSazonov!)
  • +
  • Remove ArchitectureSensitiveAttribute from the code base (#18598) (Thanks @iSazonov!)
  • +
  • Build COM adapter only on Windows (#18590)
  • +
  • Include timer instantiation for legacy telemetry in conditional compiler statements in Get-Help (#18475) (Thanks @trossr32!)
  • +
  • Convert DllImport to LibraryImport for recycle bin, clipboard, and computerinfo cmdlets (#18526)
  • +
  • Replace DllImport with LibraryImport in SMA 1 (#18520) (Thanks @iSazonov!)
  • +
  • Replace DllImport with LibraryImport in engine (#18496)
  • +
  • Fix typo in InitialSessionState.cs (#18435) (Thanks @eltociear!)
  • +
  • Remove remaining unused strings from resx files (#18448)
  • +
  • Use new LINQ Order() methods instead of OrderBy(static x => x) (#18395) (Thanks @turbedi!)
  • +
  • Make use of StringSplitOptions.TrimEntries when possible (#18412) (Thanks @turbedi!)
  • +
  • Replace some string.Join(string) calls with string.Join(char) (#18411) (Thanks @turbedi!)
  • +
  • Remove unused strings from FileSystem and Registry providers (#18403)
  • +
  • Use generic GetValues<T>, GetNames<T> enum methods (#18391) (Thanks @xtqqczze!)
  • +
  • Remove unused resource strings from SessionStateStrings (#18394)
  • +
  • Remove unused resource strings in System.Management.Automation (#18388)
  • +
  • Use Enum.HasFlags part 1 (#18386) (Thanks @xtqqczze!)
  • +
  • Remove unused strings from parser (#18383)
  • +
  • Remove unused strings from Utility module (#18370)
  • +
  • Remove unused console strings (#18369)
  • +
  • Remove unused strings from ConsoleInfoErrorStrings.resx (#18367)
  • +
  • Code cleanup in ContentHelper.Common.cs (#18288) (Thanks @CarloToso!)
  • +
  • Remove FusionAssemblyIdentity and GlobalAssemblyCache as they are not used (#18334) (Thanks @iSazonov!)
  • +
  • Remove some static initializations in StringManipulationHelper (#18243) (Thanks @xtqqczze!)
  • +
  • Use MemoryExtensions.IndexOfAny in PSv2CompletionCompleter (#18245) (Thanks @xtqqczze!)
  • +
  • Use MemoryExtensions.IndexOfAny in WildcardPattern (#18242) (Thanks @xtqqczze!)
  • +
  • Small cleanup of the stub code (#18301) (Thanks @CarloToso!)
  • +
  • Fix typo in RemoteRunspacePoolInternal.cs (#18263) (Thanks @eltociear!)
  • +
  • Some more code cleanup related to the use of PSVersionInfo (#18231)
  • +
  • Use MemoryExtensions.IndexOfAny in SessionStateInternal (#18244) (Thanks @xtqqczze!)
  • +
  • Use overload APIs that take char instead of string when it's possible (#18179) (Thanks @iSazonov!)
  • +
  • Replace UTF8Encoding(false) with Encoding.Default (#18144) (Thanks @xtqqczze!)
  • +
  • Remove unused variables (#18058) (Thanks @AtariDreams!)
  • +
  • Fix typo in PowerShell.Core.Instrumentation.man (#17963) (Thanks @eltociear!)
  • +
  • Migrate WinTrust functions to a common location (#17598) (Thanks @jborean93!)
  • +
+ +
+ +### Tools + +- Add a function to get the PR Back-port report (#18299) +- Add a workaround in automatic rebase workflow to continue on error (#18176) +- Update list of PowerShell team members in release tools (#17909) +- Don't block if we fail to create the comment (#17869) + +### Tests + +- Add `testexe.exe -echocmdline` to output raw command-line received by the process on Windows (#18591) +- Mark charset test as pending (#18511) +- Skip output rendering tests on Windows Server 2012 R2 (#18382) +- Increase timeout to make subsystem tests more reliable (#18380) +- Add missing -Tag 'CI' to describe blocks. (#18316) +- Use short path instead of multiple quotes in `Get-Item` test relying on node (#18250) +- Replace the CIM class used for `-Amended` parameter test (#17884) (Thanks @sethvs!) +- Stop ongoing progress-bar in `Write-Progress` test (#17880) (Thanks @fflaten!) + +### Build and Packaging Improvements + +
+ + + +

We thank the following contributors!

+ +
+ +
    +
  • Fix reference assembly generation logic for Microsoft.PowerShell.Commands.Utility (#18818)
  • +
  • Update the cgmanifest (#18676)(#18521)(#18415)(#18408)(#18197)(#18111)(#18051)(#17913)(#17867)(#17934)(#18088)
  • +
  • Bump Microsoft.PowerShell.Native to the latest preview version v7.4.0-preview.1 (#18805)
  • +
  • Remove unnecessary reference to System.Runtime.CompilerServices.Unsafe (#18806)
  • +
  • Update the release tag in metadata.json for next preview (#18799)
  • +
  • Bump Microsoft.CodeAnalysis.NetAnalyzers (#18750)
  • +
  • Bump .NET SDK to version 7.0.101 (#18786)
  • +
  • Bump cirrus-actions/rebase from 1.7 to 1.8 (#18788)
  • +
  • Bump decode-uri-component from 0.2.0 to 0.2.2 (#18712)
  • +
  • Bump Microsoft.CodeAnalysis.CSharp from 4.4.0-4.final to 4.4.0 (#18562)
  • +
  • Bump Newtonsoft.Json from 13.0.1 to 13.0.2 (#18657)
  • +
  • Apply expected file permissions to Linux files after Authenticode signing (#18643)
  • +
  • Remove extra quotes after agent moves to pwsh 7.3 (#18577)
  • +
  • Don't install based on build-id for RPM (#18560)
  • +
  • Bump Microsoft.NET.Test.Sdk from 17.3.2 to 17.4.0 (#18487)
  • +
  • Bump minimatch from 3.0.4 to 3.1.2 (#18514)
  • +
  • Avoid depending on the pre-generated experimental feature list in private and CI builds (#18484)
  • +
  • Update release-MsixBundle.yml to add retries (#18465)
  • +
  • Bump System.Data.SqlClient from 4.8.4 to 4.8.5 in /src/Microsoft.PowerShell.SDK (#18515)
  • +
  • Bump to use internal .NET 7 GA build (#18508)
  • +
  • Insert the pre-release nuget feed before building test artifacts (#18507)
  • +
  • Add test for framework dependent package in release pipeline (#18506) (Internal 23139)
  • +
  • Update to azCopy 10 (#18509)
  • +
  • Fix issues with uploading changelog to GitHub release draft (#18504)
  • +
  • Bump Microsoft.CodeAnalysis.NetAnalyzers (#18442)
  • +
  • Add authenticode signing for assemblies on linux builds (#18440)
  • +
  • Do not remove penimc_cor3.dll from build (#18438)
  • +
  • Bump Microsoft.PowerShell.Native from 7.3.0-rc.1 to 7.3.0 (#18405)
  • +
  • Allow two-digit revisions in vPack package validation pattern (#18392)
  • +
  • Bump Microsoft.CodeAnalysis.NetAnalyzers (#18363)
  • +
  • Bump to .NET 7 RC2 official version (#18328)
  • +
  • Bump to .NET 7 to version 7.0.100-rc.2.22477.20 (#18286)
  • +
  • Replace win7 runtime with win8 and remove APISets (#18304)
  • +
  • Bump Microsoft.CodeAnalysis.NetAnalyzers (#18312)
  • +
  • Recurse the file listing. (#18277)
  • +
  • Create tasks to collect and publish hashes for build files. (#18276)
  • +
  • Bump Microsoft.CodeAnalysis.NetAnalyzers (#18262)
  • +
  • Remove ETW trace collection and uploading for CLR CAP (#18253)
  • +
  • Do not cleanup pwsh.deps.json for framework dependent packages (#18226)
  • +
  • Add branch counter to APIScan build (#18214)
  • +
  • Remove unnecessary native dependencies from the package (#18213)
  • +
  • Remove XML files for min-size package (#18189)
  • +
  • Bump Microsoft.CodeAnalysis.NetAnalyzers (#18216)
  • +
  • Bump Microsoft.PowerShell.Native from 7.3.0-preview.1 to 7.3.0-rc.1 (#18217)
  • +
  • Bump Microsoft.CodeAnalysis.NetAnalyzers (#18201)
  • +
  • Move ApiScan to compliance build (#18191)
  • +
  • Fix the verbose message when using dotnet-install.sh (#18184)
  • +
  • Bump Microsoft.NET.Test.Sdk from 17.3.1 to 17.3.2 (#18163)
  • +
  • Bump Microsoft.CodeAnalysis.NetAnalyzers (#18164)
  • +
  • Make the link to minimal package blob public during release (#18158)
  • +
  • Bump Microsoft.CodeAnalysis.NetAnalyzers (#18147)
  • +
  • Update MSI exit message (#18137)
  • +
  • Bump Microsoft.CodeAnalysis.CSharp from 4.4.0-1.final to 4.4.0-2.final (#18132)
  • +
  • Re-enable building with Ready-to-Run (#18105)
  • +
  • Update DotnetRuntimeMetadata.json for .NET 7 RC1 build (#18091)
  • +
  • Bump Microsoft.CodeAnalysis.NetAnalyzers (#18096)
  • +
  • Add schema for cgmanifest.json (#18036)
  • +
  • Bump Microsoft.CodeAnalysis.CSharp from 4.3.0-3.final to 4.3.0 (#18012)
  • +
  • Add XML reference documents to NuPkg files for SDK (#17997)
  • +
  • Bump Microsoft.NET.Test.Sdk from 17.3.0 to 17.3.1 (#18000)
  • +
  • Bump Microsoft.CodeAnalysis.NetAnalyzers (#17988)
  • +
  • Bump Microsoft.CodeAnalysis.NetAnalyzers (#17983)
  • +
  • Bump Microsoft.CodeAnalysis.NetAnalyzers (#17945)
  • +
  • Make sure Security.types.ps1xml gets signed in release build (#17916)
  • +
  • Make Register Microsoft Update timeout (#17910)
  • +
  • Merge changes from v7.0.12 v7.2.6 and v7.3.0-preview.7
  • +
  • Bump Microsoft.NET.Test.Sdk from 17.2.0 to 17.3.0 (#17871)
  • +
+ +
+ +### Documentation and Help Content + +- Update readme and metadata for releases (#18780)(#18493)(#18393)(#18332)(#18128)(#17870) +- Remove 'please' and 'Core' from README.md per MS style guide (#18578) (Thanks @Rick-Anderson!) +- Change unsupported XML documentation tag (#18608) +- Change public API mention of `monad` to PowerShell (#18491) +- Update security reporting policy to recommend security portal for more streamlined reporting (#18437) +- Changelog for v7.3.0 (#18505) (Internal 23161) +- Replace `msh` in public API comment based documentation with PowerShell equivalent (#18483) +- Add missing XML doc elements for methods in `RunspaceFactory` (#18450) +- Changelog for `v7.3.0-rc.1` (#18400) +- Update changelogs for `v7.2.7` and `v7.0.13` (#18342) +- Update the changelog for v7.3.0-preview.8 (#18136) +- Add the `ConfigurationFile` option to the PowerShell help content (#18093) +- Update help content about the PowerShell flag `-NonInteractive` (#17952) + +[7.4.0-preview.1]: https://github.com/PowerShell/PowerShell/compare/v7.3.0-preview.8...v7.4.0-preview.1 diff --git a/CHANGELOG/7.5.md b/CHANGELOG/7.5.md new file mode 100644 index 00000000000..80b3ee0a8aa --- /dev/null +++ b/CHANGELOG/7.5.md @@ -0,0 +1,817 @@ +# 7.5 Changelog + +## [7.5.4] + +### Build and Packaging Improvements + +
+ + + +

Update to .NET SDK 9.0.306

+ +
+ +
    +
  • [release/v7.5] Update Ev2 Shell Extension Image to AzureLinux 3 for PMC Release (#26032)
  • +
  • [release/v7.5] Fix variable reference for release environment in pipeline (#26013)
  • +
  • [release/v7.5] Add v7.5.3 Changelog (#26015)
  • +
  • [release/v7.5] Add LinuxHost Network configuration to PowerShell Packages pipeline (#26002)
  • +
  • Backport Release Pipeline Changes (Internal 37168)
  • +
  • [release/v7.5] Update branch for release (#26195)
  • +
  • [release/v7.5] Mark the 3 consistently failing tests as pending to unblock PRs (#26196)
  • +
  • [release/v7.5] add CodeQL suppresion for NativeCommandProcessor (#26173)
  • +
  • [release/v7.5] add CodeQL suppressions for UpdatableHelp and NativeCommandProcessor methods (#26171)
  • +
  • [release/v7.5] Remove UseDotnet task and use the dotnet-install script (#26169)
  • +
  • [release/v7.5] Automate Store Publishing (#26164)
  • +
  • [release/v7.5] Ensure that socket timeouts are set only during the token validation (#26079)
  • +
  • [release/v7.5] Suppress false positive PSScriptAnalyzer warnings in tests and build scripts (#26059)
  • +
+ +
+ +[7.5.4]: https://github.com/PowerShell/PowerShell/compare/v7.5.3...v7.5.4 + + +## [7.5.3] + +### General Cmdlet Updates and Fixes + +- Fix `Out-GridView` by replacing the use of obsolete `BinaryFormatter` with custom implementation. (#25559) +- Remove `OnDeserialized` and `Serializable` attributes from `Microsoft.Management.UI.Internal` project (#25831) +- Make the interface `IDeepCloneable` internal (#25830) + +### Tools + +- Add CodeQL suppressions (#25972) + +### Tests + +- Fix updatable help test for new content (#25944) + +### Build and Packaging Improvements + +
+ + + +

Update to .NET SDK 9.0.304

+ +
+ +
    +
  • Make logical template name consistent between pipelines (#25991)
  • +
  • Update container images to use mcr.microsoft.com for Linux and Azure Linux (#25986)
  • +
  • Add build to vPack Pipeline (#25975)
  • +
  • Remove AsyncSDL from Pipelines Toggle Official/NonOfficial Runs (#25964)
  • +
  • Update branch for release (#25942)
  • +
+ +
+ +### Documentation and Help Content + +- Fix typo in CHANGELOG for script filename suggestion (#25963) + +[7.5.3]: https://github.com/PowerShell/PowerShell/compare/v7.5.2...v7.5.3 + +## [7.5.2] - 2025-06-24 + +### Engine Updates and Fixes + +- Move .NET method invocation logging to after the needed type conversion is done for method arguments (#25357) + +### General Cmdlet Updates and Fixes + +- Set standard handles explicitly when starting a process with `-NoNewWindow` (#25324) +- Make inherited protected internal instance members accessible in class scope. (#25547) (Thanks @mawosoft!) +- Remove the old fuzzy suggestion and fix the local script filename suggestion (#25330) +- Fix `PSMethodInvocationConstraints.GetHashCode` method (#25306) (Thanks @crazyjncsu!) + +### Build and Packaging Improvements + +
+ + + +

Update to .NET SDK 9.0.301

+ +
+ +
    +
  • Correct Capitalization Referencing Templates (#25673)
  • +
  • Publish .msixbundle package as a VPack (#25621)
  • +
  • Update ThirdPartyNotices for v7.5.2 (#25658)
  • +
  • Manually update SqlClient in TestService
  • +
  • Update cgmanifest
  • +
  • Update package references
  • +
  • Update .NET SDK to latest version
  • +
  • Change linux packaging tests to ubuntu latest (#25639)
  • +
  • Fix MSIX artifact upload, vPack template, changelog hashes, git tag command (#25633)
  • +
  • Move MSIXBundle to Packages and Release to GitHub (#25517)
  • +
  • Use new variables template for vPack (#25435)
  • +
+ +
+ +[7.5.2]: https://github.com/PowerShell/PowerShell/compare/v7.5.1...v7.5.2 + +## [7.5.1] + +### Engine Updates and Fixes + +- Fallback to AppLocker after `WldpCanExecuteFile` (#25305) + +### Code Cleanup + +
+ +
    +
  • Cleanup old release pipelines (#25236)
  • +
+ +
+ +### Tools + +- Do not run labels workflow in the internal repository (#25343) +- Update `CODEOWNERS` (#25321) +- Check GitHub token availability for `Get-Changelog` (#25328) +- Update PowerShell team members in `releaseTools.psm1` (#25302) + +### Build and Packaging Improvements + +
+ + + +

Update to .NET SDK 9.0.203

+ +
+ +
    +
  • Finish 7.5.0 release (#24855)
  • +
  • Add CodeQL suppressions for PowerShell intended behavior (#25375)
  • +
  • Update to .NET SDK 9.0.203 (#25373)
  • +
  • Switch to ubuntu-lastest for CI (#25374)
  • +
  • Add default .NET install path for SDK validation (#25338)
  • +
  • Combine GitHub and Nuget Release Stage (#25371)
  • +
  • Add Windows Store Signing to MSIX bundle (#25370)
  • +
  • Update test result processing to use NUnitXml format and enhance logging for better clarity (#25344)
  • +
  • Fix MSIX stage in release pipeline (#25345)
  • +
  • Make GitHub Workflows work in the internal mirror (#25342)
  • +
  • Update security extensions (#25322)
  • +
  • Disable SBOM generation on set variables job in release build (#25340)
  • +
  • Update GitHub Actions to work in private GitHub repo (#25332)
  • +
  • Revert "Cleanup old release pipelines (#25201)" (#25335)
  • +
  • Remove call to NuGet (#25334)
  • +
  • Simplify PR Template (#25333)
  • +
  • Update package pipeline windows image version (#25331)
  • +
  • Skip additional packages when generating component manifest (#25329)
  • +
  • Only build Linux for packaging changes (#25326)
  • +
  • Make Component Manifest Updater use neutral target in addition to RID target (#25325)
  • +
  • Remove Az module installs and AzureRM uninstalls in pipeline (#25327)
  • +
  • Make sure the vPack pipeline does not produce an empty package (#25320)
  • +
  • Add *.props and sort path filters for windows CI (#25316)
  • +
  • Fix V-Pack download package name (#25314)
  • +
  • Update path filters for Windows CI (#25312)
  • +
  • Give the pipeline runs meaningful names (#25309)
  • +
  • Migrate MacOS Signing to OneBranch (#25304)
  • +
  • Add UseDotnet task for installing dotnet (#25281)
  • +
  • Remove obsolete template from Windows Packaging CI (#25237)
  • +
  • Add setup dotnet action to the build composite action (#25235)
  • +
  • Add GitHub Actions workflow to verify PR labels (#25159)
  • +
  • Update branch for release - Transitive - true - minor (#24994)
  • +
  • Fix GitHub Action filter overmatching (#24958)
  • +
  • Fix release branch filters (#24959)
  • +
  • Convert powershell/PowerShell-CI-macos to GitHub Actions (#24954)
  • +
  • Convert powershell/PowerShell-CI-linux to GitHub Actions (#24946)
  • +
  • Convert powershell/PowerShell-Windows-CI to GitHub Actions (#24931)
  • +
  • PMC parse state correctly from update command's response (#24859)
  • +
  • Add EV2 support for publishing PowerShell packages to PMC (#24856)
  • +
+ +
+ +[7.5.1]: https://github.com/PowerShell/PowerShell/compare/v7.5.0...v7.5.1 + +## [7.5.0] + +### Build and Packaging Improvements + +
+ + + +

Update .NET SDK to 9.0.102

+ +
+ +
    +
  • Add tool package download in publish nuget stage (#24790) (#24792)
  • +
  • Fix Changelog content grab during GitHub Release (#24788) (#24791)
  • +
  • Mark build as latest stable (#24789)
  • +
  • [release/v7.5] Update branch for release - Transitive - true - minor (#24786)
  • +
  • Update Microsoft.PowerShell.PSResourceGet to 1.1.0 (#24767) (#24785)
  • +
  • Make the AssemblyVersion not change for servicing releases (#24667) (#24783)
  • +
  • Deploy Box Update (#24632) (#24779)
  • +
  • Update machine pool for copy blob and upload buildinfo stage (#24587) (#24776)
  • +
  • Update nuget publish to use Deploy Box (#24596) (#24597)
  • +
  • Added Deploy Box Product Pathway to GitHub Release and NuGet Release Pipelines (#24583) (#24595)
  • +
+ +
+ +### Documentation and Help Content + +- Update `HelpInfoUri` for 7.5 (#24610) (#24777) + +[7.5.0]: https://github.com/PowerShell/PowerShell/compare/v7.5.0-rc.1...v7.5.0 + +## [7.5.0-rc.1] - 2024-11-14 + +**NOTE:** Due to technical issues, release of packages to packages.microsoft.com ~and release to NuGet.org~ is delayed. + +### Build and Packaging Improvements + +
+ + + +

Bump to .NET 9.0.100

+ +
+ +
    +
  • Update ThirdPartyNotices file (#24582) (#24536)
  • +
  • Bump to .NET 9.0.100 (#24576) (#24535)
  • +
  • Add a way to use only NuGet feed sources (#24528) (#24530)
  • +
  • Update PSResourceGet to v1.1.0-RC2 (#24512) (#24525)
  • +
  • Add PMC mapping for debian 12 (bookworm) (#24413) (#24518)
  • +
  • Bump .NET to 9.0.100-rc.2.24474.11 (#24509) (#24522)
  • +
  • Keep the roff file when gzipping it. (#24450) (#24520)
  • +
  • Checkin generated manpage (#24423) (#24519)
  • +
  • Update PSReadLine to 2.3.6 (#24380) (#24517)
  • +
  • Download package from package build for generating vpack (#24481) (#24521)
  • +
  • Delete the msix blob if it's already there (#24353) (#24516)
  • +
  • Add CodeQL scanning to APIScan build (#24303) (#24515)
  • +
  • Update vpack pipeline (#24281) (#24514)
  • +
  • Fix seed max value for Container Linux CI (#24510) (#24511)
  • +
  • Bring preview.5 release fixes to release/v7.5 (#24379) (#24368)
  • +
  • Add BaseUrl to buildinfo json file (#24376) (#24377)
  • +
+ +
+ +[7.5.0-rc.1]: https://github.com/PowerShell/PowerShell/compare/v7.5.0-preview.5...v7.5.0-rc.1 + +## [7.5.0-preview.5] - 2024-10-01 + +### Breaking Changes + +- Treat large `Enum` values as numbers in `ConvertTo-Json` (#20999) (#24304) + +### Engine Updates and Fixes + +- Fix how processor architecture is validated in `Import-Module` (#24265) (#24317) + +### Experimental Features + +### General Cmdlet Updates and Fixes + +- Add `-Force` parameter to `Resolve-Path` and `Convert-Path` cmdlets to support wildcard hidden files (#20981) (#24344) +- Add telemetry to track the use of features (#24247) (#24331) +- Treat large `Enum` values as numbers in `ConvertTo-Json` (#20999) (#24304) +- Make features `PSCommandNotFoundSuggestion`, `PSCommandWithArgs`, and `PSModuleAutoLoadSkipOfflineFiles` stable (#24246) (#24310) +- Handle global tool when prepending `$PSHome` to `PATH` (#24228) (#24307) + +### Tests + +- Fix cleanup in `PSResourceGet` test (#24339) (#24345) + +### Build and Packaging Improvements + +
+ + + +

Bump .NET SDK to 9.0.100-rc.1.24452.12

+ +
+ +
    +
  • Fixed Test Scenario for Compress-PSResource (Internal 32696)
  • +
  • Add back local NuGet source for test packages (Internal 32693)
  • +
  • Fix typo in release-MakeBlobPublic.yml (Internal 32689)
  • +
  • Copy to static site instead of making blob public (#24269) (#24343)
  • +
  • Update Microsoft.PowerShell.PSResourceGet to 1.1.0-preview2 (#24300) (#24337)
  • +
  • Remove the MD5 branch in the strong name signing token calculation (#24288) (#24321)
  • +
  • Update experimental-feature json files (#24271) (#24319)
  • +
  • Add updated libicu dependency for Debian packages (#24301) (#24324)
  • +
  • Add mapping to AzureLinux repo (#24290) (#24322)
  • +
  • Update and add new NuGet package sources for different environments. (#24264) (#24316)
  • +
  • Bump .NET 9 to 9.0.100-rc.1.24452.12 (#24273) (#24320)
  • +
  • Make some release tests run in a hosted pools (#24270) (#24318)
  • +
  • Do not build the exe for Global tool shim project (#24263) (#24315)
  • +
  • Delete assets/AppImageThirdPartyNotices.txt (#24256) (#24313)
  • +
  • Create new pipeline for compliance (#24252) (#24312)
  • +
  • Add specific path for issues in tsaconfig (#24244) (#24309)
  • +
  • Use Managed Identity for APIScan authentication (#24243) (#24308)
  • +
  • Add Windows signing for pwsh.exe (#24219) (#24306)
  • +
  • Check Create and Submit in vPack build by default (#24181) (#24305)
  • +
+ +
+ +### Documentation and Help Content + +- Delete demos directory (#24258) (#24314) + +[7.5.0-preview.5]: https://github.com/PowerShell/PowerShell/compare/v7.5.0-preview.4...v7.5.0-preview.5 + +## [7.5.0-preview.4] - 2024-08-28 + +### Engine Updates and Fixes + +- RecommendedAction: Explicitly start and stop ANSI Error Color (#24065) (Thanks @JustinGrote!) +- Improve .NET overload definition of generic methods (#21326) (Thanks @jborean93!) +- Optimize the `+=` operation for a collection when it's an object array (#23901) (Thanks @jborean93!) +- Allow redirecting to a variable as experimental feature `PSRedirectToVariable` (#20381) + +### General Cmdlet Updates and Fixes + +- Change type of `LineNumber` to `ulong` in `Select-String` (#24075) (Thanks @Snowman-25!) +- Fix `Invoke-RestMethod` to allow `-PassThru` and `-Outfile` work together (#24086) (Thanks @jshigetomi!) +- Fix Hyper-V Remoting when the module is imported via implicit remoting (#24032) (Thanks @jborean93!) +- Add `ConvertTo-CliXml` and `ConvertFrom-CliXml` cmdlets (#21063) (Thanks @ArmaanMcleod!) +- Add `OutFile` property in `WebResponseObject` (#24047) (Thanks @jshigetomi!) +- Show filename in `Invoke-WebRequest -OutFile -Verbose` (#24041) (Thanks @jshigetomi!) +- `Set-Acl`: Do not fail on untranslatable SID (#21096) (Thanks @jborean93!) +- Fix the extent of the parser error when a number constant is invalid (#24024) +- Fix `Move-Item` to throw error when moving into itself (#24004) +- Fix up .NET method invocation with `Optional` argument (#21387) (Thanks @jborean93!) +- Fix progress calculation on `Remove-Item` (#23869) (Thanks @jborean93!) +- Fix WebCmdlets when `-Body` is specified but `ContentType` is not (#23952) (Thanks @CarloToso!) +- Enable `-NoRestart` to work with `Register-PSSessionConfiguration` (#23891) +- Add `IgnoreComments` and `AllowTrailingCommas` options to `Test-Json` cmdlet (#23817) (Thanks @ArmaanMcleod!) +- Get-Help may report parameters with `ValueFromRemainingArguments` attribute as pipeline-able (#23871) + +### Code Cleanup + +
+ + + +

We thank the following contributors!

+

@xtqqczze, @eltociear

+ +
+ +
    +
  • Minor cleanup on local variable names within a method (#24105)
  • +
  • Remove explicit IDE1005 suppressions (#21217) (Thanks @xtqqczze!)
  • +
  • Fix a typo in WebRequestSession.cs (#23963) (Thanks @eltociear!)
  • +
+ +
+ +### Tools + +- devcontainers: mount workspace in /PowerShell (#23857) (Thanks @rzippo!) + +### Tests + +- Add debugging to the MTU size test (#21463) + +### Build and Packaging Improvements + +
+ + + +

We thank the following contributors!

+

@bosesubham2011

+ +
+ +
    +
  • Update third party notices (Internal 32128)
  • +
  • Update cgmanifest (#24163)
  • +
  • Fixes to Azure Public feed usage (#24149)
  • +
  • Add support for back porting PRs from GitHub or the Private Azure Repos (#20670)
  • +
  • Move to 9.0.0-preview.6.24327.7 (#24133)
  • +
  • update path (#24134)
  • +
  • Update to the latest NOTICES file (#24131)
  • +
  • Fix semver issue with updating cgmanifest (#24132)
  • +
  • Add ability to capture MSBuild Binary logs when restore fails (#24128)
  • +
  • add ability to skip windows stage (#24116)
  • +
  • chore: Refactor Nuget package source creation to use New-NugetPackageSource function (#24104)
  • +
  • Make Microsoft feeds the default (#24098)
  • +
  • Cleanup unused csproj (#23951)
  • +
  • Add script to update SDK version during release (#24034)
  • +
  • Enumerate over all signed zip packages (#24063)
  • +
  • Update metadata.json for PowerShell July releases (#24082)
  • +
  • Add macos signing for package files (#24015)
  • +
  • Update install-powershell.sh to support azure-linux (#23955) (Thanks @bosesubham2011!)
  • +
  • Skip build steps that do not have exe packages (#23945)
  • +
  • Update metadata.json for PowerShell June releases (#23973)
  • +
  • Create powershell.config.json for PowerShell.Windows.x64 global tool (#23941)
  • +
  • Fix error in the vPack release, debug script that blocked release (#23904)
  • +
  • Add vPack release (#23898)
  • +
  • Fix exe signing with third party signing for WiX engine (#23878)
  • +
  • Update wix installation in CI (#23870)
  • +
  • Add checkout to fix TSA config paths (#23865)
  • +
  • Merge the v7.5.0-preview.3 release branch to GitHub master branch
  • +
  • Update metadata.json for the v7.5.0-preview.3 release (#23862)
  • +
  • Bump PSResourceGet to 1.1.0-preview1 (#24129)
  • +
  • Bump github/codeql-action from 3.25.8 to 3.26.0 (#23953) (#23999) (#24053) (#24069) (#24095) (#24118)
  • +
  • Bump actions/upload-artifact from 4.3.3 to 4.3.6 (#24019) (#24113) (#24119)
  • +
  • Bump agrc/create-reminder-action from 1.1.13 to 1.1.15 (#24029) (#24043)
  • +
  • Bump agrc/reminder-action from 1.0.12 to 1.0.14 (#24028) (#24042)
  • +
  • Bump super-linter/super-linter from 5.7.2 to 6.8.0 (#23809) (#23856) (#23894) (#24030) (#24103)
  • +
  • Bump ossf/scorecard-action from 2.3.1 to 2.4.0 (#23802) (#24096)
  • +
  • Bump actions/dependency-review-action from 4.3.2 to 4.3.4 (#23897) (#24046)
  • +
  • Bump actions/checkout from 4.1.5 to 4.1.7 (#23813) (#23947)
  • +
  • Bump github/codeql-action from 3.25.4 to 3.25.8 (#23801) (#23893)
  • +
+ +
+ +### Documentation and Help Content + +- Update docs sample nuget.config (#24109) +- Update Code of Conduct and Security Policy (#23811) +- Update working-group-definitions.md for the Security WG (#23884) +- Fix up broken links in Markdown files (#23863) +- Update Engine Working Group Members (#23803) (Thanks @kilasuit!) +- Remove outdated and contradictory information from `README` (#23812) + +[7.5.0-preview.4]: https://github.com/PowerShell/PowerShell/compare/v7.5.0-preview.3...v7.5.0-preview.4 + +## [7.5.0-preview.3] - 2024-05-16 + +### Breaking Changes + +- Remember installation options and used them to initialize options for the next installation (#20420) (Thanks @reduckted!) +- `ConvertTo-Json`: Serialize `BigInteger` as a number (#21000) (Thanks @jborean93!) + +### Engine Updates and Fixes + +- Fix generating `OutputType` when running in Constrained Language Mode (#21605) +- Revert the PR #17856 (Do not preserve temporary results when no need to do so) (#21368) +- Make sure the assembly/library resolvers are registered at early stage (#21361) +- Fix PowerShell class to support deriving from an abstract class with abstract properties (#21331) +- Fix error formatting for pipeline enumeration exceptions (#20211) + +### General Cmdlet Updates and Fixes + +- Added progress bar for `Remove-Item` cmdlet (#20778) (Thanks @ArmaanMcleod!) +- Expand `~` to `$home` on Windows with tab completion (#21529) +- Separate DSC configuration parser check for ARM processor (#21395) (Thanks @dkontyko!) +- Fix `[semver]` type to pass `semver.org` tests (#21401) +- Don't complete when declaring parameter name and class member (#21182) (Thanks @MartinGC94!) +- Add `RecommendedAction` to `ConciseView` of the error reporting (#20826) (Thanks @JustinGrote!) +- Fix the error when using `Start-Process -Credential` without the admin privilege (#21393) (Thanks @jborean93!) +- Fix `Test-Path -IsValid` to check for invalid path and filename characters (#21358) +- Fix build failure due to missing reference in `GlobalToolShim.cs` (#21388) +- Fix argument passing in `GlobalToolShim` (#21333) (Thanks @ForNeVeR!) +- Make sure both stdout and stderr can be redirected from a native executable (#20997) +- Handle the case that `Runspace.DefaultRunspace == null` when logging for WDAC Audit (#21344) +- Fix a typo in `releaseTools.psm1` (#21306) (Thanks @eltociear!) +- `Get-Process`: Remove admin requirement for `-IncludeUserName` (#21302) (Thanks @jborean93!) +- Fall back to type inference when hashtable key-value cannot be retrieved from safe expression (#21184) (Thanks @MartinGC94!) +- Fix the regression when doing type inference for `$_` (#21223) (Thanks @MartinGC94!) +- Revert "Adjust PUT method behavior to POST one for default content type in WebCmdlets" (#21049) +- Fix a regression in `Format-Table` when header label is empty (#21156) + +### Code Cleanup + +
+ + + +

We thank the following contributors!

+

@xtqqczze

+ +
+ +
    +
  • Enable CA1868: Unnecessary call to 'Contains' for sets (#21165) (Thanks @xtqqczze!)
  • +
  • Remove JetBrains.Annotations attributes (#21246) (Thanks @xtqqczze!)
  • +
+ +
+ +### Tests + +- Update `metadata.json` and `README.md` (#21454) +- Skip test on Windows Server 2012 R2 for `no-nl` (#21265) + +### Build and Packaging Improvements + +
+ + + +

Bump to .NET 9.0.0-preview.3

+

We thank the following contributors!

+

@alerickson, @tgauth, @step-security-bot, @xtqqczze

+ +
+ +
    +
  • Fix PMC publish and the file path for msixbundle
  • +
  • Fix release version and stage issues in build and packaging
  • +
  • Add release tag if the environment variable is set
  • +
  • Update installation on Wix module (#23808)
  • +
  • Updates to package and release pipelines (#23800)
  • +
  • Update PSResourceGet to 1.0.5 (#23796)
  • +
  • Bump actions/upload-artifact from 4.3.2 to 4.3.3 (#21520)
  • +
  • Bump actions/dependency-review-action from 4.2.5 to 4.3.2 (#21560)
  • +
  • Bump actions/checkout from 4.1.2 to 4.1.5 (#21613)
  • +
  • Bump github/codeql-action from 3.25.1 to 3.25.4 (#22071)
  • +
  • Use feed with Microsoft Wix toolset (#21651) (Thanks @tgauth!)
  • +
  • Bump to .NET 9 preview 3 (#21782)
  • +
  • Use PSScriptRoot to find path to Wix module (#21611)
  • +
  • Create the Windows.x64 global tool with shim for signing (#21559)
  • +
  • Update Wix package install (#21537) (Thanks @tgauth!)
  • +
  • Add branch counter variables for daily package builds (#21523)
  • +
  • Use correct signing certificates for RPM and DEBs (#21522)
  • +
  • Revert to version available on Nuget for Microsoft.CodeAnalysis.Analyzers (#21515)
  • +
  • Official PowerShell Package pipeline (#21504)
  • +
  • Add a PAT for fetching PMC cli (#21503)
  • +
  • Bump ossf/scorecard-action from 2.0.6 to 2.3.1 (#21485)
  • +
  • Apply security best practices (#21480) (Thanks @step-security-bot!)
  • +
  • Bump Microsoft.CodeAnalysis.Analyzers (#21449)
  • +
  • Fix package build to not check some files for a signature. (#21458)
  • +
  • Update PSResourceGet version from 1.0.2 to 1.0.4.1 (#21439) (Thanks @alerickson!)
  • +
  • Verify environment variable for OneBranch before we try to copy (#21441)
  • +
  • Add back two transitive dependency packages (#21415)
  • +
  • Multiple fixes in official build pipeline (#21408)
  • +
  • Update PSReadLine to v2.3.5 (#21414)
  • +
  • PowerShell co-ordinated build OneBranch pipeline (#21364)
  • +
  • Add file description to pwsh.exe (#21352)
  • +
  • Suppress MacOS package manager output (#21244) (Thanks @xtqqczze!)
  • +
  • Update metadata.json and README.md (#21264)
  • +
+ +
+ +### Documentation and Help Content + +- Update the doc about how to build PowerShell (#21334) (Thanks @ForNeVeR!) +- Update the member lists for the Engine and Interactive-UX working groups (#20991) (Thanks @kilasuit!) +- Update CHANGELOG for `v7.2.19`, `v7.3.12` and `v7.4.2` (#21462) +- Fix grammar in `FAQ.md` (#21468) (Thanks @CodingGod987!) +- Fix typo in `SessionStateCmdletAPIs.cs` (#21413) (Thanks @eltociear!) +- Fix typo in a test (#21337) (Thanks @testwill!) +- Fix typo in `ast.cs` (#21350) (Thanks @eltociear!) +- Adding Working Group membership template (#21153) + +[7.5.0-preview.3]: https://github.com/PowerShell/PowerShell/compare/v7.5.0-preview.2...v7.5.0-preview.3 + +## [7.5.0-preview.2] - 2024-02-22 + +### Engine Updates and Fixes + +- Fix `using assembly` to use `Path.Combine` when constructing assembly paths (#21169) +- Validate the value for `using namespace` during semantic checks to prevent declaring invalid namespaces (#21162) + +### General Cmdlet Updates and Fixes + +- Add `WinGetCommandNotFound` and `CompletionPredictor` modules to track usage (#21040) +- `ConvertFrom-Json`: Add `-DateKind` parameter (#20925) (Thanks @jborean93!) +- Add tilde expansion for windows native executables (#20402) (Thanks @domsleee!) +- Add `DirectoryInfo` to the `OutputType` for `New-Item` (#21126) (Thanks @MartinGC94!) +- Fix `Get-Error` serialization of array values (#21085) (Thanks @jborean93!) + +### Code Cleanup + +
+ + + +

We thank the following contributors!

+

@eltociear

+ +
+ +
    +
  • Fix a typo in CoreAdapter.cs (#21179) (Thanks @eltociear!)
  • +
  • Remove PSScheduledJob module source code (#21189)
  • +
+ +
+ +### Tests + +- Rewrite the mac syslog tests to make them less flaky (#21174) + +### Build and Packaging Improvements + +
+ + +

Bump to .NET 9 Preview 1

+

We thank the following contributors!

+

@gregsdennis

+ +
+ +
    +
  • Bump to .NET 9 Preview 1 (#21229)
  • +
  • Add dotnet-runtime-9.0 as a dependency for the Mariner package
  • +
  • Add dotenv install as latest version does not work with current Ruby version (#21239)
  • +
  • Remove surrogateFile setting of APIScan (#21238)
  • +
  • Update experimental-feature json files (#21213)
  • +
  • Update to the latest NOTICES file (#21236)(#21177)
  • +
  • Update the cgmanifest (#21237)(#21093)
  • +
  • Update the cgmanifest (#21178)
  • +
  • Bump XunitXml.TestLogger from 3.1.17 to 3.1.20 (#21207)
  • +
  • Update versions of PSResourceGet (#21190)
  • +
  • Generate MSI for win-arm64 installer (#20516)
  • +
  • Bump JsonSchema.Net to v5.5.1 (#21120) (Thanks @gregsdennis!)
  • +
+ +
+ +### Documentation and Help Content + +- Update `README.md` and `metadata.json` for v7.5.0-preview.1 release (#21094) +- Fix incorrect examples in XML docs in `PowerShell.cs` (#21173) +- Update WG members (#21091) +- Update changelog for v7.4.1 (#21098) + +[7.5.0-preview.2]: https://github.com/PowerShell/PowerShell/compare/v7.5.0-preview.1...v7.5.0-preview.2 + +## [7.5.0-preview.1] - 2024-01-18 + +### Breaking Changes + +- Fix `-OlderThan` and `-NewerThan` parameters for `Test-Path` when using `PathType` and date range (#20942) (Thanks @ArmaanMcleod!) +- Previously `-OlderThan` would be ignored if specified together +- Change `New-FileCatalog -CatalogVersion` default to 2 (#20428) (Thanks @ThomasNieto!) + +### General Cmdlet Updates and Fixes + +- Fix completion crash for the SCCM provider (#20815, #20919, #20915) (Thanks @MartinGC94!) +- Fix regression in `Get-Content` when `-Tail 0` and `-Wait` are used together (#20734) (Thanks @CarloToso!) +- Add `Aliases` to the properties shown up when formatting the help content of the parameter returned by `Get-Help` (#20994) +- Add implicit localization fallback to `Import-LocalizedData` (#19896) (Thanks @chrisdent-de!) +- Change `Test-FileCatalog` to use `File.OpenRead` to better handle the case where the file is being used (#20939) (Thanks @dxk3355!) +- Added `-Module` completion for `Save-Help` and `Update-Help` commands (#20678) (Thanks @ArmaanMcleod!) +- Add argument completer to `-Verb` for `Start-Process` (#20415) (Thanks @ArmaanMcleod!) +- Add argument completer to `-Scope` for `*-Variable`, `*-Alias` & `*-PSDrive` commands (#20451) (Thanks @ArmaanMcleod!) +- Add argument completer to `-Verb` for `Get-Verb` and `Get-Command` (#20286) (Thanks @ArmaanMcleod!) +- Fixing incorrect formatting string in `CommandSearcher` trace logging (#20928) (Thanks @powercode!) +- Ensure the filename is not null when logging WDAC ETW events (#20910) (Thanks @jborean93!) +- Fix four regressions introduced by the WDAC logging feature (#20913) +- Leave the input, output, and error handles unset when they are not redirected (#20853) +- Fix `Start-Process -PassThru` to make sure the `ExitCode` property is accessible for the returned `Process` object (#20749) (Thanks @CodeCyclone!) +- Fix `Group-Object` output using interpolated strings (#20745) (Thanks @mawosoft!) +- Fix rendering of `DisplayRoot` for network `PSDrive` (#20793) +- Fix `Invoke-WebRequest` to report correct size when `-Resume` is specified (#20207) (Thanks @LNKLEO!) +- Add `PSAdapter` and `ConsoleGuiTools` to module load telemetry allow list (#20641) +- Fix Web Cmdlets to allow `WinForm` apps to work correctly (#20606) +- Block getting help from network locations in restricted remoting sessions (#20593) +- Fix `Group-Object` to use current culture for its output (#20608) +- Add argument completer to `-Version` for `Set-StrictMode` (#20554) (Thanks @ArmaanMcleod!) +- Fix `Copy-Item` progress to only show completed when all files are copied (#20517) +- Fix UNC path completion regression (#20419) (Thanks @MartinGC94!) +- Add telemetry to check for specific tags when importing a module (#20371) +- Report error if invalid `-ExecutionPolicy` is passed to `pwsh` (#20460) +- Add `HelpUri` to `Remove-Service` (#20476) +- Fix `unixmode` to handle `setuid` and `sticky` when file is not an executable (#20366) +- Fix `Test-Connection` due to .NET 8 changes (#20369) +- Fix implicit remoting proxy cmdlets to act on common parameters (#20367) +- Set experimental features to stable for 7.4 release (#20285) +- Revert changes to continue using `BinaryFormatter` for `Out-GridView` (#20300) +- Fix `Get-Service` non-terminating error message to include category (#20276) +- Prevent `Export-CSV` from flushing with every input (#20282) (Thanks @Chris--A!) +- Fix a regression in DSC (#20268) +- Include the module version in error messages when module is not found (#20144) (Thanks @ArmaanMcleod!) +- Add `-Empty` and `-InputObject` parameters to `New-Guid` (#20014) (Thanks @CarloToso!) +- Remove the comment trigger from feedback provider (#20136) +- Prevent fallback to file completion when tab completing type names (#20084) (Thanks @MartinGC94!) +- Add the alias `r` to the parameter `-Recurse` for the `Get-ChildItem` command (#20100) (Thanks @kilasuit!) + +### Code Cleanup + +
+ + + +

We thank the following contributors!

+

@eltociear, @ImportTaste, @ThomasNieto, @0o001

+ +
+ +
    +
  • Fix typos in the code base (#20147, #20492, #20632, #21015, #20838) (Thanks @eltociear!)
  • +
  • Add the missing alias LP to -LiteralPath for some cmdlets (#20820) (Thanks @ImportTaste!)
  • +
  • Remove parenthesis for empty attribute parameters (#20087) (Thanks @ThomasNieto!)
  • +
  • Add space around keyword according to the CodeFactor rule (#20090) (Thanks @ThomasNieto!)
  • +
  • Remove blank lines as instructed by CodeFactor rules (#20086) (Thanks @ThomasNieto!)
  • +
  • Remove trailing whitespace (#20085) (Thanks @ThomasNieto!)
  • +
  • Fix typo in error message (#20145) (Thanks @0o001!)
  • +
+ +
+ +### Tools + +- Make sure feedback link in the bot's comment is clickable (#20878) (Thanks @floh96!) +- Fix bot so anyone who comments will remove the "Resolution-No Activity" label (#20788) +- Fix bot configuration to prevent multiple comments about "no activity" (#20758) +- Add bot logic for closing GitHub issues after 6 months of "no activity" (#20525) +- Refactor bot for easier use and updating (#20805) +- Configure bot to add survey comment for closed issues (#20397) + +### Tests + +- Suppress error output from `Set-Location` tests (#20499) +- Fix typo in `FileCatalog.Tests.ps1` (#20329) (Thanks @eltociear!) +- Continue to improve tests for release automation (#20182) +- Skip the test on x86 as `InstallDate` is not visible on `Wow64` (#20165) +- Harden some problematic release tests (#20155) + +### Build and Packaging Improvements + +
+ + + +

We thank the following contributors!

+

@alerickson, @Zhoneym, @0o001

+ +
+ +
    +
  • Bump .NET SDK to 8.0.101 (#21084)
  • +
  • Update the cgmanifest (#20083, #20436, #20523, #20560, #20627, #20764, #20906, #20933, #20955, #21047)
  • +
  • Update to the latest NOTICES file (#20074, #20161, #20385, #20453, #20576, #20590, #20880, #20905)
  • +
  • Bump StyleCop.Analyzers from 1.2.0-beta.507 to 1.2.0-beta.556 (#20953)
  • +
  • Bump xUnit to 2.6.6 (#21071)
  • +
  • Bump JsonSchema.Net to 5.5.0 (#21027)
  • +
  • Fix failures in GitHub action markdown-link-check (#20996)
  • +
  • Bump xunit.runner.visualstudio to 2.5.6 (#20966)
  • +
  • Bump github/codeql-action from 2 to 3 (#20927)
  • +
  • Bump Markdig.Signed to 0.34.0 (#20926)
  • +
  • Bump Microsoft.ApplicationInsights from 2.21.0 to 2.22.0 (#20888)
  • +
  • Bump Microsoft.NET.Test.Sdk to 17.8.0 (#20660)
  • +
  • Update apiscan.yml to have access to the AzDevOpsArtifacts variable group (#20671)
  • +
  • Set the ollForwardOnNoCandidateFx in runtimeconfig.json to roll forward only on minor and patch versions (#20689)
  • +
  • Sign the global tool shim executable (#20794)
  • +
  • Bump actions/github-script from 6 to 7 (#20682)
  • +
  • Remove RHEL7 publishing to packages.microsoft.com as it's no longer supported (#20849)
  • +
  • Bump Microsoft.CodeAnalysis.CSharp to 4.8.0 (#20751)
  • +
  • Add internal nuget feed to compliance build (#20669)
  • +
  • Copy azure blob with PowerShell global tool to private blob and move to CDN during release (#20659)
  • +
  • Fix release build by making the internal SDK parameter optional (#20658)
  • +
  • Update PSResourceGet version to 1.0.1 (#20652)
  • +
  • Make internal .NET SDK URL as a parameter for release builld (#20655)
  • +
  • Fix setting of variable to consume internal SDK source (#20644)
  • +
  • Bump Microsoft.Management.Infrastructure to v3.0.0 (#20642)
  • +
  • Bump Microsoft.PowerShell.Native to v7.4.0 (#20617)
  • +
  • Bump Microsoft.Security.Extensions from 1.2.0 to 1.3.0 (#20556)
  • +
  • Fix package version for .NET nuget packages (#20551)
  • +
  • Add SBOM for release pipeline (#20519)
  • +
  • Block any preview vPack release (#20243)
  • +
  • Only registry App Path for release package (#20478)
  • +
  • Increase timeout when publishing packages to pacakages.microsoft.com (#20470)
  • +
  • Fix alpine tar package name and do not crossgen alpine fxdependent package (#20459)
  • +
  • Bump PSReadLine from 2.2.6 to 2.3.4 (#20305)
  • +
  • Remove the ref folder before running compliance (#20373)
  • +
  • Updates RIDs used to generate component Inventory (#20370)
  • +
  • Bump XunitXml.TestLogger from 3.1.11 to 3.1.17 (#20293)
  • +
  • Update experimental-feature json files (#20335)
  • +
  • Use fxdependent-win-desktop runtime for compliance runs (#20326)
  • +
  • Release build: Change the names of the PATs (#20307)
  • +
  • Add mapping for mariner arm64 stable (#20213)
  • +
  • Put the calls to Set-AzDoProjectInfo and Set-AzDoAuthToken in the right order (#20306)
  • +
  • Enable vPack provenance data (#20220)
  • +
  • Bump actions/checkout from 3 to 4 (#20205)
  • +
  • Start using new packages.microsoft.com cli (#20140, #20141)
  • +
  • Add mariner arm64 to PMC release (#20176)
  • +
  • Fix typo donet to dotnet in build scripts and pipelines (#20122) (Thanks @0o001!)
  • +
  • Install the pmc cli
  • +
  • Add skip publish parameter
  • +
  • Add verbose to clone
  • +
+ +
+ +### Documentation and Help Content + +- Include information about upgrading in readme (#20993) +- Expand "iff" to "if-and-only-if" in XML doc content (#20852) +- Update LTS links in README.md to point to the v7.4 packages (#20839) (Thanks @kilasuit!) +- Update `README.md` to improve readability (#20553) (Thanks @AnkitaSikdar005!) +- Fix link in `docs/community/governance.md` (#20515) (Thanks @suravshresth!) +- Update `ADOPTERS.md` (#20555) (Thanks @AnkitaSikdar005!) +- Fix a typo in `ADOPTERS.md` (#20504, #20520) (Thanks @shruti-sen2004!) +- Correct grammatical errors in `README.md` (#20509) (Thanks @alienishi!) +- Add 7.3 changelog URL to readme (#20473) (Thanks @Saibamen!) +- Clarify some comments and documentation (#20462) (Thanks @darkstar!) + +[7.5.0-preview.1]: https://github.com/PowerShell/PowerShell/compare/v7.4.1...v7.5.0-preview.1 diff --git a/CHANGELOG/README.md b/CHANGELOG/README.md new file mode 100644 index 00000000000..c20cd311ff5 --- /dev/null +++ b/CHANGELOG/README.md @@ -0,0 +1,11 @@ +# Changelogs + +- [Current preview changelog](preview.md) +- [7.4 changelog](7.4.md) +- [7.3 changelog](7.3.md) +- [7.2 changelog](7.2.md) +- [7.1 changelog](7.1.md) +- [7.0 changelog](7.0.md) +- [6.2 changelog](6.2.md) +- [6.1 changelog](6.1.md) +- [6.0 changelog](6.0.md) diff --git a/CHANGELOG/preview.md b/CHANGELOG/preview.md new file mode 100644 index 00000000000..f1c5f566289 --- /dev/null +++ b/CHANGELOG/preview.md @@ -0,0 +1,727 @@ +# Preview Changelog + +## [7.6.0-preview.6] - 2025-12-11 + +### Engine Updates and Fixes + +- Properly Expand Aliases to their actual ResolvedCommand (#26571) (Thanks @kilasuit!) + +### General Cmdlet Updates and Fixes + +- Update `Microsoft.PowerShell.PSResourceGet` to `v1.2.0-preview5` (#26590) +- Make the experimental feature `PSFeedbackProvider` stable (#26502) +- Fix a regression in the API `CompletionCompleters.CompleteFilename()` that causes null reference exception (#26487) +- Add Delimiter parameter to `Get-Clipboard` (#26572) (Thanks @MartinGC94!) +- Close pipe client handles after creating the child ssh process (#26564) +- Make some experimental features stable (#26490) +- DSC v3 resource for PowerShell Profile (#26447) + +### Tools + +- Add merge conflict marker detection to linux-ci workflow and refactor existing actions to use reusable get-changed-files action (#26530) +- Add reusable get-changed-files action and refactor existing actions (#26529) +- Refactor analyze job to reusable workflow and enable on Windows CI (#26494) + +### Tests + +- Fix merge conflict checker for empty file lists and filter *.cs files (#26556) +- Add markdown link verification for PRs (#26445) + +### Build and Packaging Improvements + +
+ + + +

Expand to see details.

+ +
+ +
    +
  • Fix template path for rebuild branch check in package.yml (#26560)
  • +
  • Update the macos package name for preview releases to match the previous pattern (#26576)
  • +
  • Add rebuild branch support with conditional MSIX signing (#26573)
  • +
  • Update the WCF packages to the latest version that is compatible with v4.10.3 (#26503)
  • +
  • Improve ADO package build and validation across platforms (#26532)
  • +
  • Mirror .NET/runtime ICU version range in PowerShell (#26563) (Thanks @kasperk81!)
  • +
  • Update the macos package name for preview releases to match the previous pattern (#26562)
  • +
  • Fix condition syntax for StoreBroker package tasks in MSIX pipeline (#26561)
  • +
  • Move package validation to package pipeline (#26558)
  • +
  • Optimize/split windows package signing (#26557)
  • +
  • Remove usage of fpm for DEB package generation (#26504)
  • +
  • Add log grouping to build.psm1 for collapsible GitHub Actions logs (#26524)
  • +
  • Replace fpm with native macOS packaging tools (pkgbuild/productbuild) (#26501)
  • +
  • Replace fpm with native rpmbuild for RPM package generation (#26441)
  • +
  • Fix GitHub API rate limit errors in test actions (#26492)
  • +
  • Convert Azure DevOps Linux Packaging pipeline to GitHub Actions workflow (#26493)
  • +
  • Refactor: Centralize xUnit tests into reusable workflow and remove legacy verification (#26488)
  • +
  • Fix build to only enable ready-to-run for the Release configuration (#26481)
  • +
  • Integrate Windows packaging into windows-ci workflow using reusable workflow (#26468)
  • +
  • Update outdated package references (#26471)
  • +
  • GitHub Workflow cleanup (#26439)
  • +
  • Update PSResourceGet package version to preview4 (#26438)
  • +
  • Update PSReadLine to v2.4.5 (#26446)
  • +
  • Add network isolation policy parameter to vPack pipeline (#26444)
  • +
  • Fix a couple more lint errors
  • +
  • Fix lint errors in preview.md
  • +
  • Make MSIX publish stage dependent on SetReleaseTagandContainerName stage
  • +
+ +
+ +[7.6.0-preview.6]: https://github.com/PowerShell/PowerShell/compare/v7.6.0-preview.5...v7.6.0-preview.6 + +## [7.6.0-preview.5] - 2025-09-30 + +### Engine Updates and Fixes + +- Allow opt-out of the named-pipe listener using the environment variable `POWERSHELL_DIAGNOSTICS_OPTOUT` (#26086) +- Ensure that socket timeouts are set only during the token validation (#26066) +- Fix race condition in `RemoteHyperVSocket` (#26057) +- Fix `stderr` output of console host to respect `NO_COLOR` (#24391) +- Update PSRP protocol to deprecate session key exchange between newer client and server (#25774) +- Fix the `ssh` PATH check in `SSHConnectionInfo` when the default Runspace is not available (#25780) (Thanks @jborean93!) +- Adding hex format for native command exit codes (#21067) (Thanks @sba923!) +- Fix infinite loop crash in variable type inference (#25696) (Thanks @MartinGC94!) +- Add `PSForEach` and `PSWhere` as aliases for the PowerShell intrinsic methods `Where` and `Foreach` (#25511) (Thanks @powercode!) + +### General Cmdlet Updates and Fixes + +- Remove `IsScreenReaderActive()` check from `ConsoleHost` (#26118) +- Fix `ConvertFrom-Json` to ignore comments inside array literals (#14553) (#26050) (Thanks @MatejKafka!) +- Fix `-Debug` to not trigger the `ShouldProcess` prompt (#26081) +- Add the parameter `Register-ArgumentCompleter -NativeFallback` to support registering a cover-all completer for native commands (#25230) +- Change the default feedback provider timeout from 300ms to 1000ms (#25910) +- Update PATH environment variable for package manager executable on Windows (#25847) +- Fix `Write-Host` to respect `OutputRendering = PlainText` (#21188) +- Improve the `$using` expression support in `Invoke-Command` (#24025) (Thanks @jborean93!) +- Use parameter `HelpMessage` for tool tip in parameter completion (#25108) (Thanks @jborean93!) +- Revert "Never load a module targeting the PSReadLine module's `SessionState`" (#25792) +- Fix debug tracing error with magic extents (#25726) (Thanks @jborean93!) +- Add `MethodInvocation` trace for overload tracing (#21320) (Thanks @jborean93!) +- Improve verbose and debug logging level messaging in web cmdlets (#25510) (Thanks @JustinGrote!) +- Fix quoting in completion if the path includes a double quote character (#25631) (Thanks @MartinGC94!) +- Fix the common parameter `-ProgressAction` for advanced functions (#24591) (Thanks @cmkb3!) +- Use absolute path in `FileSystemProvider.CreateDirectory` (#24615) (Thanks @Tadas!) +- Make inherited protected internal instance members accessible in PowerShell class scope (#25245) (Thanks @mawosoft!) +- Treat `-Target` as literal in `New-Item` (#25186) (Thanks @GameMicrowave!) +- Remove duplicate modules from completion results (#25538) (Thanks @MartinGC94!) +- Add completion for variables assigned in `ArrayLiteralAst` and `ParenExpressionAst` (#25303) (Thanks @MartinGC94!) +- Add support for thousands separators in `[bigint]` casting (#25396) (Thanks @AbishekPonmudi!) +- Add internal methods to check Preferences (#25514) (Thanks @iSazonov!) +- Improve debug logging of Web cmdlet request and response (#25479) (Thanks @JustinGrote!) +- Revert "Allow empty prefix string in 'Import-Module -Prefix' to override default prefix in manifest (#20409)" (#25462) (Thanks @MartinGC94!) +- Fix the `NullReferenceException` when writing progress records to console from multiple threads (#25440) (Thanks @kborowinski!) +- Update `Get-Service` to ignore common errors when retrieving non-critical properties for a service (#24245) (Thanks @jborean93!) +- Add single/double quote support for `Join-String` Argument Completer (#25283) (Thanks @ArmaanMcleod!) +- Fix tab completion for env/function variables (#25346) (Thanks @jborean93!) +- Fix `Out-GridView` by replacing use of obsolete `BinaryFormatter` with custom implementation (#25497) (Thanks @mawosoft!) +- Remove the use of Windows PowerShell ETW provider ID from codebase and update the `PSDiagnostics` module to work for PowerShell 7 (#25590) + +### Code Cleanup + +
+ + + +

We thank the following contributors!

+

@xtqqczze, @mawosoft, @ArmaanMcleod

+ +
+ +
    +
  • Enable CA2021: Do not call Enumerable.Cast or Enumerable.OfType with incompatible types (#25813) (Thanks @xtqqczze!)
  • +
  • Remove some unused ConsoleControl structs (#26063) (Thanks @xtqqczze!)
  • +
  • Remove unused FileStreamBackReader.NativeMethods type (#26062) (Thanks @xtqqczze!)
  • +
  • Ensure data-serialization files end with one newline (#26039) (Thanks @xtqqczze!)
  • +
  • Remove unnecessary CS0618 suppressions from Variant APIs (#26006) (Thanks @xtqqczze!)
  • +
  • Ensure .cs files end with exactly one newline (#25968) (Thanks @xtqqczze!)
  • +
  • Remove obsolete CA2105 rule suppression (#25938) (Thanks @xtqqczze!)
  • +
  • Remove obsolete CA1703 rule suppression (#25955) (Thanks @xtqqczze!)
  • +
  • Remove obsolete CA2240 rule suppression (#25957) (Thanks @xtqqczze!)
  • +
  • Remove obsolete CA1701 rule suppression (#25948) (Thanks @xtqqczze!)
  • +
  • Remove obsolete CA2233 rule suppression (#25951) (Thanks @xtqqczze!)
  • +
  • Remove obsolete CA1026 rule suppression (#25934) (Thanks @xtqqczze!)
  • +
  • Remove obsolete CA1059 rule suppression (#25940) (Thanks @xtqqczze!)
  • +
  • Remove obsolete CA2118 rule suppression (#25924) (Thanks @xtqqczze!)
  • +
  • Remove redundant System.Runtime.Versioning attributes (#25926) (Thanks @xtqqczze!)
  • +
  • Seal internal types in Microsoft.PowerShell.Commands.Utility (#25892) (Thanks @xtqqczze!)
  • +
  • Seal internal types in Microsoft.PowerShell.Commands.Management (#25849) (Thanks @xtqqczze!)
  • +
  • Make the interface IDeepCloneable internal to minimize confusion (#25552)
  • +
  • Remove OnDeserialized and Serializable attributes from Microsoft.Management.UI.Internal project (#25548)
  • +
  • Refactor Tooltip/ListItemText mapping to use CompletionDisplayInfoMapper delegate (#25395) (Thanks @ArmaanMcleod!)
  • +
+ +
+ +### Tools + +- Add Codeql Suppressions (#25943, #26132) +- Update CODEOWNERS to add Justin as a maintainer (#25386) +- Do not run labels workflow in the internal repository (#25279) + +### Tests + +- Mark the 3 consistently failing tests as pending to unblock PRs (#26091) +- Make some tests less noisy on failure (#26035) (Thanks @xtqqczze!) +- Suppress false positive `PSScriptAnalyzer` warnings in tests and build scripts (#25864) +- Fix updatable help test for new content (#25819) +- Add more tests for `PSForEach` and `PSWhere` methods (#25519) +- Fix the isolated module test that was disabled previously (#25420) + +### Build and Packaging Improvements + +
+ + + +

We thank the following contributors!

+

@alerickson, @senerh, @RichardSlater, @xtqqczze

+ +
+ +
    +
  • Update package references for the master branch (#26124)
  • +
  • Remove ThreadJob module and update PSReadLine to 2.4.4-beta4 (#26120)
  • +
  • Automate Store Publishing (#25725)
  • +
  • Add global config change detection to action (#26082)
  • +
  • Update outdated package references (#26069)
  • +
  • Ensure that the workflows are triggered on .globalconfig and other files at the root of the repo (#26034)
  • +
  • Update Microsoft.PowerShell.PSResourceGet to 1.2.0-preview3 (#26056) (Thanks @alerickson!)
  • +
  • Update metadata for Stable to v7.5.3 and LTS to v7.4.12 (#26054) (Thanks @senerh!)
  • +
  • Bump github/codeql-action from 3.30.2 to 3.30.3 (#26036)
  • +
  • Update version for the package Microsoft.PowerShell.Native (#26041)
  • +
  • Fix the APIScan pipeline (#26016)
  • +
  • Move PowerShell build to use .NET SDK 10.0.100-rc.1 (#26027)
  • +
  • fix(apt-package): add libicu76 dependency to support Debian 13 (#25866) (Thanks @RichardSlater!)
  • +
  • Bump github/codeql-action from 3.30.1 to 3.30.2 (#26029)
  • +
  • Update Ev2 Shell Extension Image to AzureLinux 3 for PMC Release (#26025)
  • +
  • Bump github/codeql-action from 3.30.0 to 3.30.1 (#26008)
  • +
  • Bump actions/github-script from 7 to 8 (#25983)
  • +
  • Fix variable reference for release environment in pipeline (#26012)
  • +
  • Add LinuxHost Network configuration to PowerShell Packages pipeline (#26000)
  • +
  • Make logical template name consistent between pipelines (#25990)
  • +
  • Update container images to use mcr.microsoft.com for Linux and Azure GǪ (#25981)
  • +
  • Bump github/codeql-action from 3.29.11 to 3.30.0 (#25966)
  • +
  • Bump actions/setup-dotnet from 4 to 5 (#25978)
  • +
  • Add build to vPack Pipeline (#25915)
  • +
  • Replace DOTNET_SKIP_FIRST_TIME_EXPERIENCE with DOTNET_NOLOGO (#25946) (Thanks @xtqqczze!)
  • +
  • Bump actions/dependency-review-action from 4.7.2 to 4.7.3 (#25930)
  • +
  • Bump github/codeql-action from 3.29.10 to 3.29.11 (#25889)
  • +
  • Remove AsyncSDL from Pipelines Toggle Official/NonOfficial Runs (#25885)
  • +
  • Specify .NET Search by Build Type (#25837)
  • +
  • Update PowerShell to use .NET SDK v10-preview.7 (#25876)
  • +
  • Bump actions/dependency-review-action from 4.7.1 to 4.7.2 (#25882)
  • +
  • Bump github/codeql-action from 3.29.9 to 3.29.10 (#25881)
  • +
  • Change the macos runner image to macos 15 large (#25867)
  • +
  • Bump actions/checkout from 4 to 5 (#25853)
  • +
  • Bump github/codeql-action from 3.29.7 to 3.29.9 (#25857)
  • +
  • Update to .NET 10 Preview 6 (#25828)
  • +
  • Bump agrc/create-reminder-action from 1.1.20 to 1.1.22 (#25808)
  • +
  • Bump agrc/reminder-action from 1.0.17 to 1.0.18 (#25807)
  • +
  • Bump github/codeql-action from 3.28.19 to 3.29.5 (#25797)
  • +
  • Bump super-linter/super-linter from 7.4.0 to 8.0.0 (#25770)
  • +
  • Update metadata for v7.5.2 and v7.4.11 releases (#25687)
  • +
  • Correct Capitalization Referencing Templates (#25669)
  • +
  • Change linux packaging tests to ubuntu latest (#25634)
  • +
  • Bump github/codeql-action from 3.28.18 to 3.28.19 (#25636)
  • +
  • Move to .NET 10 preview 4 and update package references (#25602)
  • +
  • Revert "Add windows signing for pwsh.exe" (#25586)
  • +
  • Bump ossf/scorecard-action from 2.4.1 to 2.4.2 (#25628)
  • +
  • Publish .msixbundle package as a VPack (#25612)
  • +
  • Bump agrc/reminder-action from 1.0.16 to 1.0.17 (#25573)
  • +
  • Bump agrc/create-reminder-action from 1.1.18 to 1.1.20 (#25572)
  • +
  • Bump github/codeql-action from 3.28.17 to 3.28.18 (#25580)
  • +
  • Bump super-linter/super-linter from 7.3.0 to 7.4.0 (#25563)
  • +
  • Bump actions/dependency-review-action from 4.7.0 to 4.7.1 (#25562)
  • +
  • Update metadata.json with 7.4.10 (#25554)
  • +
  • Bump github/codeql-action from 3.28.16 to 3.28.17 (#25508)
  • +
  • Bump actions/dependency-review-action from 4.6.0 to 4.7.0 (#25529)
  • +
  • Move MSIXBundle to Packages and Release to GitHub (#25512)
  • +
  • Update outdated package references (#25506)
  • +
  • Bump github/codeql-action from 3.28.15 to 3.28.16 (#25429)
  • +
  • Fix Conditional Parameter to Skip NuGet Publish (#25468)
  • +
  • Update metadata.json (#25438)
  • +
  • Fix MSIX artifact upload, vPack template, changelog hashes, git tag command (#25437)
  • +
  • Use new variables template for vPack (#25434)
  • +
  • Bump agrc/create-reminder-action from 1.1.17 to 1.1.18 (#25416)
  • +
  • Add PSScriptAnalyzer (#25423)
  • +
  • Update outdated package references (#25392)
  • +
  • Use GitHubReleaseTask instead of custom script (#25398)
  • +
  • Update APIScan to use new symbols server (#25388)
  • +
  • Retry ClearlyDefined operations (#25385)
  • +
  • Update to .NET 10.0.100-preview.3 (#25358)
  • +
  • Enhance path filters action to set outputs for all changes when not a PR (#25367)
  • +
  • Combine GitHub and Nuget Release Stage (#25318)
  • +
  • Add Windows Store Signing to MSIX bundle (#25296)
  • +
  • Bump skitionek/notify-microsoft-teams from 190d4d92146df11f854709774a4dae6eaf5e2aa3 to e7a2493ac87dad8aa7a62f079f295e54ff511d88 (#25366)
  • +
  • Add CodeQL suppressions for PowerShell intended behavior (#25359)
  • +
  • Migrate MacOS Signing to OneBranch (#25295)
  • +
  • Bump github/codeql-action from 3.28.13 to 3.28.15 (#25290)
  • +
  • Update test result processing to use NUnitXml format and enhance logging for better clarity (#25288)
  • +
  • Fix R2R for fxdependent packaging (#26131)
  • +
  • Remove UseDotnet task and use the dotnet-install script (#26093)
  • +
+ +
+ +### Documentation and Help Content + +- Fix a typo in the 7.4 changelog (#26038) (Thanks @VbhvGupta!) +- Add 7.4.12 changelog (#26011) +- Add v7.5.3 changelog (#25994) +- Fix typo in changelog for script filename suggestion (#25962) +- Update changelog for v7.5.2 (#25668) +- Update changelog for v7.4.11 (#25667) +- Update build documentation with instruction of dev terminal (#25587) +- Update links and contribution guide in documentation (#25532) (Thanks @JustinGrote!) +- Add 7.4.10 changelog (#25520) +- Add 7.5.1 changelog (#25382) + +[7.6.0-preview.5]: https://github.com/PowerShell/PowerShell/compare/v7.6.0-preview.4...v7.6.0-preview.5 + +## [7.6.0-preview.4] + +### Breaking Changes + +- Fix `WildcardPattern.Escape` to escape lone backticks correctly (#25211) (Thanks @ArmaanMcleod!) +- Convert `-ChildPath` parameter to `string[]` for `Join-Path` cmdlet (#24677) (Thanks @ArmaanMcleod!) + +PowerShell 7.6-preview.4 includes the following updated modules: + +- **Microsoft.PowerShell.ThreadJob** v2.2.0 +- **ThreadJob** v2.1.0 +The **ThreadJob** module was renamed to **Microsoft.PowerShell.ThreadJob**. There is no difference +in the functionality of the module. To ensure backward compatibility for scripts that use the old +name, the **ThreadJob** v2.1.0 module is a proxy module that points to the +**Microsoft.PowerShell.ThreadJob** v2.2.0. + +### Engine Updates and Fixes + +- Add `PipelineStopToken` to `Cmdlet` which will be signaled when the pipeline is stopping (#24620) (Thanks @jborean93!) +- Fallback to AppLocker after `WldpCanExecuteFile` (#24912) +- Move .NET method invocation logging to after the needed type conversion is done for method arguments (#25022) +- Fix share completion with provider and spaces (#19440) (Thanks @MartinGC94!) + +### General Cmdlet Updates and Fixes + +- Exclude `-OutVariable` assignments within the same `CommandAst` when inferring variables (#25224) (Thanks @MartinGC94!) +- Fix infinite loop in variable type inference (#25206) (Thanks @MartinGC94!) +- Update `Microsoft.PowerShell.PSResourceGet` version in `PSGalleryModules.csproj` (#25135) +- Add tooltips for hashtable key completions (#17864) (Thanks @MartinGC94!) +- Fix type inference of parameters in classic functions (#25172) (Thanks @MartinGC94!) +- Improve assignment type inference (#21143) (Thanks @MartinGC94!) +- Fix `TypeName.GetReflectionType()` to work when the `TypeName` instance represents a generic type definition within a `GenericTypeName` (#24985) +- Remove the old fuzzy suggestion and fix the local script filename suggestion (#25177) +- Improve variable type inference (#19830) (Thanks @MartinGC94!) +- Fix parameter completion when script requirements fail (#17687) (Thanks @MartinGC94!) +- Improve the completion for attribute arguments (#25129) (Thanks @MartinGC94!) +- Fix completion that relies on pseudobinding in script blocks (#25122) (Thanks @MartinGC94!) +- Don't complete duplicate command names (#21113) (Thanks @MartinGC94!) +- Make `SystemPolicy` public APIs visible but non-op on Unix platforms so that they can be included in `PowerShellStandard.Library` (#25051) +- Set standard handles explicitly when starting a process with `-NoNewWindow` (#25061) +- Fix tooltip for variable expansion and include desc (#25112) (Thanks @jborean93!) +- Add type inference for functions without OutputType attribute and anonymous functions (#21127) (Thanks @MartinGC94!) +- Add completion for variables assigned by command redirection (#25104) (Thanks @MartinGC94!) +- Handle type inference for redirected commands (#21131) (Thanks @MartinGC94!) +- Allow empty prefix string in `Import-Module -Prefix` to override default prefix in manifest (#20409) (Thanks @MartinGC94!) +- Update variable/property assignment completion so it can fallback to type inference (#21134) (Thanks @MartinGC94!) +- Use `Get-Help` approach to find `about_*.help.txt` files with correct locale for completions (#24194) (Thanks @MartinGC94!) +- Use script filepath when completing relative paths for using statements (#20017) (Thanks @MartinGC94!) +- Fix completion of variables assigned inside Do loops (#25076) (Thanks @MartinGC94!) +- Fix completion of provider paths when a path returns itself instead of its children (#24755) (Thanks @MartinGC94!) +- Enable completion of scoped variables without specifying scope (#20340) (Thanks @MartinGC94!) +- Fix issue with incomplete results when completing paths with wildcards in non-filesystem providers (#24757) (Thanks @MartinGC94!) +- Allow DSC parsing through OS architecture translation layers (#24852) (Thanks @bdeb1337!) + +### Code Cleanup + +
+ + + +

We thank the following contributors!

+

@ArmaanMcleod, @pressRtowin

+ +
+ +
    +
  • Refactor and add comments to CompletionRequiresQuotes to clarify implementation (#25223) (Thanks @ArmaanMcleod!)
  • +
  • Add QuoteCompletionText method to CompletionHelpers class (#25180) (Thanks @ArmaanMcleod!)
  • +
  • Remove CompletionHelpers escape parameter from CompletionRequiresQuotes (#25178) (Thanks @ArmaanMcleod!)
  • +
  • Refactor CompletionHelpers HandleDoubleAndSingleQuote to have less nesting logic (#25179) (Thanks @ArmaanMcleod!)
  • +
  • Make the use of Oxford commas consistent (#25139)(#25140)(Thanks @pressRtowin!)
  • +
  • Move common completion methods to CompletionHelpers class (#25138) (Thanks @ArmaanMcleod!)
  • +
  • Return Array.Empty instead of collection [] (#25137) (Thanks @ArmaanMcleod!)
  • +
+ +
+ +### Tools + +- Check GH token availability for Get-Changelog (#25133) + +### Tests + +- Add XUnit test for `HandleDoubleAndSingleQuote` in CompletionHelpers class (#25181) (Thanks @ArmaanMcleod!) + +### Build and Packaging Improvements + +
+ +
    +
  • Switch to ubuntu-lastest for CI (#25247)
  • +
  • Update outdated package references (#25026)(#25232)
  • +
  • Bump Microsoft.PowerShell.ThreadJob and ThreadJob modules (#25232)
  • +
  • Bump github/codeql-action from 3.27.9 to 3.28.13 (#25218)(#25231)
  • +
  • Update .NET SDK to 10.0.100-preview.2 (#25154)(#25225)
  • +
  • Remove obsolete template from Windows Packaging CI (#25226)
  • +
  • Bump actions/upload-artifact from 4.5.0 to 4.6.2 (#25220)
  • +
  • Bump agrc/reminder-action from 1.0.15 to 1.0.16 (#25222)
  • +
  • Bump actions/checkout from 2 to 4 (#25221)
  • +
  • Add NoWarn NU1605 to System.ServiceModel.* (#25219)
  • +
  • Bump actions/github-script from 6 to 7 (#25217)
  • +
  • Bump ossf/scorecard-action from 2.4.0 to 2.4.1 (#25216)
  • +
  • Bump super-linter/super-linter from 7.2.1 to 7.3.0 (#25215)
  • +
  • Bump agrc/create-reminder-action from 1.1.16 to 1.1.17 (#25214)
  • +
  • Remove dependabot updates that don't work (#25213)
  • +
  • Update GitHub Actions to work in private GitHub repo (#25197)
  • +
  • Cleanup old release pipelines (#25201)
  • +
  • Update package pipeline windows image version (#25191)
  • +
  • Skip additional packages when generating component manifest (#25102)
  • +
  • Only build Linux for packaging changes (#25103)
  • +
  • Remove Az module installs and AzureRM uninstalls in pipeline (#25118)
  • +
  • Add GitHub Actions workflow to verify PR labels (#25145)
  • +
  • Add back-port workflow using dotnet/arcade (#25106)
  • +
  • Make Component Manifest Updater use neutral target in addition to RID target (#25094)
  • +
  • Make sure the vPack pipeline does not produce an empty package (#24988)
  • +
+ +
+ +### Documentation and Help Content + +- Add 7.4.9 changelog (#25169) +- Create changelog for 7.4.8 (#25089) + +[7.6.0-preview.4]: https://github.com/PowerShell/PowerShell/compare/v7.6.0-preview.3...v7.6.0-preview.4 + +## [7.6.0-preview.3] + +### Breaking Changes + +- Remove trailing space from event source name (#24192) (Thanks @MartinGC94!) + +### General Cmdlet Updates and Fixes + +- Add completion single/double quote support for `-Noun` parameter for `Get-Command` (#24977) (Thanks @ArmaanMcleod!) +- Stringify `ErrorRecord` with empty exception message to empty string (#24949) (Thanks @MatejKafka!) +- Add completion single/double quote support for `-PSEdition` parameter for `Get-Module` (#24971) (Thanks @ArmaanMcleod!) +- Error when `New-Item -Force` is passed an invalid directory name (#24936) (Thanks @kborowinski!) +- Allow `Start-Transcript`to use `$Transcript` which is a `PSObject` wrapped string to specify the transcript path (#24963) (Thanks @kborowinski!) +- Add quote handling in `Verb`, `StrictModeVersion`, `Scope` & `PropertyType` Argument Completers with single helper method (#24839) (Thanks @ArmaanMcleod!) +- Improve `Start-Process -Wait` polling efficiency (#24711) (Thanks @jborean93!) +- Convert `InvalidCommandNameCharacters` in `AnalysisCache` to `SearchValues` for more efficient char searching (#24880) (Thanks @ArmaanMcleod!) +- Convert `s_charactersRequiringQuotes` in Completion Completers to `SearchValues` for more efficient char searching (#24879) (Thanks @ArmaanMcleod!) + +### Code Cleanup + +
+ + + +

We thank the following contributors!

+

@xtqqczze, @fMichaleczek, @ArmaanMcleod

+ +
+ +
    +
  • Fix RunspacePool, RunspacePoolInternal and RemoteRunspacePoolInternal IDisposable implementation (#24720) (Thanks @xtqqczze!)
  • +
  • Remove redundant Attribute suffix (#24940) (Thanks @xtqqczze!)
  • +
  • Fix formatting of the XML comment for SteppablePipeline.Clean() (#24941)
  • +
  • Use Environment.ProcessId in SpecialVariables.PID (#24926) (Thanks @fMichaleczek!)
  • +
  • Replace char[] array in CompletionRequiresQuotes with cached SearchValues (#24907) (Thanks @ArmaanMcleod!)
  • +
  • Update IndexOfAny calls with invalid path/filename to SearchValues<char> for more efficient char searching (#24896) (Thanks @ArmaanMcleod!)
  • +
  • Seal internal types in PlatformInvokes (#24826) (Thanks @xtqqczze!)
  • +
+ +
+ +### Tools + +- Update CODEOWNERS (#24989) + +### Build and Packaging Improvements + +
+ + + +

We thank the following contributors!

+

@xtqqczze, @KyZy7

+ +
+ +
    +
  • Update branch for release - Transitive - false - none (#24995)
  • +
  • Add setup dotnet action to the build composite action (#24996)
  • +
  • Give the pipeline runs meaningful names (#24987)
  • +
  • Fix V-Pack download package name (#24866)
  • +
  • Set LangVersion compiler option to 13.0 in Test.Common.props (#24621) (Thanks @xtqqczze!)
  • +
  • Fix release branch filters (#24933)
  • +
  • Fix GitHub Action filter overmatching (#24929)
  • +
  • Add UseDotnet task for installing dotnet (#24905)
  • +
  • Convert powershell/PowerShell-CI-macos to GitHub Actions (#24914)
  • +
  • Convert powershell/PowerShell-CI-linux to GitHub Actions (#24913)
  • +
  • Convert powershell/PowerShell-Windows-CI to GitHub Actions (#24899)
  • +
  • Fix MSIX stage in release pipeline (#24900)
  • +
  • Update .NET SDK (#24906)
  • +
  • Update metadata.json (#24862)
  • +
  • PMC parse state correctly from update command's response (#24850)
  • +
  • Add EV2 support for publishing PowerShell packages to PMC (#24841)
  • +
  • Remove AzDO credscan as it is now in GitHub (#24842)
  • +
  • Add *.props and sort path filters for windows CI (#24822)
  • +
  • Use work load identity service connection to download makeappx tool from storage account (#24817)
  • +
  • Update path filters for Windows CI (#24809)
  • +
  • Update outdated package references (#24758)
  • +
  • Update metadata.json (#24787) (Thanks @KyZy7!)
  • +
  • Add tool package download in publish nuget stage (#24790)
  • +
  • Fix Changelog content grab during GitHub Release (#24788)
  • +
  • Update metadata.json (#24764)
  • +
  • Update Microsoft.PowerShell.PSResourceGet to 1.1.0 (#24767)
  • +
  • Add a parameter that skips verify packages step (#24763)
  • +
+ +
+ +### Documentation and Help Content + +- Add 7.4.7 Changelog (#24844) +- Create changelog for v7.5.0 (#24808) +- Update Changelog for v7.6.0-preview.2 (#24775) + +[7.6.0-preview.3]: https://github.com/PowerShell/PowerShell/compare/v7.6.0-preview.2...v7.6.0-preview.3 + +## [7.6.0-preview.2] - 2025-01-14 + +### General Cmdlet Updates and Fixes + +- Add the `AIShell` module to telemetry collection list (#24747) +- Add helper in `EnumSingleTypeConverter` to get enum names as array (#17785) (Thanks @fflaten!) +- Return correct FileName property for `Get-Item` when listing alternate data streams (#18019) (Thanks @kilasuit!) +- Add `-ExcludeModule` parameter to `Get-Command` (#18955) (Thanks @MartinGC94!) +- Update Named and Statement block type inference to not consider AssignmentStatements and Increment/decrement operators as part of their output (#21137) (Thanks @MartinGC94!) +- Update `DnsNameList` for `X509Certificate2` to use `X509SubjectAlternativeNameExtension.EnumerateDnsNames` Method (#24714) (Thanks @ArmaanMcleod!) +- Add completion of modules by their shortname (#20330) (Thanks @MartinGC94!) +- Fix `Get-ItemProperty` to report non-terminating error for cast exception (#21115) (Thanks @ArmaanMcleod!) +- Add `-PropertyType` argument completer for `New-ItemProperty` (#21117) (Thanks @ArmaanMcleod!) +- Fix a bug in how `Write-Host` handles `XmlNode` object (#24669) (Thanks @brendandburns!) + +### Code Cleanup + +
+ + + +

We thank the following contributors!

+

@xtqqczze

+ +
+ +
    +
  • Seal ClientRemoteSessionDSHandlerImpl (#21218) (Thanks @xtqqczze!)
  • +
  • Seal internal type ClientRemoteSessionDSHandlerImpl (#24705) (Thanks @xtqqczze!)
  • +
  • Seal classes in RemotingProtocol2 (#21164) (Thanks @xtqqczze!)
  • +
+ +
+ +### Tools + +- Added Justin Chung as PowerShell team memeber on releaseTools.psm1 (#24672) + +### Tests + +- Skip CIM ETS member test on older Windows platforms (#24681) + +### Build and Packaging Improvements + +
+ + + +

Updated SDK to 9.0.101

+ +
+ +
    +
  • Update branch for release - Transitive - false - none (#24754)
  • +
  • Update Microsoft.PowerShell.PSResourceGet to 1.1.0 (#24767)
  • +
  • Add a parameter that skips verify packages step (#24763)
  • +
  • Make the AssemblyVersion not change for servicing releases (#24667)
  • +
  • Fixed release pipeline errors and switched to KS3 (#24751)
  • +
  • Update outdated package references (#24580)
  • +
  • Bump actions/upload-artifact from 4.4.3 to 4.5.0 (#24689)
  • +
  • Update .NET feed with new domain as azureedge is retiring (#24703)
  • +
  • Bump super-linter/super-linter from 7.2.0 to 7.2.1 (#24678)
  • +
  • Bump github/codeql-action from 3.27.7 to 3.27.9 (#24674)
  • +
  • Bump actions/dependency-review-action from 4.4.0 to 4.5.0 (#24607)
  • +
+ +
+ +### Documentation and Help Content + +- Update cmdlets WG members (#24275) (Thanks @kilasuit!) + +[7.6.0-preview.2]: https://github.com/PowerShell/PowerShell/compare/v7.6.0-preview.1...v7.6.0-preview.2 + +## [7.6.0-preview.1] - 2024-12-16 + +### Breaking Changes + +- Treat large Enum values as numbers in `ConvertTo-Json` (#20999) (Thanks @jborean93!) + +### General Cmdlet Updates and Fixes + +- Add proper error for running `Get-PSSession -ComputerName` on Unix (#21009) (Thanks @jborean93!) +- Resolve symbolic link target relative to the symbolic link instead of the working directory (#15235) (#20943) (Thanks @MatejKafka!) +- Fix up buffer management getting network roots (#24600) (Thanks @jborean93!) +- Support `PSObject` wrapped values in `ArgumentToEncodingTransformationAttribute` (#24555) (Thanks @jborean93!) +- Update PSReadLine to 2.3.6 (#24380) +- Add telemetry to track the use of features (#24247) +- Handle global tool specially when prepending `PSHome` to `PATH` (#24228) +- Fix how processor architecture is validated in `Import-Module` (#24265) +- Make features `PSCommandNotFoundSuggestion`, `PSCommandWithArgs`, and `PSModuleAutoLoadSkipOfflineFiles` stable (#24246) +- Write type data to the pipeline instead of collecting it (#24236) (Thanks @MartinGC94!) +- Add support to `Get-Error` to handle BoundParameters (#20640) +- Fix `Get-FormatData` to not cast a type incorrectly (#21157) +- Delay progress bar in `Copy-Item` and `Remove-Item` cmdlets (#24013) (Thanks @TheSpyGod!) +- Add `-Force` parameter to `Resolve-Path` and `Convert-Path` cmdlets to support wildcard hidden files (#20981) (Thanks @ArmaanMcleod!) +- Use host exe to determine `$PSHOME` location when `SMA.dll` location is not found (#24072) +- Fix `Test-ModuleManifest` so it can use a UNC path (#24115) + +### Code Cleanup + +
+ + + +

We thank the following contributors!

+

@eltociear, @JayBazuzi

+ +
+ +
    +
  • Fix typos in ShowModuleControl.xaml.cs (#24248) (Thanks @eltociear!)
  • +
  • Fix a typo in the build doc (#24172) (Thanks @JayBazuzi!)
  • +
+ +
+ +### Tools + +- Fix devcontainer extensions key (#24359) (Thanks @ThomasNieto!) +- Support new backport branch format (#24378) +- Update markdownLink.yml to not run on release branches (#24323) +- Remove old code that downloads msix for win-arm64 (#24175) + +### Tests + +- Fix cleanup in PSResourceGet test (#24339) + +### Build and Packaging Improvements + +
+ + + +

We thank the following contributors!

+

@MartinGC94, @jborean93, @xtqqczze, @alerickson, @iSazonov, @rzippo

+ +
+ +
    +
  • Deploy Box update (#24632)
  • +
  • Remove Regex use (#24235) (Thanks @MartinGC94!)
  • +
  • Improve cim ETS member inference completion (#24235) (Thanks @MartinGC94!)
  • +
  • Emit ProgressRecord in CLIXML minishell output (#21373) (Thanks @jborean93!)
  • +
  • Assign the value returned by the MaybeAdd method
  • (#24652) +
  • Add support for interface static abstract props (#21061) (Thanks @jborean93!)
  • +
  • Change call to optional add in the binder expression (#24451) (Thanks @jborean93!)
  • +
  • Turn off AMSI member invocation on nix release builds (#24451) (Thanks @jborean93!)
  • +
  • Bump github/codeql-action from 3.27.0 to 3.27.6 (#24639)
  • +
  • Update src/Microsoft.PowerShell.ConsoleHost/host/msh/ConsoleHost.cs (#24239) (Thanks @jborean93!)
  • +
  • Apply suggestions from code review (#24239) (Thanks @jborean93!)
  • +
  • Add remote runspace check for PushRunspace (#24239) (Thanks @jborean93!)
  • +
  • Set LangVersion compiler option to 13.0 (#24619) (Thanks @xtqqczze!)
  • +
  • Set LangVersion compiler option to 13.0 (#24617) (Thanks @xtqqczze!)
  • +
  • Update metadata.json for PowerShell 7.5 RC1 release (#24589)
  • +
  • Update nuget publish to use Deploy Box (#24596)
  • +
  • Added Deploy Box Product Pathway to GitHub Release and NuGet Release Pipelines (#24583)
  • +
  • Update machine pool for copy blob and upload buildinfo stage (#24587)
  • +
  • Bump .NET 9 and dependencies (#24573)
  • +
  • Bump actions/dependency-review-action from 4.3.4 to 4.4.0 (#24503)
  • +
  • Bump actions/checkout from 4.2.1 to 4.2.2 (#24488)
  • +
  • Bump agrc/reminder-action from 1.0.14 to 1.0.15 (#24384)
  • +
  • Bump actions/upload-artifact from 4.4.0 to 4.4.3 (#24410)
  • +
  • Update branch for release (#24534)
  • +
  • Revert "Update package references (#24414)" (#24532)
  • +
  • Add a way to use only NuGet feed sources (#24528)
  • +
  • Update PSResourceGet to v1.1.0-RC2 (#24512) (Thanks @alerickson!)
  • +
  • Bump .NET to 9.0.100-rc.2.24474.11 (#24509)
  • +
  • Fix seed max value for Container Linux CI (#24510)
  • +
  • Update metadata.json for 7.2.24 and 7.4.6 releases (#24484)
  • +
  • Download package from package build for generating vpack (#24481)
  • +
  • Keep the roff file when gzipping it. (#24450)
  • +
  • Delete the msix blob if it's already there (#24353)
  • +
  • Add PMC mapping for debian 12 (bookworm) (#24413)
  • +
  • Checkin generated manpage (#24423)
  • +
  • Add CodeQL scanning to APIScan build (#24303)
  • +
  • Update package references (#24414)
  • +
  • Update vpack pipeline (#24281)
  • +
  • Bring changes from v7.5.0-preview.5 Release Branch to Master (#24369)
  • +
  • Bump agrc/create-reminder-action from 1.1.15 to 1.1.16 (#24375)
  • +
  • Add BaseUrl to buildinfo json file (#24376)
  • +
  • Update metadata.json (#24352)
  • +
  • Copy to static site instead of making blob public (#24269)
  • +
  • Update Microsoft.PowerShell.PSResourceGet to 1.1.0-preview2 (#24300) (Thanks @alerickson!)
  • +
  • add updated libicu dependency for debian packages (#24301)
  • +
  • add mapping to azurelinux repo (#24290)
  • +
  • Remove the MD5 branch in the strong name signing token calculation (#24288)
  • +
  • Bump .NET 9 to 9.0.100-rc.1.24452.12 (#24273)
  • +
  • Ensure the official build files CodeQL issues (#24278)
  • +
  • Update experimental-feature json files (#24271)
  • +
  • Make some release tests run in a hosted pools (#24270)
  • +
  • Do not build the exe for Global tool shim project (#24263)
  • +
  • Update and add new NuGet package sources for different environments. (#24264)
  • +
  • Bump skitionek/notify-microsoft-teams (#24261)
  • +
  • Create new pipeline for compliance (#24252)
  • +
  • Capture environment better (#24148)
  • +
  • Add specific path for issues in tsaconfig (#24244)
  • +
  • Use Managed Identity for APIScan authentication (#24243)
  • +
  • Add windows signing for pwsh.exe (#24219)
  • +
  • Bump super-linter/super-linter from 7.0.0 to 7.1.0 (#24223)
  • +
  • Update the URLs used in nuget.config files (#24203)
  • +
  • Check Create and Submit in vPack build by default (#24181)
  • +
  • Replace PSVersion source generator with incremental one (#23815) (Thanks @iSazonov!)
  • +
  • Save man files in /usr/share/man instead of /usr/local/share/man (#23855) (Thanks @rzippo!)
  • +
  • Bump super-linter/super-linter from 6.8.0 to 7.0.0 (#24169)
  • +
+ +
+ +### Documentation and Help Content + +- Updated Third Party Notices (#24666) +- Update `HelpInfoUri` for 7.5 (#24610) +- Update changelog for v7.4.6 release (#24496) +- Update to the latest NOTICES file (#24259) +- Update the changelog `preview.md` (#24213) +- Update changelog readme with 7.4 (#24182) (Thanks @ThomasNieto!) +- Fix Markdown linting error (#24204) +- Updated changelog for v7.2.23 (#24196) (Internal 32131) +- Update changelog and `metadata.json` for v7.4.5 release (#24183) +- Bring 7.2 changelogs back to master (#24158) + +[7.6.0-preview.1]: https://github.com/PowerShell/PowerShell/compare/v7.5.0-rc.1...v7.6.0-preview.1 diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index dddfa22df2a..686e5e7a090 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -1,8 +1,10 @@ -# Code of Conduct +# Microsoft Open Source Code of Conduct -This project has adopted the [Microsoft Open Source Code of Conduct][conduct-code]. -For more information see the [Code of Conduct FAQ][conduct-FAQ] or contact [opencode@microsoft.com][conduct-email] with any additional questions or comments. +This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). -[conduct-code]: http://opensource.microsoft.com/codeofconduct/ -[conduct-FAQ]: http://opensource.microsoft.com/codeofconduct/faq/ -[conduct-email]: mailto:opencode@microsoft.com +Resources: + +- [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/) +- [Microsoft Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) +- Contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with questions or concerns +- Employees can reach out at [aka.ms/opensource/moderation-support](https://aka.ms/opensource/moderation-support) diff --git a/DotnetRuntimeMetadata.json b/DotnetRuntimeMetadata.json new file mode 100644 index 00000000000..1ae71bf0737 --- /dev/null +++ b/DotnetRuntimeMetadata.json @@ -0,0 +1,15 @@ +{ + "sdk": { + "channel": "9.0.1xx-preview6", + "quality": "daily", + "qualityFallback": "preview", + "packageVersionPattern": "9.0.0-preview.6", + "sdkImageVersion": "10.0.101", + "nextChannel": "9.0.0-preview.7", + "azureFeed": "", + "sdkImageOverride": "" + }, + "internalfeed": { + "url": "" + } +} diff --git a/LICENSE.txt b/LICENSE.txt index 1df45821db0..b2f52a2bad4 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,25 +1,21 @@ -PowerShell 6.0 - -Copyright (c) Microsoft Corporation - -All rights reserved. +Copyright (c) Microsoft Corporation. MIT License -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the ""Software""), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: -The above copyright notice and this permission notice shall be included in all +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. -THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/PowerShell.Common.props b/PowerShell.Common.props index 947f320217d..dfc16f830d7 100644 --- a/PowerShell.Common.props +++ b/PowerShell.Common.props @@ -1,20 +1,234 @@ + + + + + + + + + + + + + + ^((\d+).(\d+).(\d+))(-(\w+)(.(\d+))?)?$ + $([System.Text.RegularExpressions.Regex]::Match($(ReleaseTag), $(RegexReleaseTag)).Groups[1].Value) + $([System.Text.RegularExpressions.Regex]::Match($(ReleaseTag), $(RegexReleaseTag)).Groups[8].Value) + $([System.Text.RegularExpressions.Regex]::Match($(ReleaseTag), $(RegexReleaseTag)).Groups[6].Value) + + 100 + + 500 + $([MSBuild]::Add($(ReleaseTagSemVersionPart), $(RCIncrementValue))) + + $(ReleaseTag) + + $(ReleaseTagVersionPart).$(ReleaseTagSemVersionPart) + + $(ReleaseTagVersionPart).$(GAIncrementValue) + + $(PSCoreFileVersion) + $([System.Version]::Parse($(PSCoreFileVersion)).Major).$([System.Version]::Parse($(PSCoreFileVersion)).Minor).0.$([System.Version]::Parse($(PSCoreFileVersion)).Revision) + + + + ^v(.+)-(\d+)-g(.+) + $([System.Text.RegularExpressions.Regex]::Match($(PowerShellVersion), $(RegexGitVersion)).Groups[1].Value) + $([System.Text.RegularExpressions.Regex]::Match($(PowerShellVersion), $(RegexGitVersion)).Groups[1].Value) + $([System.Text.RegularExpressions.Regex]::Match($(PowerShellVersion), $(RegexGitVersion)).Groups[2].Value) + $([System.Text.RegularExpressions.Regex]::Match($(PowerShellVersion), $(RegexGitVersion)).Groups[3].Value) + + + $(PSCoreBuildVersion) SHA: $(PSCoreCommitSHA) + $(PSCoreBuildVersion) Commits: $(PSCoreAdditionalCommits) SHA: $(PSCoreCommitSHA) + + + + + $(PSCoreFileVersion) + $(PSCoreFormattedVersion) + $(PSCoreFormattedVersion) + + + $(PSCoreBuildVersion) + + ..\..\assets\Powershell_av_colors.ico + ..\..\assets\Powershell_avatar.ico + ..\..\assets\Powershell_black.ico + + + + + + + + + + - PowerShell Core + PowerShell Microsoft Corporation - (c) Microsoft Corporation. All rights reserved. + (c) Microsoft Corporation. + PowerShell 7 - 6.0.0 - netcoreapp2.0 - 2.0.0 + net10.0 + 13.0 true true true + true en-US + true + true true ../signing/visualstudiopublic.snk true + true + + + + $(DefineConstants);CORECLR + true + + + + + $(DefineConstants);UNIX + + + + + portable + + + + + + EnvironmentVariable;Global + false + false + + + + + Global + + + + + true + true + + + + AppLocal + + + + + true + true + + + + + true + portable + + + + + true + + full + + + + strict + + + + true diff --git a/PowerShell.sln b/PowerShell.sln new file mode 100644 index 00000000000..4938316281d --- /dev/null +++ b/PowerShell.sln @@ -0,0 +1,180 @@ +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +# https://github.com/dotnet/project-system/blob/master/docs/opening-with-new-project-system.md#project-type-guids +VisualStudioVersion = 15.0.26730.12 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "powershell-win-core", "src\powershell-win-core\powershell-win-core.csproj", "{8359D422-E0C4-4A0D-94EB-3C9DD16B7932}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.Management.Automation", "src\System.Management.Automation\System.Management.Automation.csproj", "{AF660EE7-0183-4B79-A93F-221B6AC1C24B}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.PowerShell.Commands.Utility", "src\Microsoft.PowerShell.Commands.Utility\Microsoft.PowerShell.Commands.Utility.csproj", "{EAB203E1-2A68-4166-BE54-5C44DE825229}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.PowerShell.CoreCLR.Eventing", "src\Microsoft.PowerShell.CoreCLR.Eventing\Microsoft.PowerShell.CoreCLR.Eventing.csproj", "{981D3972-343D-4E17-935B-037E1C622771}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.PowerShell.ConsoleHost", "src\Microsoft.PowerShell.ConsoleHost\Microsoft.PowerShell.ConsoleHost.csproj", "{8FFE645D-F0C9-4220-9A88-83062ED211D2}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.PowerShell.Commands.Management", "src\Microsoft.PowerShell.Commands.Management\Microsoft.PowerShell.Commands.Management.csproj", "{FCE53A5E-5FAC-48BE-BAD8-2110040B5C2E}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.PowerShell.SDK", "src\Microsoft.PowerShell.SDK\Microsoft.PowerShell.SDK.csproj", "{4BC19063-1F66-467B-87DE-80449C72BCD6}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.Management.Infrastructure.CimCmdlets", "src\Microsoft.Management.Infrastructure.CimCmdlets\Microsoft.Management.Infrastructure.CimCmdlets.csproj", "{131A8527-92D7-468F-822D-5354229A865C}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.PowerShell.Commands.Diagnostics", "src\Microsoft.PowerShell.Commands.Diagnostics\Microsoft.PowerShell.Commands.Diagnostics.csproj", "{439A24FC-8E0A-48B6-8227-44C297311F49}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.WSMan.Management", "src\Microsoft.WSMan.Management\Microsoft.WSMan.Management.csproj", "{8F63D134-E413-4181-936D-D82F3F5F1D85}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.PowerShell.Security", "src\Microsoft.PowerShell.Security\Microsoft.PowerShell.Security.csproj", "{C4F81816-C87A-4ABF-8A37-24AC16A0A6CF}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.WSMan.Runtime", "src\Microsoft.WSMan.Runtime\Microsoft.WSMan.Runtime.csproj", "{D9CCCB67-4EBE-4854-AB52-C0129DC5BAE4}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "powershell-unix", "src\powershell-unix\powershell-unix.csproj", "{73EA0BE6-C0C5-4B56-A5AA-DADA4C01D690}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "xUnit.tests", "test\xUnit\xUnit.tests.csproj", "{08704934-9764-48CE-86DB-BCF0A1CF7899}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PSVersionInfoGenerator", "src\System.Management.Automation\SourceGenerators\PSVersionInfoGenerator\PSVersionInfoGenerator.csproj", "{B22424E8-0516-4FC3-A9CB-D84D15EF0589}" +EndProject +# Configuration mapping comment +# All global configurations must be mapped to project configurations +# +# 4BC19063-1F66-467B-87DE-80449C72BCD6 - Microsoft.PowerShell.SDK +# 8359D422-E0C4-4A0D-94EB-3C9DD16B7932 - PowerShell-Win +# Linux is invalid and mapped to Release +# +# 73EA0BE6-C0C5-4B56-A5AA-DADA4C01D690 - powershell-unix +# Only Linux is valid, all configurations mapped to Linux +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + CodeCoverage|Any CPU = CodeCoverage|Any CPU + Debug|Any CPU = Debug|Any CPU + Linux|Any CPU = Linux|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {8359D422-E0C4-4A0D-94EB-3C9DD16B7932}.CodeCoverage|Any CPU.ActiveCfg = CodeCoverage|Any CPU + {8359D422-E0C4-4A0D-94EB-3C9DD16B7932}.CodeCoverage|Any CPU.Build.0 = CodeCoverage|Any CPU + {8359D422-E0C4-4A0D-94EB-3C9DD16B7932}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8359D422-E0C4-4A0D-94EB-3C9DD16B7932}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8359D422-E0C4-4A0D-94EB-3C9DD16B7932}.Linux|Any CPU.ActiveCfg = Release|Any CPU + {8359D422-E0C4-4A0D-94EB-3C9DD16B7932}.Linux|Any CPU.Build.0 = Release|Any CPU + {8359D422-E0C4-4A0D-94EB-3C9DD16B7932}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8359D422-E0C4-4A0D-94EB-3C9DD16B7932}.Release|Any CPU.Build.0 = Release|Any CPU + {AF660EE7-0183-4B79-A93F-221B6AC1C24B}.CodeCoverage|Any CPU.ActiveCfg = CodeCoverage|Any CPU + {AF660EE7-0183-4B79-A93F-221B6AC1C24B}.CodeCoverage|Any CPU.Build.0 = CodeCoverage|Any CPU + {AF660EE7-0183-4B79-A93F-221B6AC1C24B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AF660EE7-0183-4B79-A93F-221B6AC1C24B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AF660EE7-0183-4B79-A93F-221B6AC1C24B}.Linux|Any CPU.ActiveCfg = Linux|Any CPU + {AF660EE7-0183-4B79-A93F-221B6AC1C24B}.Linux|Any CPU.Build.0 = Linux|Any CPU + {AF660EE7-0183-4B79-A93F-221B6AC1C24B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AF660EE7-0183-4B79-A93F-221B6AC1C24B}.Release|Any CPU.Build.0 = Release|Any CPU + {EAB203E1-2A68-4166-BE54-5C44DE825229}.CodeCoverage|Any CPU.ActiveCfg = CodeCoverage|Any CPU + {EAB203E1-2A68-4166-BE54-5C44DE825229}.CodeCoverage|Any CPU.Build.0 = CodeCoverage|Any CPU + {EAB203E1-2A68-4166-BE54-5C44DE825229}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EAB203E1-2A68-4166-BE54-5C44DE825229}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EAB203E1-2A68-4166-BE54-5C44DE825229}.Linux|Any CPU.ActiveCfg = Linux|Any CPU + {EAB203E1-2A68-4166-BE54-5C44DE825229}.Linux|Any CPU.Build.0 = Linux|Any CPU + {EAB203E1-2A68-4166-BE54-5C44DE825229}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EAB203E1-2A68-4166-BE54-5C44DE825229}.Release|Any CPU.Build.0 = Release|Any CPU + {981D3972-343D-4E17-935B-037E1C622771}.CodeCoverage|Any CPU.ActiveCfg = CodeCoverage|Any CPU + {981D3972-343D-4E17-935B-037E1C622771}.CodeCoverage|Any CPU.Build.0 = CodeCoverage|Any CPU + {981D3972-343D-4E17-935B-037E1C622771}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {981D3972-343D-4E17-935B-037E1C622771}.Debug|Any CPU.Build.0 = Debug|Any CPU + {981D3972-343D-4E17-935B-037E1C622771}.Linux|Any CPU.ActiveCfg = Linux|Any CPU + {981D3972-343D-4E17-935B-037E1C622771}.Linux|Any CPU.Build.0 = Linux|Any CPU + {981D3972-343D-4E17-935B-037E1C622771}.Release|Any CPU.ActiveCfg = Release|Any CPU + {981D3972-343D-4E17-935B-037E1C622771}.Release|Any CPU.Build.0 = Release|Any CPU + {8FFE645D-F0C9-4220-9A88-83062ED211D2}.CodeCoverage|Any CPU.ActiveCfg = CodeCoverage|Any CPU + {8FFE645D-F0C9-4220-9A88-83062ED211D2}.CodeCoverage|Any CPU.Build.0 = CodeCoverage|Any CPU + {8FFE645D-F0C9-4220-9A88-83062ED211D2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8FFE645D-F0C9-4220-9A88-83062ED211D2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8FFE645D-F0C9-4220-9A88-83062ED211D2}.Linux|Any CPU.ActiveCfg = Linux|Any CPU + {8FFE645D-F0C9-4220-9A88-83062ED211D2}.Linux|Any CPU.Build.0 = Linux|Any CPU + {8FFE645D-F0C9-4220-9A88-83062ED211D2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8FFE645D-F0C9-4220-9A88-83062ED211D2}.Release|Any CPU.Build.0 = Release|Any CPU + {FCE53A5E-5FAC-48BE-BAD8-2110040B5C2E}.CodeCoverage|Any CPU.ActiveCfg = CodeCoverage|Any CPU + {FCE53A5E-5FAC-48BE-BAD8-2110040B5C2E}.CodeCoverage|Any CPU.Build.0 = CodeCoverage|Any CPU + {FCE53A5E-5FAC-48BE-BAD8-2110040B5C2E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {FCE53A5E-5FAC-48BE-BAD8-2110040B5C2E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {FCE53A5E-5FAC-48BE-BAD8-2110040B5C2E}.Linux|Any CPU.ActiveCfg = Linux|Any CPU + {FCE53A5E-5FAC-48BE-BAD8-2110040B5C2E}.Linux|Any CPU.Build.0 = Linux|Any CPU + {FCE53A5E-5FAC-48BE-BAD8-2110040B5C2E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {FCE53A5E-5FAC-48BE-BAD8-2110040B5C2E}.Release|Any CPU.Build.0 = Release|Any CPU + {4BC19063-1F66-467B-87DE-80449C72BCD6}.CodeCoverage|Any CPU.ActiveCfg = Release|Any CPU + {4BC19063-1F66-467B-87DE-80449C72BCD6}.CodeCoverage|Any CPU.Build.0 = Release|Any CPU + {4BC19063-1F66-467B-87DE-80449C72BCD6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4BC19063-1F66-467B-87DE-80449C72BCD6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4BC19063-1F66-467B-87DE-80449C72BCD6}.Linux|Any CPU.ActiveCfg = Release|Any CPU + {4BC19063-1F66-467B-87DE-80449C72BCD6}.Linux|Any CPU.Build.0 = Release|Any CPU + {4BC19063-1F66-467B-87DE-80449C72BCD6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4BC19063-1F66-467B-87DE-80449C72BCD6}.Release|Any CPU.Build.0 = Release|Any CPU + {131A8527-92D7-468F-822D-5354229A865C}.CodeCoverage|Any CPU.ActiveCfg = Release|Any CPU + {131A8527-92D7-468F-822D-5354229A865C}.CodeCoverage|Any CPU.Build.0 = Release|Any CPU + {131A8527-92D7-468F-822D-5354229A865C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {131A8527-92D7-468F-822D-5354229A865C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {131A8527-92D7-468F-822D-5354229A865C}.Linux|Any CPU.ActiveCfg = Release|Any CPU + {131A8527-92D7-468F-822D-5354229A865C}.Linux|Any CPU.Build.0 = Release|Any CPU + {131A8527-92D7-468F-822D-5354229A865C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {131A8527-92D7-468F-822D-5354229A865C}.Release|Any CPU.Build.0 = Release|Any CPU + {439A24FC-8E0A-48B6-8227-44C297311F49}.CodeCoverage|Any CPU.ActiveCfg = CodeCoverage|Any CPU + {439A24FC-8E0A-48B6-8227-44C297311F49}.CodeCoverage|Any CPU.Build.0 = CodeCoverage|Any CPU + {439A24FC-8E0A-48B6-8227-44C297311F49}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {439A24FC-8E0A-48B6-8227-44C297311F49}.Debug|Any CPU.Build.0 = Debug|Any CPU + {439A24FC-8E0A-48B6-8227-44C297311F49}.Linux|Any CPU.ActiveCfg = Linux|Any CPU + {439A24FC-8E0A-48B6-8227-44C297311F49}.Linux|Any CPU.Build.0 = Linux|Any CPU + {439A24FC-8E0A-48B6-8227-44C297311F49}.Release|Any CPU.ActiveCfg = Release|Any CPU + {439A24FC-8E0A-48B6-8227-44C297311F49}.Release|Any CPU.Build.0 = Release|Any CPU + {8F63D134-E413-4181-936D-D82F3F5F1D85}.CodeCoverage|Any CPU.ActiveCfg = CodeCoverage|Any CPU + {8F63D134-E413-4181-936D-D82F3F5F1D85}.CodeCoverage|Any CPU.Build.0 = CodeCoverage|Any CPU + {8F63D134-E413-4181-936D-D82F3F5F1D85}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8F63D134-E413-4181-936D-D82F3F5F1D85}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8F63D134-E413-4181-936D-D82F3F5F1D85}.Linux|Any CPU.ActiveCfg = Linux|Any CPU + {8F63D134-E413-4181-936D-D82F3F5F1D85}.Linux|Any CPU.Build.0 = Linux|Any CPU + {8F63D134-E413-4181-936D-D82F3F5F1D85}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8F63D134-E413-4181-936D-D82F3F5F1D85}.Release|Any CPU.Build.0 = Release|Any CPU + {C4F81816-C87A-4ABF-8A37-24AC16A0A6CF}.CodeCoverage|Any CPU.ActiveCfg = CodeCoverage|Any CPU + {C4F81816-C87A-4ABF-8A37-24AC16A0A6CF}.CodeCoverage|Any CPU.Build.0 = CodeCoverage|Any CPU + {C4F81816-C87A-4ABF-8A37-24AC16A0A6CF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C4F81816-C87A-4ABF-8A37-24AC16A0A6CF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C4F81816-C87A-4ABF-8A37-24AC16A0A6CF}.Linux|Any CPU.ActiveCfg = Linux|Any CPU + {C4F81816-C87A-4ABF-8A37-24AC16A0A6CF}.Linux|Any CPU.Build.0 = Linux|Any CPU + {C4F81816-C87A-4ABF-8A37-24AC16A0A6CF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C4F81816-C87A-4ABF-8A37-24AC16A0A6CF}.Release|Any CPU.Build.0 = Release|Any CPU + {D9CCCB67-4EBE-4854-AB52-C0129DC5BAE4}.CodeCoverage|Any CPU.ActiveCfg = CodeCoverage|Any CPU + {D9CCCB67-4EBE-4854-AB52-C0129DC5BAE4}.CodeCoverage|Any CPU.Build.0 = CodeCoverage|Any CPU + {D9CCCB67-4EBE-4854-AB52-C0129DC5BAE4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D9CCCB67-4EBE-4854-AB52-C0129DC5BAE4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D9CCCB67-4EBE-4854-AB52-C0129DC5BAE4}.Linux|Any CPU.ActiveCfg = Linux|Any CPU + {D9CCCB67-4EBE-4854-AB52-C0129DC5BAE4}.Linux|Any CPU.Build.0 = Linux|Any CPU + {D9CCCB67-4EBE-4854-AB52-C0129DC5BAE4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D9CCCB67-4EBE-4854-AB52-C0129DC5BAE4}.Release|Any CPU.Build.0 = Release|Any CPU + {73EA0BE6-C0C5-4B56-A5AA-DADA4C01D690}.Linux|Any CPU.ActiveCfg = Linux|Any CPU + {73EA0BE6-C0C5-4B56-A5AA-DADA4C01D690}.Linux|Any CPU.Build.0 = Linux|Any CPU + {73EA0BE6-C0C5-4B56-A5AA-DADA4C01D690}.Release|Any CPU.ActiveCfg = Release|Any CPU + {73EA0BE6-C0C5-4B56-A5AA-DADA4C01D690}.Release|Any CPU.Build.0 = Release|Any CPU + {73EA0BE6-C0C5-4B56-A5AA-DADA4C01D690}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {73EA0BE6-C0C5-4B56-A5AA-DADA4C01D690}.Debug|Any CPU.Build.0 = Debug|Any CPU + {73EA0BE6-C0C5-4B56-A5AA-DADA4C01D690}.CodeCoverage|Any CPU.ActiveCfg = CodeCoverage|Any CPU + {73EA0BE6-C0C5-4B56-A5AA-DADA4C01D690}.CodeCoverage|Any CPU.Build.0 = CodeCoverage|Any CPU + {43D4F8DA-A7DE-494B-81B0-BDE3CFD7B1F1}.CodeCoverage|Any CPU.ActiveCfg = CodeCoverage|Any CPU + {43D4F8DA-A7DE-494B-81B0-BDE3CFD7B1F1}.CodeCoverage|Any CPU.Build.0 = CodeCoverage|Any CPU + {43D4F8DA-A7DE-494B-81B0-BDE3CFD7B1F1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {43D4F8DA-A7DE-494B-81B0-BDE3CFD7B1F1}.Debug|Any CPU.Build.0 = Debug|Any CPU + {43D4F8DA-A7DE-494B-81B0-BDE3CFD7B1F1}.Linux|Any CPU.ActiveCfg = Linux|Any CPU + {43D4F8DA-A7DE-494B-81B0-BDE3CFD7B1F1}.Linux|Any CPU.Build.0 = Linux|Any CPU + {43D4F8DA-A7DE-494B-81B0-BDE3CFD7B1F1}.Release|Any CPU.ActiveCfg = Release|Any CPU + {43D4F8DA-A7DE-494B-81B0-BDE3CFD7B1F1}.Release|Any CPU.Build.0 = Release|Any CPU + {08704934-9764-48CE-86DB-BCF0A1CF7899}.CodeCoverage|Any CPU.ActiveCfg = CodeCoverage|Any CPU + {08704934-9764-48CE-86DB-BCF0A1CF7899}.CodeCoverage|Any CPU.Build.0 = CodeCoverage|Any CPU + {08704934-9764-48CE-86DB-BCF0A1CF7899}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {08704934-9764-48CE-86DB-BCF0A1CF7899}.Debug|Any CPU.Build.0 = Debug|Any CPU + {08704934-9764-48CE-86DB-BCF0A1CF7899}.Linux|Any CPU.ActiveCfg = Linux|Any CPU + {08704934-9764-48CE-86DB-BCF0A1CF7899}.Linux|Any CPU.Build.0 = Linux|Any CPU + {08704934-9764-48CE-86DB-BCF0A1CF7899}.Release|Any CPU.ActiveCfg = Release|Any CPU + {08704934-9764-48CE-86DB-BCF0A1CF7899}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {9128A855-8499-43C0-9C7C-08ECC47768B0} + EndGlobalSection +EndGlobal diff --git a/README.md b/README.md index 1db20db6468..a7b31c475f8 100644 --- a/README.md +++ b/README.md @@ -1,198 +1,135 @@ # ![logo][] PowerShell Welcome to the PowerShell GitHub Community! -PowerShell Core is a cross-platform (Windows, Linux, and macOS) automation and configuration tool/framework that works well with your existing tools and is optimized +[PowerShell](https://learn.microsoft.com/powershell/scripting/overview) is a cross-platform (Windows, Linux, and macOS) automation and configuration tool/framework that works well with your existing tools and is optimized for dealing with structured data (e.g. JSON, CSV, XML, etc.), REST APIs, and object models. -It includes a command-line shell, an associated scripting language and a framework for processing cmdlets. +It includes a command-line shell, an associated scripting language, and a framework for processing cmdlets. -[logo]: assets/Powershell_64.png +[logo]: assets/ps_black_64.svg?sanitize=true -## Windows PowerShell vs PowerShell Core +## Windows PowerShell vs. PowerShell 7+ -Although this repo started as a fork of the Windows PowerShell code base, changes made in this repo do not make their way back to Windows PowerShell 5.1 automatically. -This also means that issues tracked here are only for PowerShell Core 6.0. -Windows PowerShell specific issues should be opened on [UserVoice][]. +Although this repository started as a fork of the Windows PowerShell codebase, changes made in this repository are not ported back to Windows PowerShell 5.1. +This also means that [issues tracked here][issues] are only for PowerShell 7.x and higher. +Windows PowerShell specific issues should be reported with the [Feedback Hub app][feedback-hub], by choosing "Apps > PowerShell" in the category. -[UserVoice]: https://windowsserver.uservoice.com/forums/301869-powershell +[issues]: https://github.com/PowerShell/PowerShell/issues +[feedback-hub]: https://support.microsoft.com/windows/send-feedback-to-microsoft-with-the-feedback-hub-app-f59187f8-8739-22d6-ba93-f66612949332 ## New to PowerShell? -If you are new to PowerShell and would like to learn more, we recommend reviewing the [getting started][] documentation. +If you are new to PowerShell and want to learn more, we recommend reviewing the [getting started][] documentation. -[getting started]: docs/learning-powershell +[getting started]: https://learn.microsoft.com/powershell/scripting/learn/more-powershell-learning ## Get PowerShell -You can download and install a PowerShell package for any of the following platforms. - -| Platform | Downloads | How to Install | -| ---------------------------------- | ---------------------- | ----------------------------- | -| Windows 10 / Server 2016 (x64) | [.msi][rl-windows10] | [Instructions][in-windows] | -| Windows 8.1 / Server 2012 R2 (x64) | [.msi][rl-windows81] | [Instructions][in-windows] | -| Windows 7 / Server 2008 R2 (x64) | [.msi][rl-windows7-64] | [Instructions][in-windows] | -| Windows 7 (x86) | [.msi][rl-windows7-86] | [Instructions][in-windows] | -| Ubuntu 16.04 | [.deb][rl-ubuntu16] | [Instructions][in-ubuntu16] | -| Ubuntu 14.04 | [.deb][rl-ubuntu14] | [Instructions][in-ubuntu14] | -| Debian 8 | [.deb][rl-ubuntu14] | [Instructions][in-deb8] | -| CentOS 7 | [.rpm][rl-centos] | [Instructions][in-centos] | -| Red Hat Enterprise Linux 7 | [.rpm][rl-centos] | [Instructions][in-rhel7] | -| OpenSUSE 42.1 | [.rpm][rl-opensuse421] | [Instructions][in-opensuse421]| -| Arch Linux | | [Instructions][in-archlinux] | -| Many Linux distributions | [.AppImage][rl-ai] | [Instructions][in-appimage] | -| macOS 10.12 | [.pkg][rl-macos] | [Instructions][in-macos] | -| Docker | | [Instructions][in-docker] | -| Kali Linux | [.deb][rl-ubuntu16] | [Instructions][in-kali] - -[rl-windows10]: https://github.com/PowerShell/PowerShell/releases/download/v6.0.0-beta.6/PowerShell-6.0.0-beta.6-win10-win2016-x64.msi -[rl-windows81]: https://github.com/PowerShell/PowerShell/releases/download/v6.0.0-beta.6/PowerShell-6.0.0-beta.6-win81-win2012r2-x64.msi -[rl-windows7-64]: https://github.com/PowerShell/PowerShell/releases/download/v6.0.0-beta.6/PowerShell-6.0.0-beta.6-win7-win2008r2-x64.msi -[rl-windows7-86]: https://github.com/PowerShell/PowerShell/releases/download/v6.0.0-beta.6/PowerShell-6.0.0-beta.6-win7-x86.msi -[rl-ubuntu16]: https://github.com/PowerShell/PowerShell/releases/download/v6.0.0-beta.6/powershell_6.0.0-beta.6-1ubuntu1.16.04.1_amd64.deb -[rl-ubuntu14]: https://github.com/PowerShell/PowerShell/releases/download/v6.0.0-beta.6/powershell_6.0.0-beta.6-1ubuntu1.14.04.1_amd64.deb -[rl-centos]: https://github.com/PowerShell/PowerShell/releases/download/v6.0.0-beta.6/powershell-6.0.0_beta.6-1.el7.x86_64.rpm -[rl-ai]: https://github.com/PowerShell/PowerShell/releases/download/v6.0.0-beta.6/PowerShell-6.0.0-beta.6-x86_64.AppImage -[rl-macos]: https://github.com/PowerShell/PowerShell/releases/download/v6.0.0-beta.6/powershell-6.0.0-beta.6-osx.10.12-x64.pkg -[rl-opensuse421]: https://github.com/PowerShell/PowerShell/releases/download/v6.0.0-beta.6/powershell-6.0.0_beta.6-1.suse.42.1.x86_64.rpm - -[installation]: docs/installation -[in-windows]: docs/installation/windows.md#msi -[in-ubuntu14]: docs/installation/linux.md#ubuntu-1404 -[in-ubuntu16]: docs/installation/linux.md#ubuntu-1604 -[in-deb8]: docs/installation/linux.md#debian-8 -[in-centos]: docs/installation/linux.md#centos-7 -[in-rhel7]: docs/installation/linux.md#red-hat-enterprise-linux-rhel-7 -[in-archlinux]: docs/installation/linux.md#arch-linux -[in-appimage]: docs/installation/linux.md#linux-appimage -[in-macos]: docs/installation/linux.md#macos-1012 -[in-docker]: docker -[in-opensuse421]: docs/installation/linux.md#opensuse-421 -[in-kali]: docs/installation/linux.md#kali - -To install a specific version, visit [releases](https://github.com/PowerShell/PowerShell/releases). +PowerShell is supported on Windows, macOS, and a variety of Linux platforms. For +more information, see [Installing PowerShell](https://learn.microsoft.com/powershell/scripting/install/installing-powershell). + +## Upgrading PowerShell + +For best results when upgrading, you should use the same install method you used when you first +installed PowerShell. The update method is different for each platform and install method. ## Community Dashboard -[Dashboard](https://aka.ms/psgithubbi) with visualizations for community contributions and project status using PowerShell, Azure, and PowerBI. +[Dashboard](https://aka.ms/PSPublicDashboard) with visualizations for community contributions and project status using PowerShell, Azure, and PowerBI. -For more information on how and why we built this dashboard, check out this [blog post](https://blogs.msdn.microsoft.com/powershell/2017/01/31/powershell-open-source-community-dashboard/). +For more information on how and why we built this dashboard, check out this [blog post](https://devblogs.microsoft.com/powershell/powershell-open-source-community-dashboard/). -## Chat Room +## Discussions -Want to chat with other members of the PowerShell community? +[GitHub Discussions](https://docs.github.com/discussions/quickstart) is a feature to enable free and open discussions within the community +for topics that are not related to code, unlike issues. -We have a Gitter Room which you can join below. +This is an experiment we are trying in our repositories, to see if it helps move discussions out of issues so that issues remain actionable by the team or members of the community. +There should be no expectation that PowerShell team members are regular participants in these discussions. +Individual PowerShell team members may choose to participate in discussions, but the expectation is that community members help drive discussions so that team members +can focus on issues. -[![Join the chat at https://gitter.im/PowerShell/PowerShell](https://badges.gitter.im/PowerShell/PowerShell.svg)](https://gitter.im/PowerShell/PowerShell?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) +Create or join a [discussion](https://github.com/PowerShell/PowerShell/discussions). -There is also the community driven PowerShell Slack Team which you can sign up for at [Slack Sign up]. +## Chat -[Slack Sign up]: http://slack.poshcode.org +Want to chat with other members of the PowerShell community? -## Add-ons and libraries +There are dozens of topic-specific channels on our community-driven PowerShell Virtual User Group, which you can join on: -[Awesome PowerShell](https://github.com/janikvonrotz/awesome-powershell) is a great curated list of add-ons and resources. +* [Discord](https://discord.gg/PowerShell) +* [IRC](https://web.libera.chat/#powershell) on Libera.Chat +* [Slack](https://aka.ms/psslack) -## Building the Repository +## Developing and Contributing -| Linux | Windows | macOS | -|--------------------------|----------------------------|------------------------| -| [Instructions][bd-linux] | [Instructions][bd-windows] | [Instructions][bd-macOS] | +Want to contribute to PowerShell? Please start with the [Contribution Guide][] to learn how to develop and contribute. + +If you are developing .NET Core C# applications targeting PowerShell Core, [check out our FAQ][] to learn more about the PowerShell SDK NuGet package. -If you have any problems building, please consult the developer [FAQ][]. +Also, make sure to check out our [PowerShell-RFC repository](https://github.com/powershell/powershell-rfc) for request-for-comments (RFC) documents to submit and give comments on proposed and future designs. -### Build status of master branches +[Contribution Guide]: .github/CONTRIBUTING.md +[check out our FAQ]: docs/FAQ.md#where-do-i-get-the-powershell-core-sdk-package -| AppVeyor (Windows) | Travis CI (Linux / macOS) | -|--------------------------|--------------------------| -| [![av-image][]][av-site] | [![tv-image][]][tv-site] | +## Building PowerShell -### Build status of nightly builds +| Linux | Windows | macOS | +|--------------------------|----------------------------|------------------------| +| [Instructions][bd-linux] | [Instructions][bd-windows] | [Instructions][bd-macOS] | -| AppVeyor (Windows) | Travis CI (Linux / macOS) | Code Coverage Status | -|--------------------------|---------------------------|----------------------| -| [![av-nightly-image][]][av-nightly-site] | [![tv-nightly-image][]][tv-site] | [![cc-image][]][cc-site] | +If you have any problems building PowerShell, please start by consulting the developer [FAQ]. [bd-linux]: docs/building/linux.md [bd-windows]: docs/building/windows-core.md [bd-macOS]: docs/building/macos.md - [FAQ]: docs/FAQ.md -[tv-image]: https://travis-ci.org/PowerShell/PowerShell.svg?branch=master -[tv-site]: https://travis-ci.org/PowerShell/PowerShell/branches -[av-image]: https://ci.appveyor.com/api/projects/status/nsng9iobwa895f98/branch/master?svg=true -[av-site]: https://ci.appveyor.com/project/PowerShell/powershell -[tv-nightly-image]: https://jimtru1979.blob.core.windows.net/badges/DailyBuildStatus.svg -[av-nightly-image]: https://ci.appveyor.com/api/projects/status/46yd4jogtm2jodcq?svg=true -[av-nightly-site]: https://ci.appveyor.com/project/PowerShell/powershell-f975h -[cc-site]: https://codecov.io/gh/PowerShell/PowerShell -[cc-image]: https://codecov.io/gh/PowerShell/PowerShell/branch/master/graph/badge.svg - ## Downloading the Source Code -The PowerShell repository has a number of other repositories embedded as submodules. - -To make things easy, you can just clone recursively: - -```sh -git clone --recursive https://github.com/PowerShell/PowerShell.git -``` - -If you already cloned but forgot to use `--recursive`, you can update submodules manually: +You can clone the repository: ```sh -git submodule update --init +git clone https://github.com/PowerShell/PowerShell.git ``` -See [working with the PowerShell repository](docs/git) for more information. +For more information, see [working with the PowerShell repository](https://github.com/PowerShell/PowerShell/tree/master/docs/git). -## Developing and Contributing - -Please see the [Contribution Guide][] for how to develop and contribute. +## Support -If you have any problems, please consult the [known issues][], developer [FAQ][], and [GitHub issues][]. -If you do not see your problem captured, please file a [new issue][] and follow the provided template. -If you are developing .NET Core C# applications targeting PowerShell Core, please [check out our FAQ][] to learn more about the PowerShell SDK NuGet package. +For support, see the [Support Section][]. -Also make sure to check out our [PowerShell-RFC repository](https://github.com/powershell/powershell-rfc) for request-for-comments (RFC) documents to submit and give comments on proposed and future designs. - -[check out our FAQ]: docs/FAQ.md#where-do-i-get-the-powershell-core-sdk-package -[Contribution Guide]: .github/CONTRIBUTING.md -[known issues]: docs/KNOWNISSUES.md -[GitHub issues]: https://github.com/PowerShell/PowerShell/issues -[new issue]:https://github.com/PowerShell/PowerShell/issues/new +[Support Section]: https://github.com/PowerShell/PowerShell/tree/master/.github/SUPPORT.md ## Legal and Licensing PowerShell is licensed under the [MIT license][]. -[MIT license]: LICENSE.txt +[MIT license]: https://github.com/PowerShell/PowerShell/tree/master/LICENSE.txt + +### Docker Containers -### Windows Docker Files and Images +> [!Important] +> The PowerShell container images are now [maintained by the .NET team](https://github.com/PowerShell/Announcements/issues/75). The containers at `mcr.microsoft.com/powershell` are currently not maintained. -License: By requesting and using the Container OS Image for Windows containers, you acknowledge, understand, and consent to the Supplemental License Terms available on Docker hub: +License: By requesting and using the Container OS Image for Windows containers, you acknowledge, understand, and consent to the Supplemental License Terms available on [Microsoft Artifact Registry][mcr]. -- [Window Server Core](https://hub.docker.com/r/microsoft/windowsservercore/) -- [Nano Server](https://hub.docker.com/r/microsoft/nanoserver/) +[mcr]: https://mcr.microsoft.com/en-us/product/powershell/tags ### Telemetry -By default, PowerShell collects the OS description and the version of PowerShell (equivalent to `$PSVersionTable.OS` and `$PSVersionTable.GitCommitId`) using [Application Insights](https://azure.microsoft.com/en-us/services/application-insights/). -To opt-out of sending telemetry, delete the file `DELETE_ME_TO_DISABLE_CONSOLEHOST_TELEMETRY` before starting PowerShell from the installed location. -The telemetry we collect fall under the [Microsoft Privacy Statement](https://privacy.microsoft.com/en-us/privacystatement/). +Please visit our [about_Telemetry](https://learn.microsoft.com/powershell/module/microsoft.powershell.core/about/about_telemetry) +topic to read details about telemetry gathered by PowerShell. ## Governance -Governance policy for PowerShell project is described [here][]. +The governance policy for the PowerShell project is described the [PowerShell Governance][gov] document. + +[gov]: https://github.com/PowerShell/PowerShell/blob/master/docs/community/governance.md -[here]: https://github.com/PowerShell/PowerShell/blob/master/docs/community/governance.md +## [Code of Conduct](CODE_OF_CONDUCT.md) -## [Code of Conduct][conduct-md] +Please see our [Code of Conduct](CODE_OF_CONDUCT.md) before participating in this project. -This project has adopted the [Microsoft Open Source Code of Conduct][conduct-code]. -For more information see the [Code of Conduct FAQ][conduct-FAQ] or contact [opencode@microsoft.com][conduct-email] with any additional questions or comments. +## [Security Policy](.github/SECURITY.md) -[conduct-code]: http://opensource.microsoft.com/codeofconduct/ -[conduct-FAQ]: http://opensource.microsoft.com/codeofconduct/faq/ -[conduct-email]: mailto:opencode@microsoft.com -[conduct-md]: ./CODE_OF_CONDUCT.md +For any security issues, please see our [Security Policy](.github/SECURITY.md). diff --git a/Settings.StyleCop b/Settings.StyleCop new file mode 100644 index 00000000000..e10c02bdd12 --- /dev/null +++ b/Settings.StyleCop @@ -0,0 +1,220 @@ + + + + + + + False + + + + + False + + + + + + False + + + + + + False + + + + + + + + + + False + + + + + False + + + + + + + + + + False + + + + + False + + + + + True + + + + + True + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + False + + + + + True + True + + + + + + + False + + + + + + False + + + + + + False + + + + + + False + + + + + False + + + + + + as + at + by + do + go + if + in + is + it + no + of + on + or + to + n + r + l + i + io + fs + lp + dw + h + rs + ps + op + my + sb + vt + + + + + + + + False + + + + + False + + + + + False + + + + + False + + + + + + False + + + + + + + + + + + False + + + + + + False + + + + + + + diff --git a/ThirdPartyNotices.txt b/ThirdPartyNotices.txt index 971899c4e05..8abff24592f 100644 --- a/ThirdPartyNotices.txt +++ b/ThirdPartyNotices.txt @@ -1,15 +1,4992 @@ +NOTICES AND INFORMATION +Do Not Translate or Localize + +This software incorporates material from third parties. +Microsoft makes certain open source code available at https://3rdpartysource.microsoft.com, +or you may send a check or money order for US $5.00, including the product name, +the open source component name, platform, and version number, to: + +Source Code Compliance Team +Microsoft Corporation +One Microsoft Way +Redmond, WA 98052 +USA + +Notwithstanding any other terms, you may reverse engineer this software to the extent +required to debug changes to any libraries licensed under the GNU Lesser General Public License. + +--------------------------------------------------------- + +Markdig.Signed 0.42.0 - BSD-2-Clause + + + +Copyright (c) . All rights reserved. + +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: + + 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +--------------------------------------------------------- + +--------------------------------------------------------- + +Humanizer.Core 2.14.1 - MIT + + +Copyright .NET Foundation and Contributors +Copyright (c) .NET Foundation and Contributors + +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--------------------------------------------------------- + +--------------------------------------------------------- + +Json.More.Net 2.1.1 - MIT + + +Copyright (c) .NET Foundation and Contributors + +MIT License + +Copyright (c) .NET Foundation and Contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +JsonPointer.Net 5.3.1 - MIT + + +Copyright (c) .NET Foundation and Contributors + +MIT License + +Copyright (c) .NET Foundation and Contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +JsonSchema.Net 7.4.0 - MIT + + +Copyright (c) .NET Foundation and Contributors + +MIT License + +Copyright (c) .NET Foundation and Contributors + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +Microsoft.ApplicationInsights 2.23.0 - MIT + + +(c) Microsoft Corporation + +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--------------------------------------------------------- + +--------------------------------------------------------- + +Microsoft.Bcl.AsyncInterfaces 9.0.9 - MIT + + +Copyright (c) 2021 +Copyright (c) Six Labors +(c) Microsoft Corporation +Copyright (c) 2022 FormatJS +Copyright (c) Andrew Arnott +Copyright 2019 LLVM Project +Copyright (c) 1998 Microsoft +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation +Copyright (c) 2011, Google Inc. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 2015 Andrew Gallant +Copyright (c) 2022, Wojciech Mula +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2022, Geoff Langdale +Copyright (c) 2005-2020 Rich Felker +Copyright (c) 2012-2021 Yann Collet +Copyright (c) Microsoft Corporation +Copyright (c) 2007 James Newton-King +Copyright (c) 1991-2022 Unicode, Inc. +Copyright (c) 2013-2017, Alfred Klomp +Copyright (c) 2018 Nemanja Mijailovic +Copyright 2012 the V8 project authors +Copyright (c) 1999 Lucent Technologies +Copyright (c) 2008-2016, Wojciech Mula +Copyright (c) 2011-2020 Microsoft Corp +Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2015-2018, Wojciech Mula +Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2011-2015 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski +Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) +Copyright (c) .NET Foundation Contributors +(c) 1995-2024 Jean-loup Gailly and Mark Adler +Copyright (c) 2020 Mara Bos +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2012 - present, Victor Zverovich +Copyright (c) 2006 Jb Evain (jbevain@gmail.com) +Copyright (c) 2008-2020 Advanced Micro Devices, Inc. +Copyright (c) 2019 Microsoft Corporation, Daan Leijen +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Portions (c) International Organization for Standardization 1986 +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright (c) 1980, 1986, 1993 The Regents of the University of California +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +Microsoft.CodeAnalysis.Common 4.14.0 - MIT + + +(c) Microsoft Corporation +Copyright (c) .NET Foundation and Contributors + +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--------------------------------------------------------- + +--------------------------------------------------------- + +Microsoft.CodeAnalysis.CSharp 4.14.0 - MIT + + +(c) Microsoft Corporation +Copyright (c) Microsoft Corporation +ACopyright (c) Microsoft Corporation +CCopyright (c) Microsoft Corporation +DCopyright (c) Microsoft Corporation +OCopyright (c) Microsoft Corporation +Copyright (c) .NET Foundation and Contributors + +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--------------------------------------------------------- + +--------------------------------------------------------- + +Microsoft.Extensions.ObjectPool 9.0.9 - MIT + + +Copyright Jorn Zaefferer +(c) Microsoft Corporation +Copyright (c) Andrew Arnott +Copyright (c) 2015, Google Inc. +Copyright (c) 2019 David Fowler +Copyright (c) HTML5 Boilerplate +Copyright 2019 The gRPC Authors +Copyright (c) 2016 Richard Morris +Copyright (c) 1998 John D. Polstra +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) Microsoft Corporation +Copyright (c) 2007 James Newton-King +Copyright (c) 2013 - 2018 AngleSharp +Copyright (c) 2000-2013 Julian Seward +Copyright (c) 2011-2021 Twitter, Inc. +Copyright (c) 2014-2018 Michael Daines +Copyright (c) 1996-1998 John D. Polstra +Copyright (c) 2013-2017, Milosz Krajewski +Copyright (c) .NET Foundation Contributors +Copyright (c) 2011-2021 The Bootstrap Authors +Copyright (c) 2019-2023 The Bootstrap Authors +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2019-2020 West Wind Technologies +Copyright (c) 2007 John Birrell (jb@freebsd.org) +Copyright (c) 2011 Alex MacCaw (info@eribium.org) +Copyright (c) Nicolas Gallagher and Jonathan Neal +Copyright (c) 2010-2019 Google LLC. http://angular.io/license +Copyright (c) 2011 Nicolas Gallagher (nicolas@nicolasgallagher.com) +Copyright (c) 1989, 1993 The Regents of the University of California +Copyright (c) 1990, 1993 The Regents of the University of California +Copyright OpenJS Foundation and other contributors, https://openjsf.org +Copyright (c) Sindre Sorhus (https://sindresorhus.com) + +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--------------------------------------------------------- + +--------------------------------------------------------- + +Microsoft.PowerShell.MarkdownRender 7.2.1 - MIT + + +(c) Microsoft Corporation +(c) Microsoft Corporation. PowerShell's Markdown Rendering project PowerShell Markdown Renderer + +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--------------------------------------------------------- + +--------------------------------------------------------- + +Microsoft.Security.Extensions 1.4.0 - MIT + + +(c) Microsoft Corporation +Copyright (c) Microsoft Corporation + +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--------------------------------------------------------- + +--------------------------------------------------------- + +Microsoft.Win32.Registry.AccessControl 9.0.9 - MIT + + +Copyright (c) 2021 +Copyright (c) Six Labors +(c) Microsoft Corporation +Copyright (c) 2022 FormatJS +Copyright (c) Andrew Arnott +Copyright 2019 LLVM Project +Copyright (c) 1998 Microsoft +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation +Copyright (c) 2011, Google Inc. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 2015 Andrew Gallant +Copyright (c) 2022, Wojciech Mula +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2022, Geoff Langdale +Copyright (c) 2005-2020 Rich Felker +Copyright (c) 2012-2021 Yann Collet +Copyright (c) Microsoft Corporation +Copyright (c) 2007 James Newton-King +Copyright (c) 1991-2022 Unicode, Inc. +Copyright (c) 2013-2017, Alfred Klomp +Copyright (c) 2018 Nemanja Mijailovic +Copyright 2012 the V8 project authors +Copyright (c) 1999 Lucent Technologies +Copyright (c) 2008-2016, Wojciech Mula +Copyright (c) 2011-2020 Microsoft Corp +Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2015-2018, Wojciech Mula +Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2011-2015 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski +Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) +Copyright (c) .NET Foundation Contributors +(c) 1995-2024 Jean-loup Gailly and Mark Adler +Copyright (c) 2020 Mara Bos +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2012 - present, Victor Zverovich +Copyright (c) 2006 Jb Evain (jbevain@gmail.com) +Copyright (c) 2008-2020 Advanced Micro Devices, Inc. +Copyright (c) 2019 Microsoft Corporation, Daan Leijen +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Portions (c) International Organization for Standardization 1986 +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright (c) 1980, 1986, 1993 The Regents of the University of California +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +Microsoft.Win32.SystemEvents 9.0.9 - MIT + + +Copyright (c) 2021 +Copyright (c) Six Labors +(c) Microsoft Corporation +Copyright (c) 2022 FormatJS +Copyright (c) Andrew Arnott +Copyright 2019 LLVM Project +Copyright (c) 1998 Microsoft +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation +Copyright (c) 2011, Google Inc. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 2015 Andrew Gallant +Copyright (c) 2022, Wojciech Mula +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2022, Geoff Langdale +Copyright (c) 2005-2020 Rich Felker +Copyright (c) 2012-2021 Yann Collet +Copyright (c) Microsoft Corporation +Copyright (c) 2007 James Newton-King +Copyright (c) 1991-2022 Unicode, Inc. +Copyright (c) 2013-2017, Alfred Klomp +Copyright (c) 2018 Nemanja Mijailovic +Copyright 2012 the V8 project authors +Copyright (c) 1999 Lucent Technologies +Copyright (c) 2008-2016, Wojciech Mula +Copyright (c) 2011-2020 Microsoft Corp +Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2015-2018, Wojciech Mula +Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2011-2015 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski +Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) +Copyright (c) .NET Foundation Contributors +(c) 1995-2024 Jean-loup Gailly and Mark Adler +Copyright (c) 2020 Mara Bos +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2012 - present, Victor Zverovich +Copyright (c) 2006 Jb Evain (jbevain@gmail.com) +Copyright (c) 2008-2020 Advanced Micro Devices, Inc. +Copyright (c) 2019 Microsoft Corporation, Daan Leijen +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Portions (c) International Organization for Standardization 1986 +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright (c) 1980, 1986, 1993 The Regents of the University of California +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +Microsoft.Windows.Compatibility 9.0.9 - MIT + + +(c) Microsoft Corporation + +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--------------------------------------------------------- + +--------------------------------------------------------- + +Newtonsoft.Json 13.0.4 - MIT + + +Copyright James Newton-King 2008 +Copyright (c) 2007 James Newton-King +Copyright (c) James Newton-King 2008 +Copyright James Newton-King 2008 Json.NET + +The MIT License (MIT) + +Copyright (c) 2007 James Newton-King + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +runtime.android-arm.runtime.native.System.IO.Ports 9.0.9 - MIT + + +Copyright (c) 2021 +Copyright (c) Six Labors +(c) Microsoft Corporation +Copyright (c) 2022 FormatJS +Copyright (c) Andrew Arnott +Copyright 2019 LLVM Project +Copyright (c) 1998 Microsoft +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation +Copyright (c) 2011, Google Inc. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 2015 Andrew Gallant +Copyright (c) 2022, Wojciech Mula +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2022, Geoff Langdale +Copyright (c) 2005-2020 Rich Felker +Copyright (c) 2012-2021 Yann Collet +Copyright (c) Microsoft Corporation +Copyright (c) 2007 James Newton-King +Copyright (c) 1991-2022 Unicode, Inc. +Copyright (c) 2013-2017, Alfred Klomp +Copyright (c) 2018 Nemanja Mijailovic +Copyright 2012 the V8 project authors +Copyright (c) 1999 Lucent Technologies +Copyright (c) 2008-2016, Wojciech Mula +Copyright (c) 2011-2020 Microsoft Corp +Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2015-2018, Wojciech Mula +Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2011-2015 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski +Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) +Copyright (c) .NET Foundation Contributors +(c) 1995-2024 Jean-loup Gailly and Mark Adler +Copyright (c) 2020 Mara Bos +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2012 - present, Victor Zverovich +Copyright (c) 2006 Jb Evain (jbevain@gmail.com) +Copyright (c) 2008-2020 Advanced Micro Devices, Inc. +Copyright (c) 2019 Microsoft Corporation, Daan Leijen +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Portions (c) International Organization for Standardization 1986 +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright (c) 1980, 1986, 1993 The Regents of the University of California +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +runtime.android-arm64.runtime.native.System.IO.Ports 9.0.9 - MIT + + +Copyright (c) 2021 +Copyright (c) Six Labors +(c) Microsoft Corporation +Copyright (c) 2022 FormatJS +Copyright (c) Andrew Arnott +Copyright 2019 LLVM Project +Copyright (c) 1998 Microsoft +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation +Copyright (c) 2011, Google Inc. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 2015 Andrew Gallant +Copyright (c) 2022, Wojciech Mula +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2022, Geoff Langdale +Copyright (c) 2005-2020 Rich Felker +Copyright (c) 2012-2021 Yann Collet +Copyright (c) Microsoft Corporation +Copyright (c) 2007 James Newton-King +Copyright (c) 1991-2022 Unicode, Inc. +Copyright (c) 2013-2017, Alfred Klomp +Copyright (c) 2018 Nemanja Mijailovic +Copyright 2012 the V8 project authors +Copyright (c) 1999 Lucent Technologies +Copyright (c) 2008-2016, Wojciech Mula +Copyright (c) 2011-2020 Microsoft Corp +Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2015-2018, Wojciech Mula +Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2011-2015 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski +Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) +Copyright (c) .NET Foundation Contributors +(c) 1995-2024 Jean-loup Gailly and Mark Adler +Copyright (c) 2020 Mara Bos +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2012 - present, Victor Zverovich +Copyright (c) 2006 Jb Evain (jbevain@gmail.com) +Copyright (c) 2008-2020 Advanced Micro Devices, Inc. +Copyright (c) 2019 Microsoft Corporation, Daan Leijen +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Portions (c) International Organization for Standardization 1986 +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright (c) 1980, 1986, 1993 The Regents of the University of California +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +runtime.android-x64.runtime.native.System.IO.Ports 9.0.9 - MIT + + +Copyright (c) 2021 +Copyright (c) Six Labors +(c) Microsoft Corporation +Copyright (c) 2022 FormatJS +Copyright (c) Andrew Arnott +Copyright 2019 LLVM Project +Copyright (c) 1998 Microsoft +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation +Copyright (c) 2011, Google Inc. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 2015 Andrew Gallant +Copyright (c) 2022, Wojciech Mula +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2022, Geoff Langdale +Copyright (c) 2005-2020 Rich Felker +Copyright (c) 2012-2021 Yann Collet +Copyright (c) Microsoft Corporation +Copyright (c) 2007 James Newton-King +Copyright (c) 1991-2022 Unicode, Inc. +Copyright (c) 2013-2017, Alfred Klomp +Copyright (c) 2018 Nemanja Mijailovic +Copyright 2012 the V8 project authors +Copyright (c) 1999 Lucent Technologies +Copyright (c) 2008-2016, Wojciech Mula +Copyright (c) 2011-2020 Microsoft Corp +Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2015-2018, Wojciech Mula +Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2011-2015 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski +Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) +Copyright (c) .NET Foundation Contributors +(c) 1995-2024 Jean-loup Gailly and Mark Adler +Copyright (c) 2020 Mara Bos +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2012 - present, Victor Zverovich +Copyright (c) 2006 Jb Evain (jbevain@gmail.com) +Copyright (c) 2008-2020 Advanced Micro Devices, Inc. +Copyright (c) 2019 Microsoft Corporation, Daan Leijen +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Portions (c) International Organization for Standardization 1986 +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright (c) 1980, 1986, 1993 The Regents of the University of California +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +runtime.android-x86.runtime.native.System.IO.Ports 9.0.9 - MIT + + +Copyright (c) 2021 +Copyright (c) Six Labors +(c) Microsoft Corporation +Copyright (c) 2022 FormatJS +Copyright (c) Andrew Arnott +Copyright 2019 LLVM Project +Copyright (c) 1998 Microsoft +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation +Copyright (c) 2011, Google Inc. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 2015 Andrew Gallant +Copyright (c) 2022, Wojciech Mula +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2022, Geoff Langdale +Copyright (c) 2005-2020 Rich Felker +Copyright (c) 2012-2021 Yann Collet +Copyright (c) Microsoft Corporation +Copyright (c) 2007 James Newton-King +Copyright (c) 1991-2022 Unicode, Inc. +Copyright (c) 2013-2017, Alfred Klomp +Copyright (c) 2018 Nemanja Mijailovic +Copyright 2012 the V8 project authors +Copyright (c) 1999 Lucent Technologies +Copyright (c) 2008-2016, Wojciech Mula +Copyright (c) 2011-2020 Microsoft Corp +Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2015-2018, Wojciech Mula +Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2011-2015 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski +Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) +Copyright (c) .NET Foundation Contributors +(c) 1995-2024 Jean-loup Gailly and Mark Adler +Copyright (c) 2020 Mara Bos +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2012 - present, Victor Zverovich +Copyright (c) 2006 Jb Evain (jbevain@gmail.com) +Copyright (c) 2008-2020 Advanced Micro Devices, Inc. +Copyright (c) 2019 Microsoft Corporation, Daan Leijen +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Portions (c) International Organization for Standardization 1986 +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright (c) 1980, 1986, 1993 The Regents of the University of California +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +runtime.linux-arm.runtime.native.System.IO.Ports 9.0.9 - MIT + + +Copyright (c) 2021 +Copyright (c) Six Labors +(c) Microsoft Corporation +Copyright (c) 2022 FormatJS +Copyright (c) Andrew Arnott +Copyright 2019 LLVM Project +Copyright (c) 1998 Microsoft +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation +Copyright (c) 2011, Google Inc. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 2015 Andrew Gallant +Copyright (c) 2022, Wojciech Mula +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2022, Geoff Langdale +Copyright (c) 2005-2020 Rich Felker +Copyright (c) 2012-2021 Yann Collet +Copyright (c) Microsoft Corporation +Copyright (c) 2007 James Newton-King +Copyright (c) 1991-2022 Unicode, Inc. +Copyright (c) 2013-2017, Alfred Klomp +Copyright (c) 2018 Nemanja Mijailovic +Copyright 2012 the V8 project authors +Copyright (c) 1999 Lucent Technologies +Copyright (c) 2008-2016, Wojciech Mula +Copyright (c) 2011-2020 Microsoft Corp +Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2015-2018, Wojciech Mula +Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2011-2015 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski +Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) +Copyright (c) .NET Foundation Contributors +(c) 1995-2024 Jean-loup Gailly and Mark Adler +Copyright (c) 2020 Mara Bos +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2012 - present, Victor Zverovich +Copyright (c) 2006 Jb Evain (jbevain@gmail.com) +Copyright (c) 2008-2020 Advanced Micro Devices, Inc. +Copyright (c) 2019 Microsoft Corporation, Daan Leijen +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Portions (c) International Organization for Standardization 1986 +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright (c) 1980, 1986, 1993 The Regents of the University of California +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +runtime.linux-arm64.runtime.native.System.IO.Ports 9.0.9 - MIT + + +Copyright (c) 2021 +Copyright (c) Six Labors +(c) Microsoft Corporation +Copyright (c) 2022 FormatJS +Copyright (c) Andrew Arnott +Copyright 2019 LLVM Project +Copyright (c) 1998 Microsoft +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation +Copyright (c) 2011, Google Inc. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 2015 Andrew Gallant +Copyright (c) 2022, Wojciech Mula +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2022, Geoff Langdale +Copyright (c) 2005-2020 Rich Felker +Copyright (c) 2012-2021 Yann Collet +Copyright (c) Microsoft Corporation +Copyright (c) 2007 James Newton-King +Copyright (c) 1991-2022 Unicode, Inc. +Copyright (c) 2013-2017, Alfred Klomp +Copyright (c) 2018 Nemanja Mijailovic +Copyright 2012 the V8 project authors +Copyright (c) 1999 Lucent Technologies +Copyright (c) 2008-2016, Wojciech Mula +Copyright (c) 2011-2020 Microsoft Corp +Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2015-2018, Wojciech Mula +Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2011-2015 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski +Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) +Copyright (c) .NET Foundation Contributors +(c) 1995-2024 Jean-loup Gailly and Mark Adler +Copyright (c) 2020 Mara Bos +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2012 - present, Victor Zverovich +Copyright (c) 2006 Jb Evain (jbevain@gmail.com) +Copyright (c) 2008-2020 Advanced Micro Devices, Inc. +Copyright (c) 2019 Microsoft Corporation, Daan Leijen +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Portions (c) International Organization for Standardization 1986 +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright (c) 1980, 1986, 1993 The Regents of the University of California +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +runtime.linux-bionic-arm64.runtime.native.System.IO.Ports 9.0.9 - MIT + + +Copyright (c) 2021 +Copyright (c) Six Labors +(c) Microsoft Corporation +Copyright (c) 2022 FormatJS +Copyright (c) Andrew Arnott +Copyright 2019 LLVM Project +Copyright (c) 1998 Microsoft +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation +Copyright (c) 2011, Google Inc. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 2015 Andrew Gallant +Copyright (c) 2022, Wojciech Mula +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2022, Geoff Langdale +Copyright (c) 2005-2020 Rich Felker +Copyright (c) 2012-2021 Yann Collet +Copyright (c) Microsoft Corporation +Copyright (c) 2007 James Newton-King +Copyright (c) 1991-2022 Unicode, Inc. +Copyright (c) 2013-2017, Alfred Klomp +Copyright (c) 2018 Nemanja Mijailovic +Copyright 2012 the V8 project authors +Copyright (c) 1999 Lucent Technologies +Copyright (c) 2008-2016, Wojciech Mula +Copyright (c) 2011-2020 Microsoft Corp +Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2015-2018, Wojciech Mula +Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2011-2015 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski +Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) +Copyright (c) .NET Foundation Contributors +(c) 1995-2024 Jean-loup Gailly and Mark Adler +Copyright (c) 2020 Mara Bos +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2012 - present, Victor Zverovich +Copyright (c) 2006 Jb Evain (jbevain@gmail.com) +Copyright (c) 2008-2020 Advanced Micro Devices, Inc. +Copyright (c) 2019 Microsoft Corporation, Daan Leijen +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Portions (c) International Organization for Standardization 1986 +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright (c) 1980, 1986, 1993 The Regents of the University of California +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +runtime.linux-bionic-x64.runtime.native.System.IO.Ports 9.0.9 - MIT + + +Copyright (c) 2021 +Copyright (c) Six Labors +(c) Microsoft Corporation +Copyright (c) 2022 FormatJS +Copyright (c) Andrew Arnott +Copyright 2019 LLVM Project +Copyright (c) 1998 Microsoft +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation +Copyright (c) 2011, Google Inc. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 2015 Andrew Gallant +Copyright (c) 2022, Wojciech Mula +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2022, Geoff Langdale +Copyright (c) 2005-2020 Rich Felker +Copyright (c) 2012-2021 Yann Collet +Copyright (c) Microsoft Corporation +Copyright (c) 2007 James Newton-King +Copyright (c) 1991-2022 Unicode, Inc. +Copyright (c) 2013-2017, Alfred Klomp +Copyright (c) 2018 Nemanja Mijailovic +Copyright 2012 the V8 project authors +Copyright (c) 1999 Lucent Technologies +Copyright (c) 2008-2016, Wojciech Mula +Copyright (c) 2011-2020 Microsoft Corp +Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2015-2018, Wojciech Mula +Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2011-2015 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski +Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) +Copyright (c) .NET Foundation Contributors +(c) 1995-2024 Jean-loup Gailly and Mark Adler +Copyright (c) 2020 Mara Bos +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2012 - present, Victor Zverovich +Copyright (c) 2006 Jb Evain (jbevain@gmail.com) +Copyright (c) 2008-2020 Advanced Micro Devices, Inc. +Copyright (c) 2019 Microsoft Corporation, Daan Leijen +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Portions (c) International Organization for Standardization 1986 +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright (c) 1980, 1986, 1993 The Regents of the University of California +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +runtime.linux-musl-arm.runtime.native.System.IO.Ports 9.0.9 - MIT + + +Copyright (c) 2021 +Copyright (c) Six Labors +(c) Microsoft Corporation +Copyright (c) 2022 FormatJS +Copyright (c) Andrew Arnott +Copyright 2019 LLVM Project +Copyright (c) 1998 Microsoft +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation +Copyright (c) 2011, Google Inc. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 2015 Andrew Gallant +Copyright (c) 2022, Wojciech Mula +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2022, Geoff Langdale +Copyright (c) 2005-2020 Rich Felker +Copyright (c) 2012-2021 Yann Collet +Copyright (c) Microsoft Corporation +Copyright (c) 2007 James Newton-King +Copyright (c) 1991-2022 Unicode, Inc. +Copyright (c) 2013-2017, Alfred Klomp +Copyright (c) 2018 Nemanja Mijailovic +Copyright 2012 the V8 project authors +Copyright (c) 1999 Lucent Technologies +Copyright (c) 2008-2016, Wojciech Mula +Copyright (c) 2011-2020 Microsoft Corp +Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2015-2018, Wojciech Mula +Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2011-2015 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski +Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) +Copyright (c) .NET Foundation Contributors +(c) 1995-2024 Jean-loup Gailly and Mark Adler +Copyright (c) 2020 Mara Bos +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2012 - present, Victor Zverovich +Copyright (c) 2006 Jb Evain (jbevain@gmail.com) +Copyright (c) 2008-2020 Advanced Micro Devices, Inc. +Copyright (c) 2019 Microsoft Corporation, Daan Leijen +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Portions (c) International Organization for Standardization 1986 +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright (c) 1980, 1986, 1993 The Regents of the University of California +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +runtime.linux-musl-arm64.runtime.native.System.IO.Ports 9.0.9 - MIT + + +Copyright (c) 2021 +Copyright (c) Six Labors +(c) Microsoft Corporation +Copyright (c) 2022 FormatJS +Copyright (c) Andrew Arnott +Copyright 2019 LLVM Project +Copyright (c) 1998 Microsoft +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation +Copyright (c) 2011, Google Inc. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 2015 Andrew Gallant +Copyright (c) 2022, Wojciech Mula +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2022, Geoff Langdale +Copyright (c) 2005-2020 Rich Felker +Copyright (c) 2012-2021 Yann Collet +Copyright (c) Microsoft Corporation +Copyright (c) 2007 James Newton-King +Copyright (c) 1991-2022 Unicode, Inc. +Copyright (c) 2013-2017, Alfred Klomp +Copyright (c) 2018 Nemanja Mijailovic +Copyright 2012 the V8 project authors +Copyright (c) 1999 Lucent Technologies +Copyright (c) 2008-2016, Wojciech Mula +Copyright (c) 2011-2020 Microsoft Corp +Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2015-2018, Wojciech Mula +Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2011-2015 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski +Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) +Copyright (c) .NET Foundation Contributors +(c) 1995-2024 Jean-loup Gailly and Mark Adler +Copyright (c) 2020 Mara Bos +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2012 - present, Victor Zverovich +Copyright (c) 2006 Jb Evain (jbevain@gmail.com) +Copyright (c) 2008-2020 Advanced Micro Devices, Inc. +Copyright (c) 2019 Microsoft Corporation, Daan Leijen +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Portions (c) International Organization for Standardization 1986 +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright (c) 1980, 1986, 1993 The Regents of the University of California +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +runtime.linux-musl-x64.runtime.native.System.IO.Ports 9.0.9 - MIT + + +Copyright (c) 2021 +Copyright (c) Six Labors +(c) Microsoft Corporation +Copyright (c) 2022 FormatJS +Copyright (c) Andrew Arnott +Copyright 2019 LLVM Project +Copyright (c) 1998 Microsoft +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation +Copyright (c) 2011, Google Inc. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 2015 Andrew Gallant +Copyright (c) 2022, Wojciech Mula +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2022, Geoff Langdale +Copyright (c) 2005-2020 Rich Felker +Copyright (c) 2012-2021 Yann Collet +Copyright (c) Microsoft Corporation +Copyright (c) 2007 James Newton-King +Copyright (c) 1991-2022 Unicode, Inc. +Copyright (c) 2013-2017, Alfred Klomp +Copyright (c) 2018 Nemanja Mijailovic +Copyright 2012 the V8 project authors +Copyright (c) 1999 Lucent Technologies +Copyright (c) 2008-2016, Wojciech Mula +Copyright (c) 2011-2020 Microsoft Corp +Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2015-2018, Wojciech Mula +Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2011-2015 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski +Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) +Copyright (c) .NET Foundation Contributors +(c) 1995-2024 Jean-loup Gailly and Mark Adler +Copyright (c) 2020 Mara Bos +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2012 - present, Victor Zverovich +Copyright (c) 2006 Jb Evain (jbevain@gmail.com) +Copyright (c) 2008-2020 Advanced Micro Devices, Inc. +Copyright (c) 2019 Microsoft Corporation, Daan Leijen +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Portions (c) International Organization for Standardization 1986 +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright (c) 1980, 1986, 1993 The Regents of the University of California +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +runtime.linux-x64.runtime.native.System.IO.Ports 9.0.9 - MIT + + +Copyright (c) 2021 +Copyright (c) Six Labors +(c) Microsoft Corporation +Copyright (c) 2022 FormatJS +Copyright (c) Andrew Arnott +Copyright 2019 LLVM Project +Copyright (c) 1998 Microsoft +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation +Copyright (c) 2011, Google Inc. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 2015 Andrew Gallant +Copyright (c) 2022, Wojciech Mula +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2022, Geoff Langdale +Copyright (c) 2005-2020 Rich Felker +Copyright (c) 2012-2021 Yann Collet +Copyright (c) Microsoft Corporation +Copyright (c) 2007 James Newton-King +Copyright (c) 1991-2022 Unicode, Inc. +Copyright (c) 2013-2017, Alfred Klomp +Copyright (c) 2018 Nemanja Mijailovic +Copyright 2012 the V8 project authors +Copyright (c) 1999 Lucent Technologies +Copyright (c) 2008-2016, Wojciech Mula +Copyright (c) 2011-2020 Microsoft Corp +Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2015-2018, Wojciech Mula +Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2011-2015 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski +Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) +Copyright (c) .NET Foundation Contributors +(c) 1995-2024 Jean-loup Gailly and Mark Adler +Copyright (c) 2020 Mara Bos +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2012 - present, Victor Zverovich +Copyright (c) 2006 Jb Evain (jbevain@gmail.com) +Copyright (c) 2008-2020 Advanced Micro Devices, Inc. +Copyright (c) 2019 Microsoft Corporation, Daan Leijen +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Portions (c) International Organization for Standardization 1986 +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright (c) 1980, 1986, 1993 The Regents of the University of California +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +runtime.maccatalyst-arm64.runtime.native.System.IO.Ports 9.0.9 - MIT + + +Copyright (c) 2021 +Copyright (c) Six Labors +(c) Microsoft Corporation +Copyright (c) 2022 FormatJS +Copyright (c) Andrew Arnott +Copyright 2019 LLVM Project +Copyright (c) 1998 Microsoft +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation +Copyright (c) 2011, Google Inc. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 2015 Andrew Gallant +Copyright (c) 2022, Wojciech Mula +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2022, Geoff Langdale +Copyright (c) 2005-2020 Rich Felker +Copyright (c) 2012-2021 Yann Collet +Copyright (c) Microsoft Corporation +Copyright (c) 2007 James Newton-King +Copyright (c) 1991-2022 Unicode, Inc. +Copyright (c) 2013-2017, Alfred Klomp +Copyright (c) 2018 Nemanja Mijailovic +Copyright 2012 the V8 project authors +Copyright (c) 1999 Lucent Technologies +Copyright (c) 2008-2016, Wojciech Mula +Copyright (c) 2011-2020 Microsoft Corp +Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2015-2018, Wojciech Mula +Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2011-2015 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski +Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) +Copyright (c) .NET Foundation Contributors +(c) 1995-2024 Jean-loup Gailly and Mark Adler +Copyright (c) 2020 Mara Bos +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2012 - present, Victor Zverovich +Copyright (c) 2006 Jb Evain (jbevain@gmail.com) +Copyright (c) 2008-2020 Advanced Micro Devices, Inc. +Copyright (c) 2019 Microsoft Corporation, Daan Leijen +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Portions (c) International Organization for Standardization 1986 +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright (c) 1980, 1986, 1993 The Regents of the University of California +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +runtime.maccatalyst-x64.runtime.native.System.IO.Ports 9.0.9 - MIT + + +Copyright (c) 2021 +Copyright (c) Six Labors +(c) Microsoft Corporation +Copyright (c) 2022 FormatJS +Copyright (c) Andrew Arnott +Copyright 2019 LLVM Project +Copyright (c) 1998 Microsoft +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation +Copyright (c) 2011, Google Inc. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 2015 Andrew Gallant +Copyright (c) 2022, Wojciech Mula +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2022, Geoff Langdale +Copyright (c) 2005-2020 Rich Felker +Copyright (c) 2012-2021 Yann Collet +Copyright (c) Microsoft Corporation +Copyright (c) 2007 James Newton-King +Copyright (c) 1991-2022 Unicode, Inc. +Copyright (c) 2013-2017, Alfred Klomp +Copyright (c) 2018 Nemanja Mijailovic +Copyright 2012 the V8 project authors +Copyright (c) 1999 Lucent Technologies +Copyright (c) 2008-2016, Wojciech Mula +Copyright (c) 2011-2020 Microsoft Corp +Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2015-2018, Wojciech Mula +Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2011-2015 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski +Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) +Copyright (c) .NET Foundation Contributors +(c) 1995-2024 Jean-loup Gailly and Mark Adler +Copyright (c) 2020 Mara Bos +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2012 - present, Victor Zverovich +Copyright (c) 2006 Jb Evain (jbevain@gmail.com) +Copyright (c) 2008-2020 Advanced Micro Devices, Inc. +Copyright (c) 2019 Microsoft Corporation, Daan Leijen +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Portions (c) International Organization for Standardization 1986 +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright (c) 1980, 1986, 1993 The Regents of the University of California +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +runtime.native.System.Data.SqlClient.sni 4.4.0 - MIT + + +(c) 2022 GitHub, Inc. +(c) Microsoft Corporation +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 1991-2017 Unicode, Inc. +Portions (c) International Organization +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) .NET Foundation Contributors +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 1995-2017 Jean-loup Gailly and Mark Adler +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang). Disclaimers + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +runtime.native.System.IO.Ports 9.0.9 - MIT + + +Copyright (c) 2021 +Copyright (c) Six Labors +(c) Microsoft Corporation +Copyright (c) 2022 FormatJS +Copyright (c) Andrew Arnott +Copyright 2019 LLVM Project +Copyright (c) 1998 Microsoft +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation +Copyright (c) 2011, Google Inc. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 2015 Andrew Gallant +Copyright (c) 2022, Wojciech Mula +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2022, Geoff Langdale +Copyright (c) 2005-2020 Rich Felker +Copyright (c) 2012-2021 Yann Collet +Copyright (c) Microsoft Corporation +Copyright (c) 2007 James Newton-King +Copyright (c) 1991-2022 Unicode, Inc. +Copyright (c) 2013-2017, Alfred Klomp +Copyright (c) 2018 Nemanja Mijailovic +Copyright 2012 the V8 project authors +Copyright (c) 1999 Lucent Technologies +Copyright (c) 2008-2016, Wojciech Mula +Copyright (c) 2011-2020 Microsoft Corp +Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2015-2018, Wojciech Mula +Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2011-2015 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski +Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) +Copyright (c) .NET Foundation Contributors +(c) 1995-2024 Jean-loup Gailly and Mark Adler +Copyright (c) 2020 Mara Bos +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2012 - present, Victor Zverovich +Copyright (c) 2006 Jb Evain (jbevain@gmail.com) +Copyright (c) 2008-2020 Advanced Micro Devices, Inc. +Copyright (c) 2019 Microsoft Corporation, Daan Leijen +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Portions (c) International Organization for Standardization 1986 +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright (c) 1980, 1986, 1993 The Regents of the University of California +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +runtime.osx-arm64.runtime.native.System.IO.Ports 9.0.9 - MIT + + +Copyright (c) 2021 +Copyright (c) Six Labors +(c) Microsoft Corporation +Copyright (c) 2022 FormatJS +Copyright (c) Andrew Arnott +Copyright 2019 LLVM Project +Copyright (c) 1998 Microsoft +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation +Copyright (c) 2011, Google Inc. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 2015 Andrew Gallant +Copyright (c) 2022, Wojciech Mula +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2022, Geoff Langdale +Copyright (c) 2005-2020 Rich Felker +Copyright (c) 2012-2021 Yann Collet +Copyright (c) Microsoft Corporation +Copyright (c) 2007 James Newton-King +Copyright (c) 1991-2022 Unicode, Inc. +Copyright (c) 2013-2017, Alfred Klomp +Copyright (c) 2018 Nemanja Mijailovic +Copyright 2012 the V8 project authors +Copyright (c) 1999 Lucent Technologies +Copyright (c) 2008-2016, Wojciech Mula +Copyright (c) 2011-2020 Microsoft Corp +Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2015-2018, Wojciech Mula +Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2011-2015 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski +Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) +Copyright (c) .NET Foundation Contributors +(c) 1995-2024 Jean-loup Gailly and Mark Adler +Copyright (c) 2020 Mara Bos +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2012 - present, Victor Zverovich +Copyright (c) 2006 Jb Evain (jbevain@gmail.com) +Copyright (c) 2008-2020 Advanced Micro Devices, Inc. +Copyright (c) 2019 Microsoft Corporation, Daan Leijen +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Portions (c) International Organization for Standardization 1986 +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright (c) 1980, 1986, 1993 The Regents of the University of California +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +runtime.osx-x64.runtime.native.System.IO.Ports 9.0.9 - MIT + + +Copyright (c) 2021 +Copyright (c) Six Labors +(c) Microsoft Corporation +Copyright (c) 2022 FormatJS +Copyright (c) Andrew Arnott +Copyright 2019 LLVM Project +Copyright (c) 1998 Microsoft +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation +Copyright (c) 2011, Google Inc. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 2015 Andrew Gallant +Copyright (c) 2022, Wojciech Mula +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2022, Geoff Langdale +Copyright (c) 2005-2020 Rich Felker +Copyright (c) 2012-2021 Yann Collet +Copyright (c) Microsoft Corporation +Copyright (c) 2007 James Newton-King +Copyright (c) 1991-2022 Unicode, Inc. +Copyright (c) 2013-2017, Alfred Klomp +Copyright (c) 2018 Nemanja Mijailovic +Copyright 2012 the V8 project authors +Copyright (c) 1999 Lucent Technologies +Copyright (c) 2008-2016, Wojciech Mula +Copyright (c) 2011-2020 Microsoft Corp +Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2015-2018, Wojciech Mula +Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2011-2015 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski +Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) +Copyright (c) .NET Foundation Contributors +(c) 1995-2024 Jean-loup Gailly and Mark Adler +Copyright (c) 2020 Mara Bos +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2012 - present, Victor Zverovich +Copyright (c) 2006 Jb Evain (jbevain@gmail.com) +Copyright (c) 2008-2020 Advanced Micro Devices, Inc. +Copyright (c) 2019 Microsoft Corporation, Daan Leijen +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Portions (c) International Organization for Standardization 1986 +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright (c) 1980, 1986, 1993 The Regents of the University of California +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +System.CodeDom 9.0.9 - MIT + + +Copyright (c) 2021 +Copyright (c) Six Labors +(c) Microsoft Corporation +Copyright (c) 2022 FormatJS +Copyright (c) Andrew Arnott +Copyright 2019 LLVM Project +Copyright (c) 1998 Microsoft +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation +Copyright (c) 2011, Google Inc. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 2015 Andrew Gallant +Copyright (c) 2022, Wojciech Mula +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2022, Geoff Langdale +Copyright (c) 2005-2020 Rich Felker +Copyright (c) 2012-2021 Yann Collet +Copyright (c) Microsoft Corporation +Copyright (c) 2007 James Newton-King +Copyright (c) 1991-2022 Unicode, Inc. +Copyright (c) 2013-2017, Alfred Klomp +Copyright (c) 2018 Nemanja Mijailovic +Copyright 2012 the V8 project authors +Copyright (c) 1999 Lucent Technologies +Copyright (c) 2008-2016, Wojciech Mula +Copyright (c) 2011-2020 Microsoft Corp +Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2015-2018, Wojciech Mula +Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2011-2015 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski +Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) +Copyright (c) .NET Foundation Contributors +(c) 1995-2024 Jean-loup Gailly and Mark Adler +Copyright (c) 2020 Mara Bos +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2012 - present, Victor Zverovich +Copyright (c) 2006 Jb Evain (jbevain@gmail.com) +Copyright (c) 2008-2020 Advanced Micro Devices, Inc. +Copyright (c) 2019 Microsoft Corporation, Daan Leijen +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Portions (c) International Organization for Standardization 1986 +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright (c) 1980, 1986, 1993 The Regents of the University of California +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +System.ComponentModel.Composition 9.0.9 - MIT + + +Copyright (c) 2021 +Copyright (c) Six Labors +(c) Microsoft Corporation +Copyright (c) 2022 FormatJS +Copyright (c) Andrew Arnott +Copyright 2019 LLVM Project +Copyright (c) 1998 Microsoft +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation +Copyright (c) 2011, Google Inc. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 2015 Andrew Gallant +Copyright (c) 2022, Wojciech Mula +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2022, Geoff Langdale +Copyright (c) 2005-2020 Rich Felker +Copyright (c) 2012-2021 Yann Collet +Copyright (c) Microsoft Corporation +Copyright (c) 2007 James Newton-King +Copyright (c) 1991-2022 Unicode, Inc. +Copyright (c) 2013-2017, Alfred Klomp +Copyright (c) 2018 Nemanja Mijailovic +Copyright 2012 the V8 project authors +Copyright (c) 1999 Lucent Technologies +Copyright (c) 2008-2016, Wojciech Mula +Copyright (c) 2011-2020 Microsoft Corp +Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2015-2018, Wojciech Mula +Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2011-2015 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski +Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) +Copyright (c) .NET Foundation Contributors +(c) 1995-2024 Jean-loup Gailly and Mark Adler +Copyright (c) 2020 Mara Bos +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2012 - present, Victor Zverovich +Copyright (c) 2006 Jb Evain (jbevain@gmail.com) +Copyright (c) 2008-2020 Advanced Micro Devices, Inc. +Copyright (c) 2019 Microsoft Corporation, Daan Leijen +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Portions (c) International Organization for Standardization 1986 +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright (c) 1980, 1986, 1993 The Regents of the University of California +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +System.ComponentModel.Composition.Registration 9.0.9 - MIT + + +Copyright (c) 2021 +Copyright (c) Six Labors +(c) Microsoft Corporation +Copyright (c) 2022 FormatJS +Copyright (c) Andrew Arnott +Copyright 2019 LLVM Project +Copyright (c) 1998 Microsoft +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation +Copyright (c) 2011, Google Inc. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 2015 Andrew Gallant +Copyright (c) 2022, Wojciech Mula +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2022, Geoff Langdale +Copyright (c) 2005-2020 Rich Felker +Copyright (c) 2012-2021 Yann Collet +Copyright (c) Microsoft Corporation +Copyright (c) 2007 James Newton-King +Copyright (c) 1991-2022 Unicode, Inc. +Copyright (c) 2013-2017, Alfred Klomp +Copyright (c) 2018 Nemanja Mijailovic +Copyright 2012 the V8 project authors +Copyright (c) 1999 Lucent Technologies +Copyright (c) 2008-2016, Wojciech Mula +Copyright (c) 2011-2020 Microsoft Corp +Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2015-2018, Wojciech Mula +Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2011-2015 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski +Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) +Copyright (c) .NET Foundation Contributors +(c) 1995-2024 Jean-loup Gailly and Mark Adler +Copyright (c) 2020 Mara Bos +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2012 - present, Victor Zverovich +Copyright (c) 2006 Jb Evain (jbevain@gmail.com) +Copyright (c) 2008-2020 Advanced Micro Devices, Inc. +Copyright (c) 2019 Microsoft Corporation, Daan Leijen +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Portions (c) International Organization for Standardization 1986 +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright (c) 1980, 1986, 1993 The Regents of the University of California +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +System.Configuration.ConfigurationManager 9.0.9 - MIT + + +Copyright (c) 2021 +Copyright (c) Six Labors +(c) Microsoft Corporation +Copyright (c) 2022 FormatJS +Copyright (c) Andrew Arnott +Copyright 2019 LLVM Project +Copyright (c) 1998 Microsoft +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation +Copyright (c) 2011, Google Inc. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 2015 Andrew Gallant +Copyright (c) 2022, Wojciech Mula +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2022, Geoff Langdale +Copyright (c) 2005-2020 Rich Felker +Copyright (c) 2012-2021 Yann Collet +Copyright (c) Microsoft Corporation +Copyright (c) 2007 James Newton-King +Copyright (c) 1991-2022 Unicode, Inc. +Copyright (c) 2013-2017, Alfred Klomp +Copyright (c) 2018 Nemanja Mijailovic +Copyright 2012 the V8 project authors +Copyright (c) 1999 Lucent Technologies +Copyright (c) 2008-2016, Wojciech Mula +Copyright (c) 2011-2020 Microsoft Corp +Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2015-2018, Wojciech Mula +Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2011-2015 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski +Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) +Copyright (c) .NET Foundation Contributors +(c) 1995-2024 Jean-loup Gailly and Mark Adler +Copyright (c) 2020 Mara Bos +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2012 - present, Victor Zverovich +Copyright (c) 2006 Jb Evain (jbevain@gmail.com) +Copyright (c) 2008-2020 Advanced Micro Devices, Inc. +Copyright (c) 2019 Microsoft Corporation, Daan Leijen +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Portions (c) International Organization for Standardization 1986 +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright (c) 1980, 1986, 1993 The Regents of the University of California +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +System.Data.Odbc 9.0.9 - MIT + + +Copyright (c) 2021 +Copyright (c) Six Labors +(c) Microsoft Corporation +Copyright (c) 2022 FormatJS +Copyright (c) Andrew Arnott +Copyright 2019 LLVM Project +Copyright (c) 1998 Microsoft +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation +Copyright (c) 2011, Google Inc. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 2015 Andrew Gallant +Copyright (c) 2022, Wojciech Mula +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2022, Geoff Langdale +Copyright (c) 2005-2020 Rich Felker +Copyright (c) 2012-2021 Yann Collet +Copyright (c) Microsoft Corporation +Copyright (c) 2007 James Newton-King +Copyright (c) 1991-2022 Unicode, Inc. +Copyright (c) 2013-2017, Alfred Klomp +Copyright (c) 2018 Nemanja Mijailovic +Copyright 2012 the V8 project authors +Copyright (c) 1999 Lucent Technologies +Copyright (c) 2008-2016, Wojciech Mula +Copyright (c) 2011-2020 Microsoft Corp +Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2015-2018, Wojciech Mula +Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2011-2015 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski +Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) +Copyright (c) .NET Foundation Contributors +(c) 1995-2024 Jean-loup Gailly and Mark Adler +Copyright (c) 2020 Mara Bos +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2012 - present, Victor Zverovich +Copyright (c) 2006 Jb Evain (jbevain@gmail.com) +Copyright (c) 2008-2020 Advanced Micro Devices, Inc. +Copyright (c) 2019 Microsoft Corporation, Daan Leijen +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Portions (c) International Organization for Standardization 1986 +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright (c) 1980, 1986, 1993 The Regents of the University of California +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +System.Data.OleDb 9.0.9 - MIT + + +Copyright (c) 2021 +Copyright (c) Six Labors +(c) Microsoft Corporation +Copyright (c) 2022 FormatJS +Copyright (c) Andrew Arnott +Copyright 2019 LLVM Project +Copyright (c) 1998 Microsoft +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation +Copyright (c) 2011, Google Inc. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 2015 Andrew Gallant +Copyright (c) 2022, Wojciech Mula +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2022, Geoff Langdale +Copyright (c) 2005-2020 Rich Felker +Copyright (c) 2012-2021 Yann Collet +Copyright (c) Microsoft Corporation +Copyright (c) 2007 James Newton-King +Copyright (c) 1991-2022 Unicode, Inc. +Copyright (c) 2013-2017, Alfred Klomp +Copyright (c) 2018 Nemanja Mijailovic +Copyright 2012 the V8 project authors +Copyright (c) 1999 Lucent Technologies +Copyright (c) 2008-2016, Wojciech Mula +Copyright (c) 2011-2020 Microsoft Corp +Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2015-2018, Wojciech Mula +Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2011-2015 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski +Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) +Copyright (c) .NET Foundation Contributors +(c) 1995-2024 Jean-loup Gailly and Mark Adler +Copyright (c) 2020 Mara Bos +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2012 - present, Victor Zverovich +Copyright (c) 2006 Jb Evain (jbevain@gmail.com) +Copyright (c) 2008-2020 Advanced Micro Devices, Inc. +Copyright (c) 2019 Microsoft Corporation, Daan Leijen +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Portions (c) International Organization for Standardization 1986 +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright (c) 1980, 1986, 1993 The Regents of the University of California +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +System.Data.SqlClient 4.9.0 - MIT + + +(c) Microsoft Corporation + +MIT License + +Copyright (c) + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--------------------------------------------------------- + +--------------------------------------------------------- + +System.Diagnostics.EventLog 9.0.9 - MIT + + +Copyright (c) 2021 +Copyright (c) Six Labors +(c) Microsoft Corporation +Copyright (c) 2022 FormatJS +Copyright (c) Andrew Arnott +Copyright 2019 LLVM Project +Copyright (c) 1998 Microsoft +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation +Copyright (c) 2011, Google Inc. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 2015 Andrew Gallant +Copyright (c) 2022, Wojciech Mula +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2022, Geoff Langdale +Copyright (c) 2005-2020 Rich Felker +Copyright (c) 2012-2021 Yann Collet +Copyright (c) Microsoft Corporation +Copyright (c) 2007 James Newton-King +Copyright (c) 1991-2022 Unicode, Inc. +Copyright (c) 2013-2017, Alfred Klomp +Copyright (c) 2018 Nemanja Mijailovic +Copyright 2012 the V8 project authors +Copyright (c) 1999 Lucent Technologies +Copyright (c) 2008-2016, Wojciech Mula +Copyright (c) 2011-2020 Microsoft Corp +Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2015-2018, Wojciech Mula +Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2011-2015 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski +Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) +Copyright (c) .NET Foundation Contributors +(c) 1995-2024 Jean-loup Gailly and Mark Adler +Copyright (c) 2020 Mara Bos +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2012 - present, Victor Zverovich +Copyright (c) 2006 Jb Evain (jbevain@gmail.com) +Copyright (c) 2008-2020 Advanced Micro Devices, Inc. +Copyright (c) 2019 Microsoft Corporation, Daan Leijen +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Portions (c) International Organization for Standardization 1986 +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright (c) 1980, 1986, 1993 The Regents of the University of California +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +System.Diagnostics.PerformanceCounter 9.0.9 - MIT + + +Copyright (c) 2021 +Copyright (c) Six Labors +(c) Microsoft Corporation +Copyright (c) 2022 FormatJS +Copyright (c) Andrew Arnott +Copyright 2019 LLVM Project +Copyright (c) 1998 Microsoft +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation +Copyright (c) 2011, Google Inc. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 2015 Andrew Gallant +Copyright (c) 2022, Wojciech Mula +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2022, Geoff Langdale +Copyright (c) 2005-2020 Rich Felker +Copyright (c) 2012-2021 Yann Collet +Copyright (c) Microsoft Corporation +Copyright (c) 2007 James Newton-King +Copyright (c) 1991-2022 Unicode, Inc. +Copyright (c) 2013-2017, Alfred Klomp +Copyright (c) 2018 Nemanja Mijailovic +Copyright 2012 the V8 project authors +Copyright (c) 1999 Lucent Technologies +Copyright (c) 2008-2016, Wojciech Mula +Copyright (c) 2011-2020 Microsoft Corp +Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2015-2018, Wojciech Mula +Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2011-2015 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski +Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) +Copyright (c) .NET Foundation Contributors +(c) 1995-2024 Jean-loup Gailly and Mark Adler +Copyright (c) 2020 Mara Bos +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2012 - present, Victor Zverovich +Copyright (c) 2006 Jb Evain (jbevain@gmail.com) +Copyright (c) 2008-2020 Advanced Micro Devices, Inc. +Copyright (c) 2019 Microsoft Corporation, Daan Leijen +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Portions (c) International Organization for Standardization 1986 +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright (c) 1980, 1986, 1993 The Regents of the University of California +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +System.DirectoryServices 9.0.9 - MIT + + +Copyright (c) 2021 +Copyright (c) Six Labors +(c) Microsoft Corporation +Copyright (c) 2022 FormatJS +Copyright (c) Andrew Arnott +Copyright 2019 LLVM Project +Copyright (c) 1998 Microsoft +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation +Copyright (c) 2011, Google Inc. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 2015 Andrew Gallant +Copyright (c) 2022, Wojciech Mula +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2022, Geoff Langdale +Copyright (c) 2005-2020 Rich Felker +Copyright (c) 2012-2021 Yann Collet +Copyright (c) Microsoft Corporation +Copyright (c) 2007 James Newton-King +Copyright (c) 1991-2022 Unicode, Inc. +Copyright (c) 2013-2017, Alfred Klomp +Copyright (c) 2018 Nemanja Mijailovic +Copyright 2012 the V8 project authors +Copyright (c) 1999 Lucent Technologies +Copyright (c) 2008-2016, Wojciech Mula +Copyright (c) 2011-2020 Microsoft Corp +Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2015-2018, Wojciech Mula +Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2011-2015 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski +Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) +Copyright (c) .NET Foundation Contributors +(c) 1995-2024 Jean-loup Gailly and Mark Adler +Copyright (c) 2020 Mara Bos +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2012 - present, Victor Zverovich +Copyright (c) 2006 Jb Evain (jbevain@gmail.com) +Copyright (c) 2008-2020 Advanced Micro Devices, Inc. +Copyright (c) 2019 Microsoft Corporation, Daan Leijen +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Portions (c) International Organization for Standardization 1986 +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright (c) 1980, 1986, 1993 The Regents of the University of California +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +System.DirectoryServices.AccountManagement 9.0.9 - MIT + + +Copyright (c) 2021 +Copyright (c) Six Labors +(c) Microsoft Corporation +Copyright (c) 2022 FormatJS +Copyright (c) Andrew Arnott +Copyright 2019 LLVM Project +Copyright (c) 1998 Microsoft +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation +Copyright (c) 2011, Google Inc. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 2015 Andrew Gallant +Copyright (c) 2022, Wojciech Mula +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2022, Geoff Langdale +Copyright (c) 2005-2020 Rich Felker +Copyright (c) 2012-2021 Yann Collet +Copyright (c) Microsoft Corporation +Copyright (c) 2007 James Newton-King +Copyright (c) 1991-2022 Unicode, Inc. +Copyright (c) 2013-2017, Alfred Klomp +Copyright (c) 2018 Nemanja Mijailovic +Copyright 2012 the V8 project authors +Copyright (c) 1999 Lucent Technologies +Copyright (c) 2008-2016, Wojciech Mula +Copyright (c) 2011-2020 Microsoft Corp +Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2015-2018, Wojciech Mula +Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2011-2015 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski +Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) +Copyright (c) .NET Foundation Contributors +(c) 1995-2024 Jean-loup Gailly and Mark Adler +Copyright (c) 2020 Mara Bos +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2012 - present, Victor Zverovich +Copyright (c) 2006 Jb Evain (jbevain@gmail.com) +Copyright (c) 2008-2020 Advanced Micro Devices, Inc. +Copyright (c) 2019 Microsoft Corporation, Daan Leijen +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Portions (c) International Organization for Standardization 1986 +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright (c) 1980, 1986, 1993 The Regents of the University of California +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +System.DirectoryServices.Protocols 9.0.9 - MIT + + +Copyright (c) 2021 +Copyright (c) Six Labors +(c) Microsoft Corporation +Copyright (c) 2022 FormatJS +Copyright (c) Andrew Arnott +Copyright 2019 LLVM Project +Copyright (c) 1998 Microsoft +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation +Copyright (c) 2011, Google Inc. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 2015 Andrew Gallant +Copyright (c) 2022, Wojciech Mula +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2022, Geoff Langdale +Copyright (c) 2005-2020 Rich Felker +Copyright (c) 2012-2021 Yann Collet +Copyright (c) Microsoft Corporation +Copyright (c) 2007 James Newton-King +Copyright (c) 1991-2022 Unicode, Inc. +Copyright (c) 2013-2017, Alfred Klomp +Copyright (c) 2018 Nemanja Mijailovic +Copyright 2012 the V8 project authors +Copyright (c) 1999 Lucent Technologies +Copyright (c) 2008-2016, Wojciech Mula +Copyright (c) 2011-2020 Microsoft Corp +Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2015-2018, Wojciech Mula +Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2011-2015 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski +Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) +Copyright (c) .NET Foundation Contributors +(c) 1995-2024 Jean-loup Gailly and Mark Adler +Copyright (c) 2020 Mara Bos +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2012 - present, Victor Zverovich +Copyright (c) 2006 Jb Evain (jbevain@gmail.com) +Copyright (c) 2008-2020 Advanced Micro Devices, Inc. +Copyright (c) 2019 Microsoft Corporation, Daan Leijen +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Portions (c) International Organization for Standardization 1986 +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright (c) 1980, 1986, 1993 The Regents of the University of California +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +System.Drawing.Common 9.0.9 - MIT + + +(c) Microsoft Corporation +Copyright (c) Sven Groot (Ookii.org) 2009 +Copyright (c) .NET Foundation and Contributors + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +--------------------------------------------------------- + +--------------------------------------------------------- + +System.IO.Packaging 9.0.9 - MIT + + +Copyright (c) 2021 +Copyright (c) Six Labors +(c) Microsoft Corporation +Copyright (c) 2022 FormatJS +Copyright (c) Andrew Arnott +Copyright 2019 LLVM Project +Copyright (c) 1998 Microsoft +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation +Copyright (c) 2011, Google Inc. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 2015 Andrew Gallant +Copyright (c) 2022, Wojciech Mula +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2022, Geoff Langdale +Copyright (c) 2005-2020 Rich Felker +Copyright (c) 2012-2021 Yann Collet +Copyright (c) Microsoft Corporation +Copyright (c) 2007 James Newton-King +Copyright (c) 1991-2022 Unicode, Inc. +Copyright (c) 2013-2017, Alfred Klomp +Copyright (c) 2018 Nemanja Mijailovic +Copyright 2012 the V8 project authors +Copyright (c) 1999 Lucent Technologies +Copyright (c) 2008-2016, Wojciech Mula +Copyright (c) 2011-2020 Microsoft Corp +Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2015-2018, Wojciech Mula +Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2011-2015 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski +Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) +Copyright (c) .NET Foundation Contributors +(c) 1995-2024 Jean-loup Gailly and Mark Adler +Copyright (c) 2020 Mara Bos +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2012 - present, Victor Zverovich +Copyright (c) 2006 Jb Evain (jbevain@gmail.com) +Copyright (c) 2008-2020 Advanced Micro Devices, Inc. +Copyright (c) 2019 Microsoft Corporation, Daan Leijen +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Portions (c) International Organization for Standardization 1986 +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright (c) 1980, 1986, 1993 The Regents of the University of California +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +System.IO.Ports 9.0.9 - MIT + + +Copyright (c) 2021 +Copyright (c) Six Labors +(c) Microsoft Corporation +Copyright (c) 2022 FormatJS +Copyright (c) Andrew Arnott +Copyright 2019 LLVM Project +Copyright (c) 1998 Microsoft +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation +Copyright (c) 2011, Google Inc. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 2015 Andrew Gallant +Copyright (c) 2022, Wojciech Mula +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2022, Geoff Langdale +Copyright (c) 2005-2020 Rich Felker +Copyright (c) 2012-2021 Yann Collet +Copyright (c) Microsoft Corporation +Copyright (c) 2007 James Newton-King +Copyright (c) 1991-2022 Unicode, Inc. +Copyright (c) 2013-2017, Alfred Klomp +Copyright (c) 2018 Nemanja Mijailovic +Copyright 2012 the V8 project authors +Copyright (c) 1999 Lucent Technologies +Copyright (c) 2008-2016, Wojciech Mula +Copyright (c) 2011-2020 Microsoft Corp +Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2015-2018, Wojciech Mula +Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2011-2015 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski +Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) +Copyright (c) .NET Foundation Contributors +(c) 1995-2024 Jean-loup Gailly and Mark Adler +Copyright (c) 2020 Mara Bos +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2012 - present, Victor Zverovich +Copyright (c) 2006 Jb Evain (jbevain@gmail.com) +Copyright (c) 2008-2020 Advanced Micro Devices, Inc. +Copyright (c) 2019 Microsoft Corporation, Daan Leijen +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Portions (c) International Organization for Standardization 1986 +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright (c) 1980, 1986, 1993 The Regents of the University of California +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +System.Management 9.0.9 - MIT + + +Copyright (c) 2021 +Copyright (c) Six Labors +(c) Microsoft Corporation +Copyright (c) 2022 FormatJS +Copyright (c) Andrew Arnott +Copyright 2019 LLVM Project +Copyright (c) 1998 Microsoft +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation +Copyright (c) 2011, Google Inc. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 2015 Andrew Gallant +Copyright (c) 2022, Wojciech Mula +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2022, Geoff Langdale +Copyright (c) 2005-2020 Rich Felker +Copyright (c) 2012-2021 Yann Collet +Copyright (c) Microsoft Corporation +Copyright (c) 2007 James Newton-King +Copyright (c) 1991-2022 Unicode, Inc. +Copyright (c) 2013-2017, Alfred Klomp +Copyright (c) 2018 Nemanja Mijailovic +Copyright 2012 the V8 project authors +Copyright (c) 1999 Lucent Technologies +Copyright (c) 2008-2016, Wojciech Mula +Copyright (c) 2011-2020 Microsoft Corp +Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2015-2018, Wojciech Mula +Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2011-2015 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski +Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) +Copyright (c) .NET Foundation Contributors +(c) 1995-2024 Jean-loup Gailly and Mark Adler +Copyright (c) 2020 Mara Bos +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2012 - present, Victor Zverovich +Copyright (c) 2006 Jb Evain (jbevain@gmail.com) +Copyright (c) 2008-2020 Advanced Micro Devices, Inc. +Copyright (c) 2019 Microsoft Corporation, Daan Leijen +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Portions (c) International Organization for Standardization 1986 +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright (c) 1980, 1986, 1993 The Regents of the University of California +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +System.Net.Http.WinHttpHandler 9.0.9 - MIT + + +Copyright (c) 2021 +Copyright (c) Six Labors +(c) Microsoft Corporation +Copyright (c) 2022 FormatJS +Copyright (c) Andrew Arnott +Copyright 2019 LLVM Project +Copyright (c) 1998 Microsoft +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation +Copyright (c) 2011, Google Inc. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 2015 Andrew Gallant +Copyright (c) 2022, Wojciech Mula +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2022, Geoff Langdale +Copyright (c) 2005-2020 Rich Felker +Copyright (c) 2012-2021 Yann Collet +Copyright (c) Microsoft Corporation +Copyright (c) 2007 James Newton-King +Copyright (c) 1991-2022 Unicode, Inc. +Copyright (c) 2013-2017, Alfred Klomp +Copyright (c) 2018 Nemanja Mijailovic +Copyright 2012 the V8 project authors +Copyright (c) 1999 Lucent Technologies +Copyright (c) 2008-2016, Wojciech Mula +Copyright (c) 2011-2020 Microsoft Corp +Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2015-2018, Wojciech Mula +Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2011-2015 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski +Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) +Copyright (c) .NET Foundation Contributors +(c) 1995-2024 Jean-loup Gailly and Mark Adler +Copyright (c) 2020 Mara Bos +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2012 - present, Victor Zverovich +Copyright (c) 2006 Jb Evain (jbevain@gmail.com) +Copyright (c) 2008-2020 Advanced Micro Devices, Inc. +Copyright (c) 2019 Microsoft Corporation, Daan Leijen +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Portions (c) International Organization for Standardization 1986 +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright (c) 1980, 1986, 1993 The Regents of the University of California +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +System.Private.ServiceModel 4.10.3 - MIT + + +(c) Microsoft Corporation +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2000-2014 The Legion of the Bouncy Castle Inc. (http://www.bouncycastle.org) + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +System.Reflection.Context 9.0.9 - MIT + + +Copyright (c) 2021 +Copyright (c) Six Labors +(c) Microsoft Corporation +Copyright (c) 2022 FormatJS +Copyright (c) Andrew Arnott +Copyright 2019 LLVM Project +Copyright (c) 1998 Microsoft +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation +Copyright (c) 2011, Google Inc. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 2015 Andrew Gallant +Copyright (c) 2022, Wojciech Mula +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2022, Geoff Langdale +Copyright (c) 2005-2020 Rich Felker +Copyright (c) 2012-2021 Yann Collet +Copyright (c) Microsoft Corporation +Copyright (c) 2007 James Newton-King +Copyright (c) 1991-2022 Unicode, Inc. +Copyright (c) 2013-2017, Alfred Klomp +Copyright (c) 2018 Nemanja Mijailovic +Copyright 2012 the V8 project authors +Copyright (c) 1999 Lucent Technologies +Copyright (c) 2008-2016, Wojciech Mula +Copyright (c) 2011-2020 Microsoft Corp +Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2015-2018, Wojciech Mula +Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2011-2015 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski +Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) +Copyright (c) .NET Foundation Contributors +(c) 1995-2024 Jean-loup Gailly and Mark Adler +Copyright (c) 2020 Mara Bos +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2012 - present, Victor Zverovich +Copyright (c) 2006 Jb Evain (jbevain@gmail.com) +Copyright (c) 2008-2020 Advanced Micro Devices, Inc. +Copyright (c) 2019 Microsoft Corporation, Daan Leijen +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Portions (c) International Organization for Standardization 1986 +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright (c) 1980, 1986, 1993 The Regents of the University of California +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +System.Runtime.Caching 9.0.9 - MIT + + +Copyright (c) 2021 +Copyright (c) Six Labors +(c) Microsoft Corporation +Copyright (c) 2022 FormatJS +Copyright (c) Andrew Arnott +Copyright 2019 LLVM Project +Copyright (c) 1998 Microsoft +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation +Copyright (c) 2011, Google Inc. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 2015 Andrew Gallant +Copyright (c) 2022, Wojciech Mula +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2022, Geoff Langdale +Copyright (c) 2005-2020 Rich Felker +Copyright (c) 2012-2021 Yann Collet +Copyright (c) Microsoft Corporation +Copyright (c) 2007 James Newton-King +Copyright (c) 1991-2022 Unicode, Inc. +Copyright (c) 2013-2017, Alfred Klomp +Copyright (c) 2018 Nemanja Mijailovic +Copyright 2012 the V8 project authors +Copyright (c) 1999 Lucent Technologies +Copyright (c) 2008-2016, Wojciech Mula +Copyright (c) 2011-2020 Microsoft Corp +Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2015-2018, Wojciech Mula +Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2011-2015 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski +Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) +Copyright (c) .NET Foundation Contributors +(c) 1995-2024 Jean-loup Gailly and Mark Adler +Copyright (c) 2020 Mara Bos +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2012 - present, Victor Zverovich +Copyright (c) 2006 Jb Evain (jbevain@gmail.com) +Copyright (c) 2008-2020 Advanced Micro Devices, Inc. +Copyright (c) 2019 Microsoft Corporation, Daan Leijen +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Portions (c) International Organization for Standardization 1986 +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright (c) 1980, 1986, 1993 The Regents of the University of California +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +System.Security.Cryptography.Pkcs 9.0.9 - MIT + + +Copyright (c) 2021 +Copyright (c) Six Labors +(c) Microsoft Corporation +Copyright (c) 2022 FormatJS +Copyright (c) Andrew Arnott +Copyright 2019 LLVM Project +Copyright (c) 1998 Microsoft +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation +Copyright (c) 2011, Google Inc. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 2015 Andrew Gallant +Copyright (c) 2022, Wojciech Mula +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2022, Geoff Langdale +Copyright (c) 2005-2020 Rich Felker +Copyright (c) 2012-2021 Yann Collet +Copyright (c) Microsoft Corporation +Copyright (c) 2007 James Newton-King +Copyright (c) 1991-2022 Unicode, Inc. +Copyright (c) 2013-2017, Alfred Klomp +Copyright (c) 2018 Nemanja Mijailovic +Copyright 2012 the V8 project authors +Copyright (c) 1999 Lucent Technologies +Copyright (c) 2008-2016, Wojciech Mula +Copyright (c) 2011-2020 Microsoft Corp +Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2015-2018, Wojciech Mula +Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2011-2015 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski +Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) +Copyright (c) .NET Foundation Contributors +(c) 1995-2024 Jean-loup Gailly and Mark Adler +Copyright (c) 2020 Mara Bos +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2012 - present, Victor Zverovich +Copyright (c) 2006 Jb Evain (jbevain@gmail.com) +Copyright (c) 2008-2020 Advanced Micro Devices, Inc. +Copyright (c) 2019 Microsoft Corporation, Daan Leijen +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Portions (c) International Organization for Standardization 1986 +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright (c) 1980, 1986, 1993 The Regents of the University of California +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors -THIRD-PARTY SOFTWARE NOTICES AND INFORMATION -Do Not Translate or Localize +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +System.Security.Cryptography.ProtectedData 9.0.9 - MIT + + +Copyright (c) 2021 +Copyright (c) Six Labors +(c) Microsoft Corporation +Copyright (c) 2022 FormatJS +Copyright (c) Andrew Arnott +Copyright 2019 LLVM Project +Copyright (c) 1998 Microsoft +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation +Copyright (c) 2011, Google Inc. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 2015 Andrew Gallant +Copyright (c) 2022, Wojciech Mula +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2022, Geoff Langdale +Copyright (c) 2005-2020 Rich Felker +Copyright (c) 2012-2021 Yann Collet +Copyright (c) Microsoft Corporation +Copyright (c) 2007 James Newton-King +Copyright (c) 1991-2022 Unicode, Inc. +Copyright (c) 2013-2017, Alfred Klomp +Copyright (c) 2018 Nemanja Mijailovic +Copyright 2012 the V8 project authors +Copyright (c) 1999 Lucent Technologies +Copyright (c) 2008-2016, Wojciech Mula +Copyright (c) 2011-2020 Microsoft Corp +Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2015-2018, Wojciech Mula +Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2011-2015 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski +Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) +Copyright (c) .NET Foundation Contributors +(c) 1995-2024 Jean-loup Gailly and Mark Adler +Copyright (c) 2020 Mara Bos +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2012 - present, Victor Zverovich +Copyright (c) 2006 Jb Evain (jbevain@gmail.com) +Copyright (c) 2008-2020 Advanced Micro Devices, Inc. +Copyright (c) 2019 Microsoft Corporation, Daan Leijen +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Portions (c) International Organization for Standardization 1986 +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright (c) 1980, 1986, 1993 The Regents of the University of California +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +System.Security.Cryptography.Xml 9.0.9 - MIT + + +Copyright (c) 2021 +Copyright (c) Six Labors +(c) Microsoft Corporation +Copyright (c) 2022 FormatJS +Copyright (c) Andrew Arnott +Copyright 2019 LLVM Project +Copyright (c) 1998 Microsoft +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation +Copyright (c) 2011, Google Inc. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 2015 Andrew Gallant +Copyright (c) 2022, Wojciech Mula +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2022, Geoff Langdale +Copyright (c) 2005-2020 Rich Felker +Copyright (c) 2012-2021 Yann Collet +Copyright (c) Microsoft Corporation +Copyright (c) 2007 James Newton-King +Copyright (c) 1991-2022 Unicode, Inc. +Copyright (c) 2013-2017, Alfred Klomp +Copyright (c) 2018 Nemanja Mijailovic +Copyright 2012 the V8 project authors +Copyright (c) 1999 Lucent Technologies +Copyright (c) 2008-2016, Wojciech Mula +Copyright (c) 2011-2020 Microsoft Corp +Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2015-2018, Wojciech Mula +Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2011-2015 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski +Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) +Copyright (c) .NET Foundation Contributors +(c) 1995-2024 Jean-loup Gailly and Mark Adler +Copyright (c) 2020 Mara Bos +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2012 - present, Victor Zverovich +Copyright (c) 2006 Jb Evain (jbevain@gmail.com) +Copyright (c) 2008-2020 Advanced Micro Devices, Inc. +Copyright (c) 2019 Microsoft Corporation, Daan Leijen +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Portions (c) International Organization for Standardization 1986 +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright (c) 1980, 1986, 1993 The Regents of the University of California +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +System.Security.Permissions 9.0.9 - MIT + + +Copyright (c) 2021 +Copyright (c) Six Labors +(c) Microsoft Corporation +Copyright (c) 2022 FormatJS +Copyright (c) Andrew Arnott +Copyright 2019 LLVM Project +Copyright (c) 1998 Microsoft +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation +Copyright (c) 2011, Google Inc. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 2015 Andrew Gallant +Copyright (c) 2022, Wojciech Mula +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2022, Geoff Langdale +Copyright (c) 2005-2020 Rich Felker +Copyright (c) 2012-2021 Yann Collet +Copyright (c) Microsoft Corporation +Copyright (c) 2007 James Newton-King +Copyright (c) 1991-2022 Unicode, Inc. +Copyright (c) 2013-2017, Alfred Klomp +Copyright (c) 2018 Nemanja Mijailovic +Copyright 2012 the V8 project authors +Copyright (c) 1999 Lucent Technologies +Copyright (c) 2008-2016, Wojciech Mula +Copyright (c) 2011-2020 Microsoft Corp +Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2015-2018, Wojciech Mula +Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2011-2015 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski +Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) +Copyright (c) .NET Foundation Contributors +(c) 1995-2024 Jean-loup Gailly and Mark Adler +Copyright (c) 2020 Mara Bos +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2012 - present, Victor Zverovich +Copyright (c) 2006 Jb Evain (jbevain@gmail.com) +Copyright (c) 2008-2020 Advanced Micro Devices, Inc. +Copyright (c) 2019 Microsoft Corporation, Daan Leijen +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Portions (c) International Organization for Standardization 1986 +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright (c) 1980, 1986, 1993 The Regents of the University of California +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +System.ServiceModel.Duplex 4.10.3 - MIT + + +(c) Microsoft Corporation +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2000-2014 The Legion of the Bouncy Castle Inc. (http://www.bouncycastle.org) + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +System.ServiceModel.Http 4.10.3 - MIT + + +(c) Microsoft Corporation +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2000-2014 The Legion of the Bouncy Castle Inc. (http://www.bouncycastle.org) + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +System.ServiceModel.NetTcp 4.10.3 - MIT + + +(c) Microsoft Corporation +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2000-2014 The Legion of the Bouncy Castle Inc. (http://www.bouncycastle.org) + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +System.ServiceModel.Primitives 4.10.3 - MIT + + +(c) Microsoft Corporation +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2000-2014 The Legion of the Bouncy Castle Inc. (http://www.bouncycastle.org) + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +System.ServiceModel.Security 4.10.3 - MIT + + +(c) Microsoft Corporation +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2000-2014 The Legion of the Bouncy Castle Inc. (http://www.bouncycastle.org) + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +System.ServiceModel.Syndication 9.0.9 - MIT + + +Copyright (c) 2021 +Copyright (c) Six Labors +(c) Microsoft Corporation +Copyright (c) 2022 FormatJS +Copyright (c) Andrew Arnott +Copyright 2019 LLVM Project +Copyright (c) 1998 Microsoft +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation +Copyright (c) 2011, Google Inc. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 2015 Andrew Gallant +Copyright (c) 2022, Wojciech Mula +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2022, Geoff Langdale +Copyright (c) 2005-2020 Rich Felker +Copyright (c) 2012-2021 Yann Collet +Copyright (c) Microsoft Corporation +Copyright (c) 2007 James Newton-King +Copyright (c) 1991-2022 Unicode, Inc. +Copyright (c) 2013-2017, Alfred Klomp +Copyright (c) 2018 Nemanja Mijailovic +Copyright 2012 the V8 project authors +Copyright (c) 1999 Lucent Technologies +Copyright (c) 2008-2016, Wojciech Mula +Copyright (c) 2011-2020 Microsoft Corp +Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2015-2018, Wojciech Mula +Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2011-2015 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski +Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) +Copyright (c) .NET Foundation Contributors +(c) 1995-2024 Jean-loup Gailly and Mark Adler +Copyright (c) 2020 Mara Bos +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2012 - present, Victor Zverovich +Copyright (c) 2006 Jb Evain (jbevain@gmail.com) +Copyright (c) 2008-2020 Advanced Micro Devices, Inc. +Copyright (c) 2019 Microsoft Corporation, Daan Leijen +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Portions (c) International Organization for Standardization 1986 +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright (c) 1980, 1986, 1993 The Regents of the University of California +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +System.ServiceProcess.ServiceController 9.0.9 - MIT + + +Copyright (c) 2021 +Copyright (c) Six Labors +(c) Microsoft Corporation +Copyright (c) 2022 FormatJS +Copyright (c) Andrew Arnott +Copyright 2019 LLVM Project +Copyright (c) 1998 Microsoft +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation +Copyright (c) 2011, Google Inc. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 2015 Andrew Gallant +Copyright (c) 2022, Wojciech Mula +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2022, Geoff Langdale +Copyright (c) 2005-2020 Rich Felker +Copyright (c) 2012-2021 Yann Collet +Copyright (c) Microsoft Corporation +Copyright (c) 2007 James Newton-King +Copyright (c) 1991-2022 Unicode, Inc. +Copyright (c) 2013-2017, Alfred Klomp +Copyright (c) 2018 Nemanja Mijailovic +Copyright 2012 the V8 project authors +Copyright (c) 1999 Lucent Technologies +Copyright (c) 2008-2016, Wojciech Mula +Copyright (c) 2011-2020 Microsoft Corp +Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2015-2018, Wojciech Mula +Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2011-2015 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski +Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) +Copyright (c) .NET Foundation Contributors +(c) 1995-2024 Jean-loup Gailly and Mark Adler +Copyright (c) 2020 Mara Bos +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2012 - present, Victor Zverovich +Copyright (c) 2006 Jb Evain (jbevain@gmail.com) +Copyright (c) 2008-2020 Advanced Micro Devices, Inc. +Copyright (c) 2019 Microsoft Corporation, Daan Leijen +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Portions (c) International Organization for Standardization 1986 +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright (c) 1980, 1986, 1993 The Regents of the University of California +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass -The software is based on or incorporates material from the projects listed below (collectively, “Third Party Code”). Microsoft is not the original author of the Third Party Code. The original copyright notice and license, under which Microsoft received such Third Party Code, are set forth below. Microsoft reserves all rights not expressly granted herein, whether by implication, estoppel or otherwise. +The MIT License (MIT) +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +System.Speech 9.0.9 - MIT + + +Copyright (c) 2021 +Copyright (c) Six Labors +(c) Microsoft Corporation +Copyright (c) 2022 FormatJS +Copyright (c) Andrew Arnott +Copyright 2019 LLVM Project +Copyright (c) 1998 Microsoft +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation +Copyright (c) 2011, Google Inc. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 2015 Andrew Gallant +Copyright (c) 2022, Wojciech Mula +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2022, Geoff Langdale +Copyright (c) 2005-2020 Rich Felker +Copyright (c) 2012-2021 Yann Collet +Copyright (c) Microsoft Corporation +Copyright (c) 2007 James Newton-King +Copyright (c) 1991-2022 Unicode, Inc. +Copyright (c) 2013-2017, Alfred Klomp +Copyright (c) 2018 Nemanja Mijailovic +Copyright 2012 the V8 project authors +Copyright (c) 1999 Lucent Technologies +Copyright (c) 2008-2016, Wojciech Mula +Copyright (c) 2011-2020 Microsoft Corp +Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2015-2018, Wojciech Mula +Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2011-2015 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski +Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) +Copyright (c) .NET Foundation Contributors +(c) 1995-2024 Jean-loup Gailly and Mark Adler +Copyright (c) 2020 Mara Bos +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2012 - present, Victor Zverovich +Copyright (c) 2006 Jb Evain (jbevain@gmail.com) +Copyright (c) 2008-2020 Advanced Micro Devices, Inc. +Copyright (c) 2019 Microsoft Corporation, Daan Leijen +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Portions (c) International Organization for Standardization 1986 +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright (c) 1980, 1986, 1993 The Regents of the University of California +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +System.Threading.AccessControl 9.0.9 - MIT + + +Copyright (c) 2021 +Copyright (c) Six Labors +(c) Microsoft Corporation +Copyright (c) 2022 FormatJS +Copyright (c) Andrew Arnott +Copyright 2019 LLVM Project +Copyright (c) 1998 Microsoft +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation +Copyright (c) 2011, Google Inc. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 2015 Andrew Gallant +Copyright (c) 2022, Wojciech Mula +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2022, Geoff Langdale +Copyright (c) 2005-2020 Rich Felker +Copyright (c) 2012-2021 Yann Collet +Copyright (c) Microsoft Corporation +Copyright (c) 2007 James Newton-King +Copyright (c) 1991-2022 Unicode, Inc. +Copyright (c) 2013-2017, Alfred Klomp +Copyright (c) 2018 Nemanja Mijailovic +Copyright 2012 the V8 project authors +Copyright (c) 1999 Lucent Technologies +Copyright (c) 2008-2016, Wojciech Mula +Copyright (c) 2011-2020 Microsoft Corp +Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2015-2018, Wojciech Mula +Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2011-2015 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski +Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) +Copyright (c) .NET Foundation Contributors +(c) 1995-2024 Jean-loup Gailly and Mark Adler +Copyright (c) 2020 Mara Bos +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2012 - present, Victor Zverovich +Copyright (c) 2006 Jb Evain (jbevain@gmail.com) +Copyright (c) 2008-2020 Advanced Micro Devices, Inc. +Copyright (c) 2019 Microsoft Corporation, Daan Leijen +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Portions (c) International Organization for Standardization 1986 +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright (c) 1980, 1986, 1993 The Regents of the University of California +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +System.Web.Services.Description 8.1.2 - MIT + + +(c) Microsoft Corporation +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2000-2014 The Legion of the Bouncy Castle Inc. (http://www.bouncycastle.org) Provided + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + +--------------------------------------------------------- + +System.Windows.Extensions 9.0.9 - MIT + + +Copyright (c) 2021 +Copyright (c) Six Labors +(c) Microsoft Corporation +Copyright (c) 2022 FormatJS +Copyright (c) Andrew Arnott +Copyright 2019 LLVM Project +Copyright (c) 1998 Microsoft +Copyright 2018 Daniel Lemire +Copyright (c) .NET Foundation +Copyright (c) 2011, Google Inc. +Copyright (c) 2020 Dan Shechter +(c) 1997-2005 Sean Eron Anderson +Copyright (c) 2015 Andrew Gallant +Copyright (c) 2022, Wojciech Mula +Copyright (c) 2017 Yoshifumi Kawai +Copyright (c) 2022, Geoff Langdale +Copyright (c) 2005-2020 Rich Felker +Copyright (c) 2012-2021 Yann Collet +Copyright (c) Microsoft Corporation +Copyright (c) 2007 James Newton-King +Copyright (c) 1991-2022 Unicode, Inc. +Copyright (c) 2013-2017, Alfred Klomp +Copyright (c) 2018 Nemanja Mijailovic +Copyright 2012 the V8 project authors +Copyright (c) 1999 Lucent Technologies +Copyright (c) 2008-2016, Wojciech Mula +Copyright (c) 2011-2020 Microsoft Corp +Copyright (c) 2015-2017, Wojciech Mula +Copyright (c) 2015-2018, Wojciech Mula +Copyright (c) 2005-2007, Nick Galbreath +Copyright (c) 2015 The Chromium Authors +Copyright (c) 2018 Alexander Chermyanin +Copyright (c) The Internet Society 1997 +Copyright (c) 2004-2006 Intel Corporation +Copyright (c) 2011-2015 Intel Corporation +Copyright (c) 2013-2017, Milosz Krajewski +Copyright (c) 2016-2017, Matthieu Darbois +Copyright (c) The Internet Society (2003) +Copyright (c) .NET Foundation Contributors +(c) 1995-2024 Jean-loup Gailly and Mark Adler +Copyright (c) 2020 Mara Bos +Copyright (c) .NET Foundation and Contributors +Copyright (c) 2012 - present, Victor Zverovich +Copyright (c) 2006 Jb Evain (jbevain@gmail.com) +Copyright (c) 2008-2020 Advanced Micro Devices, Inc. +Copyright (c) 2019 Microsoft Corporation, Daan Leijen +Copyright (c) 2011 Novell, Inc (http://www.novell.com) +Copyright (c) 2015 Xamarin, Inc (http://www.xamarin.com) +Copyright (c) 2009, 2010, 2013-2016 by the Brotli Authors +Copyright (c) 2014 Ryan Juckett http://www.ryanjuckett.com +Copyright (c) 1990- 1993, 1996 Open Software Foundation, Inc. +Portions (c) International Organization for Standardization 1986 +Copyright (c) YEAR W3C(r) (MIT, ERCIM, Keio, Beihang) Disclaimers +Copyright (c) 2015 THL A29 Limited, a Tencent company, and Milo Yip +Copyright (c) 1980, 1986, 1993 The Regents of the University of California +Copyright 2011, 2012, 2013, 2014, 2015, 2016, 2017, 2018 The Regents of the University of California +Copyright (c) 1989 by Hewlett-Packard Company, Palo Alto, Ca. & Digital Equipment Corporation, Maynard, Mass + +The MIT License (MIT) + +Copyright (c) .NET Foundation and Contributors + +All rights reserved. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + + +--------------------------------------------------------- + + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +Additional - + +------------------------------------------------- +Microsoft.PowerShell.Archive +------------------------------------------------- + +Copyright (c) 2016 Microsoft Corporation. + +The MIT License (MIT) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +------------------------------------------------- +Microsoft.Management.Infrastructure.Runtime.Unix +Microsoft.Management.Infrastructure +------------------------------------------------- + +Copyright (c) Microsoft Corporation + +All rights reserved. + +MIT License + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the ""Software""), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +-------------------------------------------------------- +• NuGet.Common +• NuGet.Configuration +• NuGet.DependencyResolver.Core +• NuGet.Frameworks +• NuGet.LibraryModel +• NuGet.Packaging +• NuGet.Packaging.Core +• NuGet.Packaging.Core.Types +• NuGet.ProjectModel +• NuGet.Protocol.Core.Types +• NuGet.Protocol.Core.v3 +• NuGet.Repositories +• NuGet.RuntimeModel +• NuGet.Versioning +---------------------------------------------------------- + +Copyright (c) .NET Foundation. All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); you may not use +these files except in compliance with the License. You may obtain a copy of the +License at + +https://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed +under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. + +------------------------------------------------- +PackageManagement +------------------------------------------------- + +Copyright (c) Microsoft Corporation +All rights reserved. + +MIT License + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the Software), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------- +PowerShellGet +------------------------------------------------- + +Copyright (c) Microsoft Corporation + +All rights reserved. + +The MIT License (MIT) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. --------------------------------------------- File: PSReadLine --------------------------------------------- -https://github.com/lzybkr/PSReadLine +https://github.com/PowerShell/PSReadLine Copyright (c) 2013, Jason Shirk @@ -18,13 +4995,13 @@ All rights reserved. BSD License Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are met: +modification, are permitted provided that the following conditions are met: 1. Redistributions of source code must retain the above copyright notice, this - list of conditions and the following disclaimer. + list of conditions and the following disclaimer. 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation - and/or other materials provided with the distribution. + and/or other materials provided with the distribution. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED @@ -37,42 +5014,29 @@ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - - ----------------------------------------------- -File: Hashtables from ConvertFrom-json ----------------------------------------------- - -http://stackoverflow.com/questions/22002748/hashtables-from-convertfrom-json-have-different-type-from-powershells-built-in-h - -Copyright (c) 2015 Dave Wyatt. All rights reserved. - -All rights reserved. - -MIT License - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the ""Software""), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - ------------------------------------------------- -File: PackageManagement +ThreadJob ------------------------------------------------- -Copyright (c) Microsoft Corporation. +Copyright (c) 2018 Paul Higinbotham -All rights reserved. +MIT License + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. -See the License for the specific language governing permissions and -limitations under the License. diff --git a/appveyor.yml b/appveyor.yml deleted file mode 100644 index 5c2ab1fc7fd..00000000000 --- a/appveyor.yml +++ /dev/null @@ -1,28 +0,0 @@ -# version is set in tools\appveyor.psm1 - Invoke-AppVeyorInstall - -image: Visual Studio 2017 - -# cache version - netcoreapp.2.0.0 -cache: - - '%LocalAppData%\Microsoft\dotnet -> appveyor.yml' - - '%HOMEDRIVE%%HOMEPATH%\.nuget\packages -> appveyor.yml' - -nuget: - project_feed: true - -install: - - git submodule update --init - - ps: Import-Module .\tools\Appveyor.psm1 - - ps: Invoke-AppveyorInstall - -build_script: - - ps: Invoke-AppveyorBuild - -test_script: - - ps: Invoke-AppveyorTest - -after_test: - - ps: Invoke-AppVeyorAfterTest - -on_finish: - - ps: Invoke-AppveyorFinish diff --git a/assets/AppImageThirdPartyNotices.txt b/assets/AppImageThirdPartyNotices.txt deleted file mode 100644 index 53e24978b4d..00000000000 --- a/assets/AppImageThirdPartyNotices.txt +++ /dev/null @@ -1,506 +0,0 @@ -------------------------------------------- START OF THIRD PARTY NOTICE ----------------------------------------- - - This file is based on or incorporates material from the projects listed below (Third Party IP). The original copyright notice and the license under which Microsoft received such Third Party IP, are set forth below. Such licenses and notices are provided for informational purposes only. Microsoft licenses the Third Party IP to you under the licensing terms for the Microsoft product. Microsoft reserves all other rights not expressly granted under this agreement, whether by implication, estoppel or otherwise. - - - - -Copyright 1991-2016 Unicode, Inc. All rights reserved. -Distributed under the Terms of Use in http://www.unicode.org/copyright.html - -Permission is hereby granted, free of charge, to any person obtaining -a copy of the Unicode data files and any associated documentation -(the "Data Files") or Unicode software and any associated documentation -(the "Software") to deal in the Data Files or Software -without restriction, including without limitation the rights to use, -copy, modify, merge, publish, distribute, and/or sell copies of -the Data Files or Software, and to permit persons to whom the Data Files -or Software are furnished to do so, provided that either -(a) this copyright and permission notice appear with all copies -of the Data Files or Software, or -(b) this copyright and permission notice appear in associated -Documentation. - -THE DATA FILES AND SOFTWARE ARE PROVIDED "AS IS", WITHOUT WARRANTY OF -ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT OF THIRD PARTY RIGHTS. -IN NO EVENT SHALL THE COPYRIGHT HOLDER OR HOLDERS INCLUDED IN THIS -NOTICE BE LIABLE FOR ANY CLAIM, OR ANY SPECIAL INDIRECT OR CONSEQUENTIAL -DAMAGES, OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, -DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER -TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR -PERFORMANCE OF THE DATA FILES OR SOFTWARE. - -Except as contained in this notice, the name of a copyright holder -shall not be used in advertising or otherwise to promote the sale, -use or other dealings in these Data Files or Software without prior -written authorization of the copyright holder. - ---------------------- - -Third-Party Software Licenses - -This section contains third-party software notices and/or additional -terms for licensed third-party software components included within ICU -libraries. - -1. ICU License - ICU 1.8.1 to ICU 57.1 - -COPYRIGHT AND PERMISSION NOTICE - -Copyright (c) 1995-2016 International Business Machines Corporation and others -All rights reserved. - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, and/or sell copies of the Software, and to permit persons -to whom the Software is furnished to do so, provided that the above -copyright notice(s) and this permission notice appear in all copies of -the Software and that both the above copyright notice(s) and this -permission notice appear in supporting documentation. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT -OF THIRD PARTY RIGHTS. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR -HOLDERS INCLUDED IN THIS NOTICE BE LIABLE FOR ANY CLAIM, OR ANY -SPECIAL INDIRECT OR CONSEQUENTIAL DAMAGES, OR ANY DAMAGES WHATSOEVER -RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF -CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN -CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - -Except as contained in this notice, the name of a copyright holder -shall not be used in advertising or otherwise to promote the sale, use -or other dealings in this Software without prior written authorization -of the copyright holder. - -All trademarks and registered trademarks mentioned herein are the -property of their respective owners. - -2. Chinese/Japanese Word Break Dictionary Data (cjdict.txt) - - # The Google Chrome software developed by Google is licensed under - # the BSD license. Other software included in this distribution is - # provided under other licenses, as set forth below. - # - # The BSD License - # http://opensource.org/licenses/bsd-license.php - # Copyright (C) 2006-2008, Google Inc. - # - # All rights reserved. - # - # Redistribution and use in source and binary forms, with or without - # modification, are permitted provided that the following conditions are met: - # - # Redistributions of source code must retain the above copyright notice, - # this list of conditions and the following disclaimer. - # Redistributions in binary form must reproduce the above - # copyright notice, this list of conditions and the following - # disclaimer in the documentation and/or other materials provided with - # the distribution. - # Neither the name of Google Inc. nor the names of its - # contributors may be used to endorse or promote products derived from - # this software without specific prior written permission. - # - # - # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND - # CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, - # INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - # MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE - # LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR - # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR - # BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - # LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - # NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - # - # - # The word list in cjdict.txt are generated by combining three word lists - # listed below with further processing for compound word breaking. The - # frequency is generated with an iterative training against Google web - # corpora. - # - # * Libtabe (Chinese) - # - https://sourceforge.net/project/?group_id=1519 - # - Its license terms and conditions are shown below. - # - # * IPADIC (Japanese) - # - http://chasen.aist-nara.ac.jp/chasen/distribution.html - # - Its license terms and conditions are shown below. - # - # ---------COPYING.libtabe ---- BEGIN-------------------- - # - # /* - # * Copyrighy (c) 1999 TaBE Project. - # * Copyright (c) 1999 Pai-Hsiang Hsiao. - # * All rights reserved. - # * - # * Redistribution and use in source and binary forms, with or without - # * modification, are permitted provided that the following conditions - # * are met: - # * - # * . Redistributions of source code must retain the above copyright - # * notice, this list of conditions and the following disclaimer. - # * . Redistributions in binary form must reproduce the above copyright - # * notice, this list of conditions and the following disclaimer in - # * the documentation and/or other materials provided with the - # * distribution. - # * . Neither the name of the TaBE Project nor the names of its - # * contributors may be used to endorse or promote products derived - # * from this software without specific prior written permission. - # * - # * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - # * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - # * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - # * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - # * REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - # * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - # * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - # * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - # * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - # * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - # * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED - # * OF THE POSSIBILITY OF SUCH DAMAGE. - # */ - # - # /* - # * Copyright (c) 1999 Computer Systems and Communication Lab, - # * Institute of Information Science, Academia - # * Sinica. All rights reserved. - # * - # * Redistribution and use in source and binary forms, with or without - # * modification, are permitted provided that the following conditions - # * are met: - # * - # * . Redistributions of source code must retain the above copyright - # * notice, this list of conditions and the following disclaimer. - # * . Redistributions in binary form must reproduce the above copyright - # * notice, this list of conditions and the following disclaimer in - # * the documentation and/or other materials provided with the - # * distribution. - # * . Neither the name of the Computer Systems and Communication Lab - # * nor the names of its contributors may be used to endorse or - # * promote products derived from this software without specific - # * prior written permission. - # * - # * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - # * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - # * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - # * FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - # * REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - # * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - # * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - # * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - # * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - # * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - # * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED - # * OF THE POSSIBILITY OF SUCH DAMAGE. - # */ - # - # Copyright 1996 Chih-Hao Tsai @ Beckman Institute, - # University of Illinois - # c-tsai4@uiuc.edu http://casper.beckman.uiuc.edu/~c-tsai4 - # - # ---------------COPYING.libtabe-----END-------------------------------- - # - # - # ---------------COPYING.ipadic-----BEGIN------------------------------- - # - # Copyright 2000, 2001, 2002, 2003 Nara Institute of Science - # and Technology. All Rights Reserved. - # - # Use, reproduction, and distribution of this software is permitted. - # Any copy of this software, whether in its original form or modified, - # must include both the above copyright notice and the following - # paragraphs. - # - # Nara Institute of Science and Technology (NAIST), - # the copyright holders, disclaims all warranties with regard to this - # software, including all implied warranties of merchantability and - # fitness, in no event shall NAIST be liable for - # any special, indirect or consequential damages or any damages - # whatsoever resulting from loss of use, data or profits, whether in an - # action of contract, negligence or other tortuous action, arising out - # of or in connection with the use or performance of this software. - # - # A large portion of the dictionary entries - # originate from ICOT Free Software. The following conditions for ICOT - # Free Software applies to the current dictionary as well. - # - # Each User may also freely distribute the Program, whether in its - # original form or modified, to any third party or parties, PROVIDED - # that the provisions of Section 3 ("NO WARRANTY") will ALWAYS appear - # on, or be attached to, the Program, which is distributed substantially - # in the same form as set out herein and that such intended - # distribution, if actually made, will neither violate or otherwise - # contravene any of the laws and regulations of the countries having - # jurisdiction over the User or the intended distribution itself. - # - # NO WARRANTY - # - # The program was produced on an experimental basis in the course of the - # research and development conducted during the project and is provided - # to users as so produced on an experimental basis. Accordingly, the - # program is provided without any warranty whatsoever, whether express, - # implied, statutory or otherwise. The term "warranty" used herein - # includes, but is not limited to, any warranty of the quality, - # performance, merchantability and fitness for a particular purpose of - # the program and the nonexistence of any infringement or violation of - # any right of any third party. - # - # Each user of the program will agree and understand, and be deemed to - # have agreed and understood, that there is no warranty whatsoever for - # the program and, accordingly, the entire risk arising from or - # otherwise connected with the program is assumed by the user. - # - # Therefore, neither ICOT, the copyright holder, or any other - # organization that participated in or was otherwise related to the - # development of the program and their respective officials, directors, - # officers and other employees shall be held liable for any and all - # damages, including, without limitation, general, special, incidental - # and consequential damages, arising out of or otherwise in connection - # with the use or inability to use the program or any product, material - # or result produced or otherwise obtained by using the program, - # regardless of whether they have been advised of, or otherwise had - # knowledge of, the possibility of such damages at any time during the - # project or thereafter. Each user will be deemed to have agreed to the - # foregoing by his or her commencement of use of the program. The term - # "use" as used herein includes, but is not limited to, the use, - # modification, copying and distribution of the program and the - # production of secondary products from the program. - # - # In the case where the program, whether in its original form or - # modified, was distributed or delivered to or received by a user from - # any person, organization or entity other than ICOT, unless it makes or - # grants independently of ICOT any specific warranty to the user in - # writing, such person, organization or entity, will also be exempted - # from and not be held liable to the user for any such damages as noted - # above as far as the program is concerned. - # - # ---------------COPYING.ipadic-----END---------------------------------- - -3. Lao Word Break Dictionary Data (laodict.txt) - - # Copyright (c) 2013 International Business Machines Corporation - # and others. All Rights Reserved. - # - # Project: http://code.google.com/p/lao-dictionary/ - # Dictionary: http://lao-dictionary.googlecode.com/git/Lao-Dictionary.txt - # License: http://lao-dictionary.googlecode.com/git/Lao-Dictionary-LICENSE.txt - # (copied below) - # - # This file is derived from the above dictionary, with slight - # modifications. - # ---------------------------------------------------------------------- - # Copyright (C) 2013 Brian Eugene Wilson, Robert Martin Campbell. - # All rights reserved. - # - # Redistribution and use in source and binary forms, with or without - # modification, - # are permitted provided that the following conditions are met: - # - # - # Redistributions of source code must retain the above copyright notice, this - # list of conditions and the following disclaimer. Redistributions in - # binary form must reproduce the above copyright notice, this list of - # conditions and the following disclaimer in the documentation and/or - # other materials provided with the distribution. - # - # - # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - # COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, - # INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES - # (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR - # SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - # HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, - # STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) - # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED - # OF THE POSSIBILITY OF SUCH DAMAGE. - # -------------------------------------------------------------------------- - -4. Burmese Word Break Dictionary Data (burmesedict.txt) - - # Copyright (c) 2014 International Business Machines Corporation - # and others. All Rights Reserved. - # - # This list is part of a project hosted at: - # github.com/kanyawtech/myanmar-karen-word-lists - # - # -------------------------------------------------------------------------- - # Copyright (c) 2013, LeRoy Benjamin Sharon - # All rights reserved. - # - # Redistribution and use in source and binary forms, with or without - # modification, are permitted provided that the following conditions - # are met: Redistributions of source code must retain the above - # copyright notice, this list of conditions and the following - # disclaimer. Redistributions in binary form must reproduce the - # above copyright notice, this list of conditions and the following - # disclaimer in the documentation and/or other materials provided - # with the distribution. - # - # Neither the name Myanmar Karen Word Lists, nor the names of its - # contributors may be used to endorse or promote products derived - # from this software without specific prior written permission. - # - # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND - # CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, - # INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - # MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE - # DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS - # BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - # EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED - # TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, - # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - # ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR - # TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF - # THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - # SUCH DAMAGE. - # -------------------------------------------------------------------------- - -5. Time Zone Database - - ICU uses the public domain data and code derived from Time Zone -Database for its time zone support. The ownership of the TZ database -is explained in BCP 175: Procedure for Maintaining the Time Zone -Database section 7. - - # 7. Database Ownership - # - # The TZ database itself is not an IETF Contribution or an IETF - # document. Rather it is a pre-existing and regularly updated work - # that is in the public domain, and is intended to remain in the - # public domain. Therefore, BCPs 78 [RFC5378] and 79 [RFC3979] do - # not apply to the TZ Database or contributions that individuals make - # to it. Should any claims be made and substantiated against the TZ - # Database, the organization that is providing the IANA - # Considerations defined in this RFC, under the memorandum of - # understanding with the IETF, currently ICANN, may act in accordance - # with all competent court orders. No ownership claims will be made - # by ICANN or the IETF Trust on the database or the code. Any person - # making a contribution to the database or code waives all rights to - # future claims in that contribution or in the TZ Database. - - -8. liblzma - -XZ Utils Licensing -================== - - Different licenses apply to different files in this package. Here - is a rough summary of which licenses apply to which parts of this - package (but check the individual files to be sure!): - - - liblzma is in the public domain. - - - xz, xzdec, and lzmadec command line tools are in the public - domain unless GNU getopt_long had to be compiled and linked - in from the lib directory. The getopt_long code is under - GNU LGPLv2.1+. - - - The scripts to grep, diff, and view compressed files have been - adapted from gzip. These scripts and their documentation are - under GNU GPLv2+. - - - All the documentation in the doc directory and most of the - XZ Utils specific documentation files in other directories - are in the public domain. - - - Translated messages are in the public domain. - - - The build system contains public domain files, and files that - are under GNU GPLv2+ or GNU GPLv3+. None of these files end up - in the binaries being built. - - - Test files and test code in the tests directory, and debugging - utilities in the debug directory are in the public domain. - - - The extra directory may contain public domain files, and files - that are under various free software licenses. - - You can do whatever you want with the files that have been put into - the public domain. If you find public domain legally problematic, - take the previous sentence as a license grant. If you still find - the lack of copyright legally problematic, you have too many - lawyers. - - As usual, this software is provided "as is", without any warranty. - - If you copy significant amounts of public domain code from XZ Utils - into your project, acknowledging this somewhere in your software is - polite (especially if it is proprietary, non-free software), but - naturally it is not legally required. Here is an example of a good - notice to put into "about box" or into documentation: - - This software includes code from XZ Utils . - - The following license texts are included in the following files: - - COPYING.LGPLv2.1: GNU Lesser General Public License version 2.1 - - COPYING.GPLv2: GNU General Public License version 2 - - COPYING.GPLv3: GNU General Public License version 3 - - Note that the toolchain (compiler, linker etc.) may add some code - pieces that are copyrighted. Thus, it is possible that e.g. liblzma - binary wouldn't actually be in the public domain in its entirety - even though it contains no copyrighted code from the XZ Utils source - package. - - If you have questions, don't hesitate to ask the author(s) for more - information. - - -BSD License - -All rights reserved. - -Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met: - -Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer. - -Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ""AS IS"" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - -9. libunwind - -Permission is hereby granted, free of charge, to any person obtaining -a copy of this software and associated documentation files (the -"Software"), to deal in the Software without restriction, including -without limitation the rights to use, copy, modify, merge, publish, -distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to -the following conditions: - -The above copyright notice and this permission notice shall be -included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, -EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND -NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE -LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION -OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION -WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -Provided for Informational Purposes Only - -MIT License - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the Software), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - - - ------------------------------------------------ END OF THIRD PARTY NOTICE ------------------------------------------ diff --git a/assets/AppxManifest.xml b/assets/AppxManifest.xml new file mode 100644 index 00000000000..50a8c7af45d --- /dev/null +++ b/assets/AppxManifest.xml @@ -0,0 +1,52 @@ + + + + + + + + $DISPLAYNAME$ + Microsoft Corporation + assets\StoreLogo.png + disabled + disabled + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/Avatar.svg b/assets/Avatar.svg new file mode 100644 index 00000000000..c6f73920773 --- /dev/null +++ b/assets/Avatar.svg @@ -0,0 +1,663 @@ + + + +image/svg+xml \ No newline at end of file diff --git a/assets/Chibi_Avatar.png b/assets/Chibi_Avatar.png new file mode 100644 index 00000000000..bbcf9569bb2 Binary files /dev/null and b/assets/Chibi_Avatar.png differ diff --git a/assets/Chibi_Avatar.svg b/assets/Chibi_Avatar.svg new file mode 100644 index 00000000000..ac678777be2 --- /dev/null +++ b/assets/Chibi_Avatar.svg @@ -0,0 +1,351 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/GroupPolicy/InstallPSCorePolicyDefinitions.ps1 b/assets/GroupPolicy/InstallPSCorePolicyDefinitions.ps1 new file mode 100644 index 00000000000..ee3c725b1bd --- /dev/null +++ b/assets/GroupPolicy/InstallPSCorePolicyDefinitions.ps1 @@ -0,0 +1,88 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +<# +.Synopsis + Group Policy tools use administrative template files (.admx, .adml) to populate policy settings in the user interface. + This allows administrators to manage registry-based policy settings. + This script installes PowerShell Core Administrative Templates for Windows. +.Notes + The PowerShellCoreExecutionPolicy.admx and PowerShellCoreExecutionPolicy.adml files are + expected to be at the location specified by the Path parameter with default value of the location of this script. +#> +[CmdletBinding()] +param +( + [ValidateNotNullOrEmpty()] + [string] $Path = $PSScriptRoot +) +Set-StrictMode -Version 3.0 +$ErrorActionPreference = 'Stop' + +function Test-Elevated +{ + [CmdletBinding()] + [OutputType([bool])] + Param() + + # if the current Powershell session was called with administrator privileges, + # the Administrator Group's well-known SID will show up in the Groups for the current identity. + # Note that the SID won't show up unless the process is elevated. + return (([Security.Principal.WindowsIdentity]::GetCurrent()).Groups -contains "S-1-5-32-544") +} +$IsWindowsOs = $PSHOME.EndsWith('\WindowsPowerShell\v1.0', [System.StringComparison]::OrdinalIgnoreCase) -or $IsWindows + +if (-not $IsWindowsOs) +{ + throw 'This script must be run on Windows.' +} + +if (-not (Test-Elevated)) +{ + throw 'This script must be run from an elevated process.' +} + +if ([System.Management.Automation.Platform]::IsNanoServer) +{ + throw 'Group policy definitions are not supported on Nano Server.' +} + +$admxName = 'PowerShellCoreExecutionPolicy.admx' +$admlName = 'PowerShellCoreExecutionPolicy.adml' +$admx = Get-Item -Path (Join-Path -Path $Path -ChildPath $admxName) +$adml = Get-Item -Path (Join-Path -Path $Path -ChildPath $admlName) +$admxTargetPath = Join-Path -Path $env:WINDIR -ChildPath "PolicyDefinitions" +$admlTargetPath = Join-Path -Path $admxTargetPath -ChildPath "en-US" + +$files = @($admx, $adml) +foreach ($file in $files) +{ + if (-not (Test-Path -Path $file)) + { + throw "Could not find $($file.Name) at $Path" + } +} + +Write-Verbose "Copying $admx to $admxTargetPath" +Copy-Item -Path $admx -Destination $admxTargetPath -Force +$admxTargetFullPath = Join-Path -Path $admxTargetPath -ChildPath $admxName +if (Test-Path -Path $admxTargetFullPath) +{ + Write-Verbose "$admxName was installed successfully" +} +else +{ + Write-Error "Could not install $admxName" +} + +Write-Verbose "Copying $adml to $admlTargetPath" +Copy-Item -Path $adml -Destination $admlTargetPath -Force +$admlTargetFullPath = Join-Path -Path $admlTargetPath -ChildPath $admlName +if (Test-Path -Path $admlTargetFullPath) +{ + Write-Verbose "$admlName was installed successfully" +} +else +{ + Write-Error "Could not install $admlName" +} diff --git a/assets/GroupPolicy/PowerShellCoreExecutionPolicy.adml b/assets/GroupPolicy/PowerShellCoreExecutionPolicy.adml new file mode 100644 index 00000000000..3068ae57a24 --- /dev/null +++ b/assets/GroupPolicy/PowerShellCoreExecutionPolicy.adml @@ -0,0 +1,125 @@ + + + PowerShell Core + This file contains the configuration options for PowerShell Core + + + Allow all scripts + Allow only signed scripts + Turn on Script Execution + This policy setting lets you configure the script execution policy, controlling which scripts are allowed to run. + +If you enable this policy setting, the scripts selected in the drop-down list are allowed to run. + +The "Allow only signed scripts" policy setting allows scripts to execute only if they are signed by a trusted publisher. + +The "Allow local scripts and remote signed scripts" policy setting allows any local scrips to run; scripts that originate from the internet must be signed by a trusted publisher. + +The "Allow all scripts" policy setting allows all scripts to run. + +If you disable this policy setting, no scripts are allowed to run. + +Note: This policy setting exists under both "Computer Configuration" and "User Configuration" in the Local Group Policy Editor. The "Computer Configuration" has precedence over "User Configuration." + +If you disable or do not configure this policy setting, it reverts to a per-machine preference setting; the default if that is not configured is "Allow local scripts and remote signed scripts." + PowerShell Core + Allow local scripts and remote signed scripts + At least Microsoft Windows 7 or Windows Server 2008 family + + Turn on Module Logging + + This policy setting allows you to turn on logging for PowerShell Core modules. + + If you enable this policy setting, pipeline execution events for members of the specified modules are recorded in the PowerShell Core log in Event Viewer. Enabling this policy setting for a module is equivalent to setting the LogPipelineExecutionDetails property of the module to True. + + If you disable this policy setting, logging of execution events is disabled for all PowerShell Core modules. Disabling this policy setting for a module is equivalent to setting the LogPipelineExecutionDetails property of the module to False. + + If this policy setting is not configured, the LogPipelineExecutionDetails property of a module determines whether the execution events of a module are logged. By default, the LogPipelineExecutionDetails property of all modules is set to False. + + To add modules to the policy setting list, click Show, and then type the module names in the list. The modules in the list must be installed on the computer. + + Note: This policy setting exists under both Computer Configuration and User Configuration in the Group Policy Editor. The Computer Configuration policy setting takes precedence over the User Configuration policy setting. + + + Turn on PowerShell Transcription + + This policy setting lets you capture the input and output of PowerShell Core commands into text-based transcripts. + + If you enable this policy setting, PowerShell Core will enable transcription logging for PowerShell Core and any other + applications that leverage the PowerShell Core engine. By default, PowerShell Core will record transcript output to each users' My Documents + directory, with a file name that includes 'PowerShell_transcript', along with the computer name and time started. Enabling this policy is equivalent + to calling the Start-Transcript cmdlet on each PowerShell Core session. + + If you disable this policy setting, transcription logging of PowerShell-based applications is disabled by default, although transcripting can still be enabled + through the Start-Transcript cmdlet. + + If you use the OutputDirectory setting to enable transcription logging to a shared location, be sure to limit access to that directory to prevent users + from viewing the transcripts of other users or computers. + + Note: This policy setting exists under both Computer Configuration and User Configuration in the Group Policy Editor. The Computer Configuration policy setting takes precedence over the User Configuration policy setting. + + + Turn on PowerShell Script Block Logging + + This policy setting enables logging of all PowerShell script input to the Microsoft-Windows-PowerShell/Operational event log. If you enable this policy setting, + PowerShell Core will log the processing of commands, script blocks, functions, and scripts - whether invoked interactively, or through automation. + + If you disable this policy setting, logging of PowerShell script input is disabled. + + If you enable the Script Block Invocation Logging, PowerShell additionally logs events when invocation of a command, script block, function, or script + starts or stops. Enabling Invocation Logging generates a high volume of event logs. + + Note: This policy setting exists under both Computer Configuration and User Configuration in the Group Policy Editor. The Computer Configuration policy setting takes precedence over the User Configuration policy setting. + + + Set the default source path for Update-Help + This policy setting allows you to set the default value of the SourcePath parameter on the Update-Help cmdlet. + +If you enable this policy setting, the Update-Help cmdlet will use the specified value as the default value for the SourcePath parameter. This default value can be overridden by specifying a different value with the SourcePath parameter on the Update-Help cmdlet. + +If this policy setting is disabled or not configured, this policy setting does not set a default value for the SourcePath parameter of the Update-Help cmdlet. + +Note: This policy setting exists under both Computer Configuration and User Configuration in the Group Policy Editor. The Computer Configuration policy setting takes precedence over the User Configuration policy setting. + + Console session configuration + Specifies a configuration endpoint in which PowerShell is run. This can be any endpoint registered on the local machine including the default PowerShell remoting endpoints or a custom endpoint having specific user role capabilities. + + + + + + Use Windows PowerShell Policy setting. + Execution Policy + + + Use Windows PowerShell Policy setting. + To turn on logging for one or more modules, click Show, and then type the module names in the list. Wildcards are supported. + Module Names + To turn on logging for the PowerShell Core core modules, type the following module names in the list: + Microsoft.PowerShell.* + Microsoft.WSMan.Management + + + Use Windows PowerShell Policy setting. + + Include invocation headers: + + + Use Windows PowerShell Policy setting. + Log script block invocation start / stop events: + + + Use Windows PowerShell Policy setting. + + + + + + + + + + + + + diff --git a/assets/GroupPolicy/PowerShellCoreExecutionPolicy.admx b/assets/GroupPolicy/PowerShellCoreExecutionPolicy.admx new file mode 100644 index 00000000000..0622a8d2695 --- /dev/null +++ b/assets/GroupPolicy/PowerShellCoreExecutionPolicy.admx @@ -0,0 +1,119 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + AllSigned + + + + + RemoteSigned + + + + + Unrestricted + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/MicrosoftUpdate/RegisterMicrosoftUpdate.ps1 b/assets/MicrosoftUpdate/RegisterMicrosoftUpdate.ps1 new file mode 100644 index 00000000000..da89768f74a --- /dev/null +++ b/assets/MicrosoftUpdate/RegisterMicrosoftUpdate.ps1 @@ -0,0 +1,70 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +param( + [ValidateSet('Hang', 'Fail')] + $TestHook +) + +$waitTimeoutSeconds = 300 +switch ($TestHook) { + 'Hang' { + $waitTimeoutSeconds = 10 + $jobScript = { Start-Sleep -Seconds 600 } + } + 'Fail' { + $jobScript = { throw "This job script should fail" } + } + default { + $jobScript = { + # This registers Microsoft Update via a predefined GUID with the Windows Update Agent. + # https://learn.microsoft.com/windows/win32/wua_sdk/opt-in-to-microsoft-update + + $serviceManager = (New-Object -ComObject Microsoft.Update.ServiceManager) + $isRegistered = $serviceManager.QueryServiceRegistration('7971f918-a847-4430-9279-4a52d1efe18d').Service.IsRegisteredWithAu + + if (!$isRegistered) { + Write-Verbose -Verbose "Opting into Microsoft Update as the Automatic Update Service" + # 7 is the combination of asfAllowPendingRegistration, asfAllowOnlineRegistration, asfRegisterServiceWithAU + # AU means Automatic Updates + $null = $serviceManager.AddService2('7971f918-a847-4430-9279-4a52d1efe18d', 7, '') + } + else { + Write-Verbose -Verbose "Microsoft Update is already registered for Automatic Updates" + } + + $isRegistered = $serviceManager.QueryServiceRegistration('7971f918-a847-4430-9279-4a52d1efe18d').Service.IsRegisteredWithAu + + # Return if it was successful, which is the opposite of Pending. + return $isRegistered + } + } +} + +Write-Verbose "Running job script: $jobScript" -Verbose +$job = Start-ThreadJob -ScriptBlock $jobScript + +Write-Verbose "Waiting on Job for $waitTimeoutSeconds seconds" -Verbose +$null = Wait-Job -Job $job -Timeout $waitTimeoutSeconds + +if ($job.State -ne 'Running') { + Write-Verbose "Job finished. State: $($job.State)" -Verbose + $result = Receive-Job -Job $job -Verbose + Write-Verbose "Result: $result" -Verbose + if ($result) { + Write-Verbose "Registration succeeded" -Verbose + exit 0 + } + else { + Write-Verbose "Registration failed" -Verbose + # at the time this was written, the MSI is ignoring the exit code + exit 1 + } +} +else { + Write-Verbose "Job timed out" -Verbose + Write-Verbose "Stopping Job. State: $($job.State)" -Verbose + Stop-Job -Job $job + # at the time this was written, the MSI is ignoring the exit code + exit 258 +} diff --git a/assets/Powershell-preview.icns b/assets/Powershell-preview.icns new file mode 100644 index 00000000000..a0ed0c4bc2c Binary files /dev/null and b/assets/Powershell-preview.icns differ diff --git a/assets/Powershell.icns b/assets/Powershell.icns index 703c785f601..b29582a266b 100644 Binary files a/assets/Powershell.icns and b/assets/Powershell.icns differ diff --git a/assets/Powershell_16.png b/assets/Powershell_16.png deleted file mode 100644 index acc6c493b19..00000000000 Binary files a/assets/Powershell_16.png and /dev/null differ diff --git a/assets/Powershell_20.png b/assets/Powershell_20.png deleted file mode 100644 index df170bf6e81..00000000000 Binary files a/assets/Powershell_20.png and /dev/null differ diff --git a/assets/Powershell_24.png b/assets/Powershell_24.png deleted file mode 100644 index 31895106973..00000000000 Binary files a/assets/Powershell_24.png and /dev/null differ diff --git a/assets/Powershell_256.ico b/assets/Powershell_256.ico deleted file mode 100644 index dfecd8de807..00000000000 Binary files a/assets/Powershell_256.ico and /dev/null differ diff --git a/assets/Powershell_256.png b/assets/Powershell_256.png index 8767330b82d..0f51af844e4 100644 Binary files a/assets/Powershell_256.png and b/assets/Powershell_256.png differ diff --git a/assets/Powershell_32.png b/assets/Powershell_32.png deleted file mode 100644 index e801ee7e8d0..00000000000 Binary files a/assets/Powershell_32.png and /dev/null differ diff --git a/assets/Powershell_40.png b/assets/Powershell_40.png deleted file mode 100644 index f34dd54a481..00000000000 Binary files a/assets/Powershell_40.png and /dev/null differ diff --git a/assets/Powershell_48.png b/assets/Powershell_48.png deleted file mode 100644 index 716c2aa5b60..00000000000 Binary files a/assets/Powershell_48.png and /dev/null differ diff --git a/assets/Powershell_av_colors.ico b/assets/Powershell_av_colors.ico new file mode 100644 index 00000000000..fec7b45fc10 Binary files /dev/null and b/assets/Powershell_av_colors.ico differ diff --git a/assets/Powershell_avatar.ico b/assets/Powershell_avatar.ico new file mode 100644 index 00000000000..447b0f2e31c Binary files /dev/null and b/assets/Powershell_avatar.ico differ diff --git a/assets/Powershell_black.ico b/assets/Powershell_black.ico new file mode 100644 index 00000000000..2ef67c76a1f Binary files /dev/null and b/assets/Powershell_black.ico differ diff --git a/assets/Powershell_black_64.png b/assets/Powershell_black_64.png new file mode 100644 index 00000000000..53bbbee10b7 Binary files /dev/null and b/assets/Powershell_black_64.png differ diff --git a/assets/Product.wxs b/assets/Product.wxs deleted file mode 100644 index 942676cef12..00000000000 --- a/assets/Product.wxs +++ /dev/null @@ -1,107 +0,0 @@ - - - - = 601" ?> - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - WIXUI_EXITDIALOGOPTIONALCHECKBOX = 1 and NOT Installed - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/assets/README.md b/assets/README.md new file mode 100644 index 00000000000..d557aa285f8 --- /dev/null +++ b/assets/README.md @@ -0,0 +1,22 @@ +# Use of Trademarked Logos + +The assets in this folder are trademarked by Microsoft and don't fall under the same [License](https://raw.githubusercontent.com/PowerShell/PowerShell/master/LICENSE.txt) as the source code. + +## Permitted Uses + +Parties interested in using these logos can review the [Use of Microsoft Copyrighted Material](https://www.microsoft.com/en-us/legal/intellectualproperty/permissions) page. +If the use falls into any of those categories, you can move forward with the use and will not need additional permission. + +## Third Party Permission Requests + +If questions remain after reviewing the permitted uses page, please submit a request to [Third Party Permissions](mailto:mscrqs@microsoft.com). +The people supporting this will direct the request depending upon the content. + +If a request needs to be submitted to the [Third Party Permissions](mailto:mscrqs@microsoft.com), the request should include the following: + + - A statement that the [Use of Microsoft Copyrighted Material](https://www.microsoft.com/en-us/legal/intellectualproperty/permissions) was reviewed and did not address the situation. + - A statement that Microsoft (and not a third party) is the owner of the logo at issue. + - Clear identification of the materials to be used (i.e., the picture of the book cover included by the author in the letter request should suffice). + - A description, including URLs of how they found or located the logo. + - A description of how the party intends to use or distribute the logo (i.e., that it is for the book cover of an instructional book on PowerShell). + - A description of how long the party needs to use the logo. diff --git a/assets/Square150x150Logo-Preview.png b/assets/Square150x150Logo-Preview.png new file mode 100644 index 00000000000..e206cf8064f Binary files /dev/null and b/assets/Square150x150Logo-Preview.png differ diff --git a/assets/Square150x150Logo.png b/assets/Square150x150Logo.png new file mode 100644 index 00000000000..bba1843f120 Binary files /dev/null and b/assets/Square150x150Logo.png differ diff --git a/assets/Square44x44Logo-Preview.png b/assets/Square44x44Logo-Preview.png new file mode 100644 index 00000000000..df150a063b9 Binary files /dev/null and b/assets/Square44x44Logo-Preview.png differ diff --git a/assets/Square44x44Logo.png b/assets/Square44x44Logo.png new file mode 100644 index 00000000000..16b1f6dd160 Binary files /dev/null and b/assets/Square44x44Logo.png differ diff --git a/assets/Square44x44Logo.targetsize-48-Preview.png b/assets/Square44x44Logo.targetsize-48-Preview.png new file mode 100644 index 00000000000..df150a063b9 Binary files /dev/null and b/assets/Square44x44Logo.targetsize-48-Preview.png differ diff --git a/assets/Square44x44Logo.targetsize-48.png b/assets/Square44x44Logo.targetsize-48.png new file mode 100644 index 00000000000..16b1f6dd160 Binary files /dev/null and b/assets/Square44x44Logo.targetsize-48.png differ diff --git a/assets/Square44x44Logo.targetsize-48_altform-unplated-Preview.png b/assets/Square44x44Logo.targetsize-48_altform-unplated-Preview.png new file mode 100644 index 00000000000..df150a063b9 Binary files /dev/null and b/assets/Square44x44Logo.targetsize-48_altform-unplated-Preview.png differ diff --git a/assets/Square44x44Logo.targetsize-48_altform-unplated.png b/assets/Square44x44Logo.targetsize-48_altform-unplated.png new file mode 100644 index 00000000000..16b1f6dd160 Binary files /dev/null and b/assets/Square44x44Logo.targetsize-48_altform-unplated.png differ diff --git a/assets/StoreLogo-Preview.png b/assets/StoreLogo-Preview.png new file mode 100644 index 00000000000..8c1fa58568f Binary files /dev/null and b/assets/StoreLogo-Preview.png differ diff --git a/assets/StoreLogo.png b/assets/StoreLogo.png new file mode 100644 index 00000000000..afd20a3d9d4 Binary files /dev/null and b/assets/StoreLogo.png differ diff --git a/assets/additionalAttributions.txt b/assets/additionalAttributions.txt new file mode 100644 index 00000000000..6676ca99cf5 --- /dev/null +++ b/assets/additionalAttributions.txt @@ -0,0 +1,192 @@ + +------------------------------------------------------------------- + +------------------------------------------------------------------- + +Additional - + +------------------------------------------------- +Microsoft.PowerShell.Archive +------------------------------------------------- + +Copyright (c) 2016 Microsoft Corporation. + +The MIT License (MIT) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + +------------------------------------------------- +Microsoft.Management.Infrastructure.Runtime.Unix +Microsoft.Management.Infrastructure +------------------------------------------------- + +Copyright (c) Microsoft Corporation + +All rights reserved. + +MIT License + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the ""Software""), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +-------------------------------------------------------- +• NuGet.Common +• NuGet.Configuration +• NuGet.DependencyResolver.Core +• NuGet.Frameworks +• NuGet.LibraryModel +• NuGet.Packaging +• NuGet.Packaging.Core +• NuGet.Packaging.Core.Types +• NuGet.ProjectModel +• NuGet.Protocol.Core.Types +• NuGet.Protocol.Core.v3 +• NuGet.Repositories +• NuGet.RuntimeModel +• NuGet.Versioning +---------------------------------------------------------- + +Copyright (c) .NET Foundation. All rights reserved. + +Licensed under the Apache License, Version 2.0 (the "License"); you may not use +these files except in compliance with the License. You may obtain a copy of the +License at + +https://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software distributed +under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR +CONDITIONS OF ANY KIND, either express or implied. See the License for the +specific language governing permissions and limitations under the License. + +------------------------------------------------- +PackageManagement +------------------------------------------------- + +Copyright (c) Microsoft Corporation +All rights reserved. + +MIT License + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the Software), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +------------------------------------------------- +PowerShellGet +------------------------------------------------- + +Copyright (c) Microsoft Corporation + +All rights reserved. + +The MIT License (MIT) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. + +--------------------------------------------- +File: PSReadLine +--------------------------------------------- + +https://github.com/PowerShell/PSReadLine + +Copyright (c) 2013, Jason Shirk + +All rights reserved. + +BSD License + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + +1. Redistributions of source code must retain the above copyright notice, this + list of conditions and the following disclaimer. +2. Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND +ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR +ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND +ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +------------------------------------------------- +ThreadJob +------------------------------------------------- + +Copyright (c) 2018 Paul Higinbotham + +MIT License + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/assets/av_colors_128.svg b/assets/av_colors_128.svg new file mode 100644 index 00000000000..ab296393719 --- /dev/null +++ b/assets/av_colors_128.svg @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + diff --git a/assets/avatar_128.svg b/assets/avatar_128.svg new file mode 100644 index 00000000000..2948614737c --- /dev/null +++ b/assets/avatar_128.svg @@ -0,0 +1,72 @@ + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/assets/default.help.txt b/assets/default.help.txt new file mode 100644 index 00000000000..a5b5cefc9cd --- /dev/null +++ b/assets/default.help.txt @@ -0,0 +1,108 @@ + +TOPIC + PowerShell Help System + +SHORT DESCRIPTION + Displays help about PowerShell cmdlets and concepts. + +LONG DESCRIPTION + PowerShell Help describes PowerShell cmdlets, functions, scripts, and + modules, and explains concepts, including the elements of the PowerShell + language. + + PowerShell does not include help files, but you can read the help topics + online, or use the Update-Help cmdlet to download help files to your + computer and then use the Get-Help cmdlet to display the help topics at + the command line. + + You can also use the Update-Help cmdlet to download updated help files + as they are released so that your local help content is never obsolete. + + Without help files, Get-Help displays auto-generated help for cmdlets, + functions, and scripts. + + + ONLINE HELP + You can find help for PowerShell online at + https://go.microsoft.com/fwlink/?LinkID=108518. + + To open online help for any cmdlet or function, type: + + Get-Help -Online + + UPDATE-HELP + To download and install help files on your computer: + + 1. Start PowerShell with the "Run as administrator" option. + 2. Type: + + Update-Help + + After the help files are installed, you can use the Get-Help cmdlet to + display the help topics. You can also use the Update-Help cmdlet to + download updated help files so that your local help files are always + up-to-date. + + For more information about the Update-Help cmdlet, type: + + Get-Help Update-Help -Online + + or go to: https://go.microsoft.com/fwlink/?LinkID=210614 + + + GET-HELP + The Get-Help cmdlet displays help at the command line from content in + help files on your computer. Without help files, Get-Help displays basic + help about cmdlets and functions. You can also use Get-Help to display + online help for cmdlets and functions. + + To get help for a cmdlet, type: + + Get-Help + + To get online help, type: + + Get-Help -Online + + The titles of conceptual topics begin with "About_". To get help for a + concept or language element, type: + + Get-Help About_ + + To search for a word or phrase in all help files, type: + + Get-Help + + For more information about the Get-Help cmdlet, type: + + Get-Help Get-Help -Online + + or go to: https://go.microsoft.com/fwlink/?LinkID=113316 + + + EXAMPLES: + Save-Help : Download help files from the internet and save + them on a file share. + + Update-Help : Downloads and installs help files from the + internet or a file share. + + Get-Help Get-Process : Displays help about the Get-Process cmdlet. + + Get-Help Get-Process -Online + : Opens online help for the Get-Process cmdlet. + + Help Get-Process : Displays help about Get-Process one page at a + time. + Get-Process -? : Displays help about the Get-Process cmdlet. + + Get-Help About_Modules : Displays help about PowerShell modules. + + Get-Help remoting : Searches the help topics for the word "remoting." + + SEE ALSO: + about_Updatable_Help + Get-Help + Save-Help + Update-Help + diff --git a/assets/license.rtf b/assets/license.rtf deleted file mode 100644 index 0e507920b40..00000000000 --- a/assets/license.rtf +++ /dev/null @@ -1,1248 +0,0 @@ -{\rtf1\adeflang1025\ansi\ansicpg1252\uc1\adeff31507\deff0\stshfdbch31506\stshfloch31506\stshfhich31506\stshfbi31507\deflang1033\deflangfe1033\themelang1033\themelangfe0\themelangcs0{\fonttbl{\f0\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\f2\fbidi \fmodern\fcharset0\fprq1{\*\panose 02070309020205020404}Courier New;} -{\f3\fbidi \froman\fcharset2\fprq2{\*\panose 05050102010706020507}Symbol;}{\f10\fbidi \fnil\fcharset2\fprq2{\*\panose 05000000000000000000}Wingdings;} -{\f11\fbidi \froman\fcharset128\fprq1{\*\panose 02020609040205080304}MS Mincho{\*\falt \'82\'6c\'82\'72 \'96\'be\'92\'a9};}{\f34\fbidi \froman\fcharset1\fprq2{\*\panose 02040503050406030204}Cambria Math;} -{\f39\fbidi \fswiss\fcharset0\fprq2{\*\panose 020f0502020204030204}Calibri;}{\f40\fbidi \fswiss\fcharset0\fprq2{\*\panose 00000000000000000000}Tahoma;}{\f41\fbidi \fswiss\fcharset0\fprq2{\*\panose 020b0603020202020204}Trebuchet MS;} -{\f42\fbidi \fswiss\fcharset0\fprq2{\*\panose 00000000000000000000}Segoe UI;}{\f43\fbidi \froman\fcharset128\fprq1{\*\panose 02020609040205080304}@MS Mincho;}{\flomajor\f31500\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;} -{\fdbmajor\f31501\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\fhimajor\f31502\fbidi \fswiss\fcharset0\fprq2{\*\panose 020f0302020204030204}Calibri Light;} -{\fbimajor\f31503\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\flominor\f31504\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;} -{\fdbminor\f31505\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\fhiminor\f31506\fbidi \fswiss\fcharset0\fprq2{\*\panose 020f0502020204030204}Calibri;} -{\fbiminor\f31507\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\f44\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\f45\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;} -{\f47\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\f48\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\f49\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\f50\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);} -{\f51\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\f52\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\f64\fbidi \fmodern\fcharset238\fprq1 Courier New CE;}{\f65\fbidi \fmodern\fcharset204\fprq1 Courier New Cyr;} -{\f67\fbidi \fmodern\fcharset161\fprq1 Courier New Greek;}{\f68\fbidi \fmodern\fcharset162\fprq1 Courier New Tur;}{\f69\fbidi \fmodern\fcharset177\fprq1 Courier New (Hebrew);}{\f70\fbidi \fmodern\fcharset178\fprq1 Courier New (Arabic);} -{\f71\fbidi \fmodern\fcharset186\fprq1 Courier New Baltic;}{\f72\fbidi \fmodern\fcharset163\fprq1 Courier New (Vietnamese);}{\f434\fbidi \fswiss\fcharset238\fprq2 Calibri CE;}{\f435\fbidi \fswiss\fcharset204\fprq2 Calibri Cyr;} -{\f437\fbidi \fswiss\fcharset161\fprq2 Calibri Greek;}{\f438\fbidi \fswiss\fcharset162\fprq2 Calibri Tur;}{\f439\fbidi \fswiss\fcharset177\fprq2 Calibri (Hebrew);}{\f440\fbidi \fswiss\fcharset178\fprq2 Calibri (Arabic);} -{\f441\fbidi \fswiss\fcharset186\fprq2 Calibri Baltic;}{\f442\fbidi \fswiss\fcharset163\fprq2 Calibri (Vietnamese);}{\f444\fbidi \fswiss\fcharset238\fprq2 Tahoma CE;}{\f445\fbidi \fswiss\fcharset204\fprq2 Tahoma Cyr;} -{\f447\fbidi \fswiss\fcharset161\fprq2 Tahoma Greek;}{\f448\fbidi \fswiss\fcharset162\fprq2 Tahoma Tur;}{\f449\fbidi \fswiss\fcharset177\fprq2 Tahoma (Hebrew);}{\f450\fbidi \fswiss\fcharset178\fprq2 Tahoma (Arabic);} -{\f451\fbidi \fswiss\fcharset186\fprq2 Tahoma Baltic;}{\f452\fbidi \fswiss\fcharset163\fprq2 Tahoma (Vietnamese);}{\f453\fbidi \fswiss\fcharset222\fprq2 Tahoma (Thai);}{\f454\fbidi \fswiss\fcharset238\fprq2 Trebuchet MS CE;} -{\f455\fbidi \fswiss\fcharset204\fprq2 Trebuchet MS Cyr;}{\f457\fbidi \fswiss\fcharset161\fprq2 Trebuchet MS Greek;}{\f458\fbidi \fswiss\fcharset162\fprq2 Trebuchet MS Tur;}{\f461\fbidi \fswiss\fcharset186\fprq2 Trebuchet MS Baltic;} -{\f464\fbidi \fswiss\fcharset238\fprq2 Segoe UI CE;}{\f465\fbidi \fswiss\fcharset204\fprq2 Segoe UI Cyr;}{\f467\fbidi \fswiss\fcharset161\fprq2 Segoe UI Greek;}{\f468\fbidi \fswiss\fcharset162\fprq2 Segoe UI Tur;} -{\f469\fbidi \fswiss\fcharset177\fprq2 Segoe UI (Hebrew);}{\f470\fbidi \fswiss\fcharset178\fprq2 Segoe UI (Arabic);}{\f471\fbidi \fswiss\fcharset186\fprq2 Segoe UI Baltic;}{\f472\fbidi \fswiss\fcharset163\fprq2 Segoe UI (Vietnamese);} -{\flomajor\f31508\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\flomajor\f31509\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\flomajor\f31511\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;} -{\flomajor\f31512\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\flomajor\f31513\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\flomajor\f31514\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);} -{\flomajor\f31515\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\flomajor\f31516\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\fdbmajor\f31518\fbidi \froman\fcharset238\fprq2 Times New Roman CE;} -{\fdbmajor\f31519\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\fdbmajor\f31521\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\fdbmajor\f31522\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;} -{\fdbmajor\f31523\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\fdbmajor\f31524\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\fdbmajor\f31525\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;} -{\fdbmajor\f31526\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\fhimajor\f31528\fbidi \fswiss\fcharset238\fprq2 Calibri Light CE;}{\fhimajor\f31529\fbidi \fswiss\fcharset204\fprq2 Calibri Light Cyr;} -{\fhimajor\f31531\fbidi \fswiss\fcharset161\fprq2 Calibri Light Greek;}{\fhimajor\f31532\fbidi \fswiss\fcharset162\fprq2 Calibri Light Tur;}{\fhimajor\f31533\fbidi \fswiss\fcharset177\fprq2 Calibri Light (Hebrew);} -{\fhimajor\f31534\fbidi \fswiss\fcharset178\fprq2 Calibri Light (Arabic);}{\fhimajor\f31535\fbidi \fswiss\fcharset186\fprq2 Calibri Light Baltic;}{\fhimajor\f31536\fbidi \fswiss\fcharset163\fprq2 Calibri Light (Vietnamese);} -{\fbimajor\f31538\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\fbimajor\f31539\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\fbimajor\f31541\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;} -{\fbimajor\f31542\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\fbimajor\f31543\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\fbimajor\f31544\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);} -{\fbimajor\f31545\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\fbimajor\f31546\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\flominor\f31548\fbidi \froman\fcharset238\fprq2 Times New Roman CE;} -{\flominor\f31549\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;}{\flominor\f31551\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\flominor\f31552\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;} -{\flominor\f31553\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);}{\flominor\f31554\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\flominor\f31555\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;} -{\flominor\f31556\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}{\fdbminor\f31558\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\fdbminor\f31559\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;} -{\fdbminor\f31561\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\fdbminor\f31562\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\fdbminor\f31563\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);} -{\fdbminor\f31564\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\fdbminor\f31565\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\fdbminor\f31566\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);} -{\fhiminor\f31568\fbidi \fswiss\fcharset238\fprq2 Calibri CE;}{\fhiminor\f31569\fbidi \fswiss\fcharset204\fprq2 Calibri Cyr;}{\fhiminor\f31571\fbidi \fswiss\fcharset161\fprq2 Calibri Greek;}{\fhiminor\f31572\fbidi \fswiss\fcharset162\fprq2 Calibri Tur;} -{\fhiminor\f31573\fbidi \fswiss\fcharset177\fprq2 Calibri (Hebrew);}{\fhiminor\f31574\fbidi \fswiss\fcharset178\fprq2 Calibri (Arabic);}{\fhiminor\f31575\fbidi \fswiss\fcharset186\fprq2 Calibri Baltic;} -{\fhiminor\f31576\fbidi \fswiss\fcharset163\fprq2 Calibri (Vietnamese);}{\fbiminor\f31578\fbidi \froman\fcharset238\fprq2 Times New Roman CE;}{\fbiminor\f31579\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr;} -{\fbiminor\f31581\fbidi \froman\fcharset161\fprq2 Times New Roman Greek;}{\fbiminor\f31582\fbidi \froman\fcharset162\fprq2 Times New Roman Tur;}{\fbiminor\f31583\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew);} -{\fbiminor\f31584\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic);}{\fbiminor\f31585\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic;}{\fbiminor\f31586\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese);}} -{\colortbl;\red0\green0\blue0;\red0\green0\blue255;\red0\green255\blue255;\red0\green255\blue0;\red255\green0\blue255;\red255\green0\blue0;\red255\green255\blue0;\red255\green255\blue255;\red0\green0\blue128;\red0\green128\blue128;\red0\green128\blue0; -\red128\green0\blue128;\red128\green0\blue0;\red128\green128\blue0;\red128\green128\blue128;\red192\green192\blue192;\red51\green51\blue51;}{\*\defchp \f31506\fs22 }{\*\defpap \ql \li0\ri0\sa160\sl259\slmult1 -\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 }\noqfpromote {\stylesheet{\ql \li0\ri0\sa160\sl259\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af31507\afs22\alang1025 -\ltrch\fcs0 \f31506\fs22\lang1033\langfe1033\cgrid\langnp1033\langfenp1033 \snext0 \sqformat \spriority0 Normal;}{\s1\ql \li0\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\outlinelevel0\adjustright\rin0\lin0\itap0 \rtlch\fcs1 -\ab\af40\afs19\alang1025 \ltrch\fcs0 \fs19\lang1033\langfe1033\loch\f40\hich\af40\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext1 \slink22 \sqformat \styrsid7813854 heading 1;}{ -\s2\ql \li0\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\outlinelevel1\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \ab\af40\afs19\alang1025 \ltrch\fcs0 \fs19\lang1033\langfe1033\loch\f40\hich\af40\dbch\af11\cgrid\langnp1033\langfenp1033 -\sbasedon0 \snext2 \slink23 \sqformat \styrsid7813854 heading 2;}{\s3\ql \fi-357\li1077\ri0\sb120\sa120\widctlpar\tx1077\jclisttab\tx1440\wrapdefault\aspalpha\aspnum\faauto\ls12\ilvl2\outlinelevel2\adjustright\rin0\lin1077\itap0 \rtlch\fcs1 -\af40\afs19\alang1025 \ltrch\fcs0 \b\fs19\lang1033\langfe1033\loch\f40\hich\af40\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext3 \slink24 \sqformat \styrsid7813854 heading 3;}{\s4\ql \fi-358\li1435\ri0\sb120\sa120\widctlpar -\jclisttab\tx1437\wrapdefault\aspalpha\aspnum\faauto\ls12\ilvl3\outlinelevel3\adjustright\rin0\lin1435\itap0 \rtlch\fcs1 \af40\afs19\alang1025 \ltrch\fcs0 \b\fs19\lang1033\langfe1033\loch\f40\hich\af40\dbch\af11\cgrid\langnp1033\langfenp1033 -\sbasedon0 \snext4 \slink25 \sqformat \styrsid7813854 heading 4;}{\s5\ql \fi-357\li1792\ri0\sb120\sa120\widctlpar\tx1792\jclisttab\tx2155\wrapdefault\aspalpha\aspnum\faauto\ls12\ilvl4\outlinelevel4\adjustright\rin0\lin1792\itap0 \rtlch\fcs1 -\af40\afs19\alang1025 \ltrch\fcs0 \b\fs19\lang1033\langfe1033\loch\f40\hich\af40\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext5 \slink26 \sqformat \styrsid7813854 heading 5;}{\s6\ql \fi-357\li2149\ri0\sb120\sa120\widctlpar -\jclisttab\tx2152\wrapdefault\aspalpha\aspnum\faauto\ls12\ilvl5\outlinelevel5\adjustright\rin0\lin2149\itap0 \rtlch\fcs1 \af40\afs19\alang1025 \ltrch\fcs0 \b\fs19\lang1033\langfe1033\loch\f40\hich\af40\dbch\af11\cgrid\langnp1033\langfenp1033 -\sbasedon0 \snext6 \slink27 \sqformat \styrsid7813854 heading 6;}{\s7\ql \fi-357\li2506\ri0\sb120\sa120\widctlpar\jclisttab\tx2509\wrapdefault\aspalpha\aspnum\faauto\ls12\ilvl6\outlinelevel6\adjustright\rin0\lin2506\itap0 \rtlch\fcs1 -\af40\afs19\alang1025 \ltrch\fcs0 \b\fs19\lang1033\langfe1033\loch\f40\hich\af40\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext7 \slink28 \sqformat \styrsid7813854 heading 7;}{\s8\ql \fi-357\li2863\ri0\sb120\sa120\widctlpar -\jclisttab\tx2866\wrapdefault\aspalpha\aspnum\faauto\ls12\ilvl7\outlinelevel7\adjustright\rin0\lin2863\itap0 \rtlch\fcs1 \af40\afs19\alang1025 \ltrch\fcs0 \b\fs19\lang1033\langfe1033\loch\f40\hich\af40\dbch\af11\cgrid\langnp1033\langfenp1033 -\sbasedon0 \snext8 \slink29 \sqformat \styrsid7813854 heading 8;}{\s9\ql \fi-358\li3221\ri0\sb120\sa120\widctlpar\jclisttab\tx3223\wrapdefault\aspalpha\aspnum\faauto\ls12\ilvl8\outlinelevel8\adjustright\rin0\lin3221\itap0 \rtlch\fcs1 -\af40\afs19\alang1025 \ltrch\fcs0 \b\fs19\lang1033\langfe1033\loch\f40\hich\af40\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext9 \slink30 \sqformat \styrsid7813854 heading 9;}{\*\cs10 \additive \ssemihidden \sunhideused \spriority1 -Default Paragraph Font;}{\*\ts11\tsrowd\trftsWidthB3\trpaddl108\trpaddr108\trpaddfl3\trpaddft3\trpaddfb3\trpaddfr3\trcbpat1\trcfpat1\tblind0\tblindtype3\tsvertalt\tsbrdrt\tsbrdrl\tsbrdrb\tsbrdrr\tsbrdrdgl\tsbrdrdgr\tsbrdrh\tsbrdrv -\ql \li0\ri0\sa160\sl259\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af31507\afs22\alang1025 \ltrch\fcs0 \f31506\fs22\lang1033\langfe1033\cgrid\langnp1033\langfenp1033 \snext11 \ssemihidden \sunhideused -Normal Table;}{\s15\ql \li0\ri0\sb100\sa100\sbauto1\saauto1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af0\afs24\alang1025 \ltrch\fcs0 \fs24\lang1033\langfe1033\cgrid\langnp1033\langfenp1033 -\sbasedon0 \snext15 \ssemihidden \sunhideused \styrsid3804850 Normal (Web);}{\s16\ql \li720\ri0\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin720\itap0 \rtlch\fcs1 \af39\afs22\alang1025 \ltrch\fcs0 -\f39\fs22\lang1033\langfe1033\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext16 \sqformat \spriority34 \styrsid6173475 List Paragraph;}{\s17\ql \li0\ri0\widctlpar -\tx916\tx1832\tx2748\tx3664\tx4580\tx5496\tx6412\tx7328\tx8244\tx9160\tx10076\tx10992\tx11908\tx12824\tx13740\tx14656\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af2\afs20\alang1025 \ltrch\fcs0 -\f2\fs20\lang1033\langfe1033\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext17 \slink18 \ssemihidden \sunhideused \styrsid6573559 HTML Preformatted;}{\*\cs18 \additive \rtlch\fcs1 \af2\afs20 \ltrch\fcs0 \f2\fs20 -\sbasedon10 \slink17 \slocked \ssemihidden \styrsid6573559 HTML Preformatted Char;}{\*\cs19 \additive \rtlch\fcs1 \af0 \ltrch\fcs0 \ul\cf1 \sbasedon10 \sunhideused \styrsid7092439 Hyperlink;}{\*\cs20 \additive \rtlch\fcs1 \af0 \ltrch\fcs0 -\sbasedon10 \spriority0 \styrsid7092439 spelle;}{\*\cs21 \additive \rtlch\fcs1 \af0 \ltrch\fcs0 \sbasedon10 \spriority0 \styrsid7092439 grame;}{\*\cs22 \additive \rtlch\fcs1 \ab\af40\afs19 \ltrch\fcs0 \fs19\loch\f40\hich\af40\dbch\af11 -\sbasedon10 \slink1 \slocked \styrsid7813854 Heading 1 Char;}{\*\cs23 \additive \rtlch\fcs1 \ab\af40\afs19 \ltrch\fcs0 \fs19\loch\f40\hich\af40\dbch\af11 \sbasedon10 \slink2 \slocked \styrsid7813854 Heading 2 Char;}{\*\cs24 \additive \rtlch\fcs1 -\af40\afs19 \ltrch\fcs0 \b\fs19\loch\f40\hich\af40\dbch\af11 \sbasedon10 \slink3 \slocked \styrsid7813854 Heading 3 Char;}{\*\cs25 \additive \rtlch\fcs1 \af40\afs19 \ltrch\fcs0 \b\fs19\loch\f40\hich\af40\dbch\af11 -\sbasedon10 \slink4 \slocked \styrsid7813854 Heading 4 Char;}{\*\cs26 \additive \rtlch\fcs1 \af40\afs19 \ltrch\fcs0 \b\fs19\loch\f40\hich\af40\dbch\af11 \sbasedon10 \slink5 \slocked \styrsid7813854 Heading 5 Char;}{\*\cs27 \additive \rtlch\fcs1 -\af40\afs19 \ltrch\fcs0 \b\fs19\loch\f40\hich\af40\dbch\af11 \sbasedon10 \slink6 \slocked \styrsid7813854 Heading 6 Char;}{\*\cs28 \additive \rtlch\fcs1 \af40\afs19 \ltrch\fcs0 \b\fs19\loch\f40\hich\af40\dbch\af11 -\sbasedon10 \slink7 \slocked \styrsid7813854 Heading 7 Char;}{\*\cs29 \additive \rtlch\fcs1 \af40\afs19 \ltrch\fcs0 \b\fs19\loch\f40\hich\af40\dbch\af11 \sbasedon10 \slink8 \slocked \styrsid7813854 Heading 8 Char;}{\*\cs30 \additive \rtlch\fcs1 -\af40\afs19 \ltrch\fcs0 \b\fs19\loch\f40\hich\af40\dbch\af11 \sbasedon10 \slink9 \slocked \styrsid7813854 Heading 9 Char;}{\s31\ql \li0\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 -\af40\afs19\alang1025 \ltrch\fcs0 \b\fs19\lang1033\langfe1033\loch\f40\hich\af40\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext31 \styrsid7813854 Body 1;}{ -\s32\ql \li0\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \ab\af40\afs28\alang1025 \ltrch\fcs0 \fs28\lang1033\langfe1033\loch\f40\hich\af40\dbch\af11\cgrid\langnp1033\langfenp1033 -\sbasedon0 \snext0 \styrsid7813854 Heading EULA;}{\s33\ql \li0\ri0\sb120\sa120\widctlpar\brdrb\brdrs\brdrw10\brsp20 \wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \ab\af40\afs28\alang1025 \ltrch\fcs0 -\fs28\lang1033\langfe1033\loch\f40\hich\af40\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext0 \styrsid7813854 Heading Software Title;}{\s34\ql \li0\ri0\sb120\sa120\widctlpar\brdrt\brdrs\brdrw10\brsp20 -\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \ab\af40\afs19\alang1025 \ltrch\fcs0 \fs19\lang1033\langfe1033\loch\f40\hich\af40\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext34 \styrsid7813854 -Preamble Border Above;}}{\*\listtable{\list\listtemplateid412752146\listhybrid{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698689\'01\u-3913 ?;}{\levelnumbers;} -\f3\fbias0\hres0\chhres0 \fi-360\li720\lin720 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0\hres0\chhres0 -\fi-360\li1440\lin1440 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0\hres0\chhres0 -\fi-360\li2160\lin2160 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698689\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0\hres0\chhres0 -\fi-360\li2880\lin2880 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0\hres0\chhres0 \fi-360\li3600\lin3600 } -{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0\hres0\chhres0 \fi-360\li4320\lin4320 }{\listlevel -\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698689\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0\hres0\chhres0 \fi-360\li5040\lin5040 }{\listlevel\levelnfc23 -\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0\hres0\chhres0 \fi-360\li5760\lin5760 }{\listlevel\levelnfc23\levelnfcn23\leveljc0 -\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0\hres0\chhres0 \fi-360\li6480\lin6480 }{\listname ;}\listid256596791}{\list\listtemplateid-234466468 -\listhybrid{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0\hres0\chhres0 \fi-360\li720\lin720 }{\listlevel\levelnfc23 -\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0\hres0\chhres0 \fi-360\li1440\lin1440 }{\listlevel\levelnfc23\levelnfcn23\leveljc0 -\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0\hres0\chhres0 \fi-360\li2160\lin2160 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0 -\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698689\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0\hres0\chhres0 \fi-360\li2880\lin2880 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0 -\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0\hres0\chhres0 \fi-360\li3600\lin3600 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative -\levelspace0\levelindent0{\leveltext\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0\hres0\chhres0 \fi-360\li4320\lin4320 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0 -\levelindent0{\leveltext\leveltemplateid67698689\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0\hres0\chhres0 \fi-360\li5040\lin5040 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0 -{\leveltext\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0\hres0\chhres0 \fi-360\li5760\lin5760 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext -\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0\hres0\chhres0 \fi-360\li6480\lin6480 }{\listname ;}\listid268852893}{\list\listtemplateid2071779370\listhybrid{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0 -\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0\hres0\chhres0 \fi-360\li720\lin720 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative -\levelspace0\levelindent0{\leveltext\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0\hres0\chhres0 \fi-360\li1440\lin1440 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0 -{\leveltext\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0\hres0\chhres0 \fi-360\li2160\lin2160 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext -\leveltemplateid67698689\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0\hres0\chhres0 \fi-360\li2880\lin2880 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext -\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0\hres0\chhres0 \fi-360\li3600\lin3600 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698693 -\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0\hres0\chhres0 \fi-360\li4320\lin4320 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698689 -\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0\hres0\chhres0 \fi-360\li5040\lin5040 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698691\'01o;}{\levelnumbers -;}\f2\fbias0\hres0\chhres0 \fi-360\li5760\lin5760 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;} -\f10\fbias0\hres0\chhres0 \fi-360\li6480\lin6480 }{\listname ;}\listid472724034}{\list\listtemplateid837583652\listhybrid{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext -\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0\hres0\chhres0 \fi-360\li720\lin720 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext -\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0\hres0\chhres0 \fi-360\li1440\lin1440 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698693 -\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0\hres0\chhres0 \fi-360\li2160\lin2160 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698689 -\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0\hres0\chhres0 \fi-360\li2880\lin2880 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698691\'01o;}{\levelnumbers -;}\f2\fbias0\hres0\chhres0 \fi-360\li3600\lin3600 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;} -\f10\fbias0\hres0\chhres0 \fi-360\li4320\lin4320 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698689\'01\u-3913 ?;}{\levelnumbers;} -\f3\fbias0\hres0\chhres0 \fi-360\li5040\lin5040 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0\hres0\chhres0 -\fi-360\li5760\lin5760 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0\hres0\chhres0 -\fi-360\li6480\lin6480 }{\listname ;}\listid678236712}{\list\listtemplateid468190476\listhybrid{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698693 -\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0\hres0\chhres0 \fi-360\li720\lin720 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698691\'01o;}{\levelnumbers;} -\f2\fbias0\hres0\chhres0 \fi-360\li1440\lin1440 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;} -\f10\fbias0\hres0\chhres0 \fi-360\li2160\lin2160 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698689\'01\u-3913 ?;}{\levelnumbers;} -\f3\fbias0\hres0\chhres0 \fi-360\li2880\lin2880 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0\hres0\chhres0 -\fi-360\li3600\lin3600 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0\hres0\chhres0 -\fi-360\li4320\lin4320 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698689\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0\hres0\chhres0 -\fi-360\li5040\lin5040 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0\hres0\chhres0 \fi-360\li5760\lin5760 } -{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0\hres0\chhres0 \fi-360\li6480\lin6480 }{\listname -;}\listid696808916}{\list\listtemplateid256027766\listhybrid{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;} -\f10\fbias0\hres0\chhres0 \fi-360\li720\lin720 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0\hres0\chhres0 -\fi-360\li1440\lin1440 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0\hres0\chhres0 -\fi-360\li2160\lin2160 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698689\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0\hres0\chhres0 -\fi-360\li2880\lin2880 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0\hres0\chhres0 \fi-360\li3600\lin3600 } -{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0\hres0\chhres0 \fi-360\li4320\lin4320 }{\listlevel -\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698689\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0\hres0\chhres0 \fi-360\li5040\lin5040 }{\listlevel\levelnfc23 -\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0\hres0\chhres0 \fi-360\li5760\lin5760 }{\listlevel\levelnfc23\levelnfcn23\leveljc0 -\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0\hres0\chhres0 \fi-360\li6480\lin6480 }{\listname ;}\listid833446968}{\list\listtemplateid812297174 -{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'00);}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \b\hres0\chhres0 \fi-360\li717\lin717 }{\listlevel\levelnfc4\levelnfcn4\leveljc0 -\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'01);}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-360\li1077\lin1077 }{\listlevel\levelnfc2\levelnfcn2\leveljc0\leveljcn0\levelfollow0\levelstartat1 -\levelspace0\levelindent0{\leveltext\'02\'02);}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-360\li1437\lin1437 }{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext -\'03(\'03);}{\levelnumbers\'02;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-360\li1797\lin1797 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'03(\'04);}{\levelnumbers\'02;} -\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-360\li2157\lin2157 }{\listlevel\levelnfc2\levelnfcn2\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'03(\'05);}{\levelnumbers\'02;}\rtlch\fcs1 \af0 \ltrch\fcs0 -\hres0\chhres0 \fi-360\li2517\lin2517 }{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'06.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-360\li2877\lin2877 } -{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'07.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-360\li3237\lin3237 }{\listlevel\levelnfc2\levelnfcn2\leveljc0 -\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'08.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-360\li3597\lin3597 }{\listname ;}\listid888110291}{\list\listtemplateid812297174{\listlevel\levelnfc4 -\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'00);}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \b\hres0\chhres0 \fi-360\li717\lin717 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0 -\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'01);}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-360\li1077\lin1077 }{\listlevel\levelnfc2\levelnfcn2\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0 -\levelindent0{\leveltext\'02\'02);}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-360\li1437\lin1437 }{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext -\'03(\'03);}{\levelnumbers\'02;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-360\li1797\lin1797 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'03(\'04);}{\levelnumbers\'02;} -\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-360\li2157\lin2157 }{\listlevel\levelnfc2\levelnfcn2\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'03(\'05);}{\levelnumbers\'02;}\rtlch\fcs1 \af0 \ltrch\fcs0 -\hres0\chhres0 \fi-360\li2517\lin2517 }{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'06.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-360\li2877\lin2877 } -{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'07.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-360\li3237\lin3237 }{\listlevel\levelnfc2\levelnfcn2\leveljc0 -\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'08.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-360\li3597\lin3597 }{\listname ;}\listid1282881056}{\list\listtemplateid827650700\listhybrid{\listlevel -\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0\hres0\chhres0 \fi-360\li720\lin720 }{\listlevel\levelnfc23\levelnfcn23\leveljc0 -\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0\hres0\chhres0 \fi-360\li1440\lin1440 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0 -\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0\hres0\chhres0 \fi-360\li2160\lin2160 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1 -\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698689\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0\hres0\chhres0 \fi-360\li2880\lin2880 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative -\levelspace0\levelindent0{\leveltext\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0\hres0\chhres0 \fi-360\li3600\lin3600 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0 -{\leveltext\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0\hres0\chhres0 \fi-360\li4320\lin4320 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext -\leveltemplateid67698689\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0\hres0\chhres0 \fi-360\li5040\lin5040 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext -\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0\hres0\chhres0 \fi-360\li5760\lin5760 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698693 -\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0\hres0\chhres0 \fi-360\li6480\lin6480 }{\listname ;}\listid1470391773}{\list\listtemplateid-1331279738\listhybrid{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0 -\levelindent0{\leveltext\leveltemplateid67698689\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0\hres0\chhres0 \fi-360\li720\lin720 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext -\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0\hres0\chhres0 \fi-360\li1440\lin1440 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698693 -\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0\hres0\chhres0 \fi-360\li2160\lin2160 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698689\'01\u-3913 ?;}{\levelnumbers;} -\f3\fbias0\hres0\chhres0 \fi-360\li2880\lin2880 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0\hres0\chhres0 -\fi-360\li3600\lin3600 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0\hres0\chhres0 \fi-360\li4320\lin4320 } -{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698689\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0\hres0\chhres0 \fi-360\li5040\lin5040 }{\listlevel\levelnfc23\levelnfcn23 -\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0\hres0\chhres0 \fi-360\li5760\lin5760 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0 -\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0\hres0\chhres0 \fi-360\li6480\lin6480 }{\listname ;}\listid1543639483}{\list\listtemplateid812297174{\listlevel\levelnfc4\levelnfcn4 -\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'00);}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \b\hres0\chhres0 \fi-360\li717\lin717 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0 -\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'01);}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-360\li1077\lin1077 }{\listlevel\levelnfc2\levelnfcn2\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0 -{\leveltext\'02\'02);}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-360\li1437\lin1437 }{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'03(\'03);}{\levelnumbers -\'02;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-360\li1797\lin1797 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'03(\'04);}{\levelnumbers\'02;}\rtlch\fcs1 \af0 \ltrch\fcs0 -\hres0\chhres0 \fi-360\li2157\lin2157 }{\listlevel\levelnfc2\levelnfcn2\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'03(\'05);}{\levelnumbers\'02;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-360\li2517\lin2517 } -{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'06.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-360\li2877\lin2877 }{\listlevel\levelnfc4\levelnfcn4\leveljc0 -\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'07.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-360\li3237\lin3237 }{\listlevel\levelnfc2\levelnfcn2\leveljc0\leveljcn0\levelfollow0\levelstartat1 -\levelspace0\levelindent0{\leveltext\'02\'08.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-360\li3597\lin3597 }{\listname ;}\listid1670060985}{\list\listtemplateid-1676387632{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0 -\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'00.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab\ai0\af40\afs20 \ltrch\fcs0 \b\i0\f40\fs20\fbias0\hres0\chhres0 \fi-357\li357\jclisttab\tx360\lin357 }{\listlevel\levelnfc23\levelnfcn23\leveljc0 -\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'01\u-3913 ?;}{\levelnumbers;}\b\i0\f3\fs20\fbias0\hres0\chhres0 \fi-363\li720\jclisttab\tx720\lin720 }{\listlevel\levelnfc2\levelnfcn2\leveljc0\leveljcn0\levelfollow0\levelstartat1 -\levelspace0\levelindent0{\leveltext\'02\'02.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab\ai0\af40\afs20 \ltrch\fcs0 \b\i0\f40\fs20\fbias0\hres0\chhres0 \s3\fi-357\li1077\jclisttab\tx1440\lin1077 }{\listlevel\levelnfc3\levelnfcn3\leveljc0\leveljcn0\levelfollow0 -\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'03.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab0\ai0\af41\afs20 \ltrch\fcs0 \b0\i0\strike0\f41\fs20\ulnone\fbias0\hres0\chhres0 \s4\fi-358\li1435\jclisttab\tx1437\lin1435 }{\listlevel\levelnfc1\levelnfcn1 -\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'04.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab0\ai0\af41\afs20 \ltrch\fcs0 \b0\i0\strike0\f41\fs20\ulnone\fbias0\hres0\chhres0 \s5\fi-357\li1792\jclisttab\tx2155\lin1792 } -{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'05.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab0\ai0\af41\afs20 \ltrch\fcs0 \b0\i0\f41\fs20\fbias0\hres0\chhres0 \s6\fi-357\li2149 -\jclisttab\tx2152\lin2149 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'06.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab0\ai0\af41\afs20 \ltrch\fcs0 \b0\i0\f41\fs20\fbias0\hres0\chhres0 -\s7\fi-357\li2506\jclisttab\tx2509\lin2506 }{\listlevel\levelnfc255\levelnfcn255\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02i.;}{\levelnumbers;}\rtlch\fcs1 \ab0\ai0\af41\afs20 \ltrch\fcs0 -\b0\i0\f41\fs20\fbias0\hres0\chhres0 \s8\fi-357\li2863\jclisttab\tx2866\lin2863 }{\listlevel\levelnfc255\levelnfcn255\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02A.;}{\levelnumbers;}\rtlch\fcs1 \ab0\ai0\af41\afs20 -\ltrch\fcs0 \b0\i0\f41\fs20\fbias0\hres0\chhres0 \s9\fi-358\li3221\jclisttab\tx3223\lin3221 }{\listname ;}\listid1743720866}{\list\listtemplateid-646571904\listhybrid{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1 -\levelspace0\levelindent0{\leveltext\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0\hres0\chhres0 \fi-360\li720\lin720 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0 -\levelindent0{\leveltext\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0\hres0\chhres0 \fi-360\li1440\lin1440 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext -\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0\hres0\chhres0 \fi-360\li2160\lin2160 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext -\leveltemplateid67698689\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0\hres0\chhres0 \fi-360\li2880\lin2880 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext -\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0\hres0\chhres0 \fi-360\li3600\lin3600 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698693 -\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0\hres0\chhres0 \fi-360\li4320\lin4320 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698689 -\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0\hres0\chhres0 \fi-360\li5040\lin5040 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698691\'01o;}{\levelnumbers -;}\f2\fbias0\hres0\chhres0 \fi-360\li5760\lin5760 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;} -\f10\fbias0\hres0\chhres0 \fi-360\li6480\lin6480 }{\listname ;}\listid2003921709}{\list\listtemplateid1067461628\listhybrid{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext -\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0\hres0\chhres0 \fi-360\li720\lin720 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext -\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0\hres0\chhres0 \fi-360\li1440\lin1440 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698693 -\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0\hres0\chhres0 \fi-360\li2160\lin2160 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698689 -\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0\hres0\chhres0 \fi-360\li2880\lin2880 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698691\'01o;}{\levelnumbers -;}\f2\fbias0\hres0\chhres0 \fi-360\li3600\lin3600 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;} -\f10\fbias0\hres0\chhres0 \fi-360\li4320\lin4320 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698689\'01\u-3913 ?;}{\levelnumbers;} -\f3\fbias0\hres0\chhres0 \fi-360\li5040\lin5040 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0\hres0\chhres0 -\fi-360\li5760\lin5760 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0\hres0\chhres0 -\fi-360\li6480\lin6480 }{\listname ;}\listid2022782249}{\list\listtemplateid1736062262\listhybrid{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698693 -\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0\hres0\chhres0 \fi-360\li720\lin720 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698691\'01o;}{\levelnumbers;} -\f2\fbias0\hres0\chhres0 \fi-360\li1440\lin1440 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;} -\f10\fbias0\hres0\chhres0 \fi-360\li2160\lin2160 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698689\'01\u-3913 ?;}{\levelnumbers;} -\f3\fbias0\hres0\chhres0 \fi-360\li2880\lin2880 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0\hres0\chhres0 -\fi-360\li3600\lin3600 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0\hres0\chhres0 -\fi-360\li4320\lin4320 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698689\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0\hres0\chhres0 -\fi-360\li5040\lin5040 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0\hres0\chhres0 \fi-360\li5760\lin5760 } -{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0\hres0\chhres0 \fi-360\li6480\lin6480 }{\listname -;}\listid2055036124}{\list\listtemplateid812297174{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'00);}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \b\hres0\chhres0 -\fi-360\li717\lin717 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'01);}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-360\li1077\lin1077 }{\listlevel -\levelnfc2\levelnfcn2\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'02);}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-360\li1437\lin1437 }{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0 -\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'03(\'03);}{\levelnumbers\'02;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-360\li1797\lin1797 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0 -\levelindent0{\leveltext\'03(\'04);}{\levelnumbers\'02;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-360\li2157\lin2157 }{\listlevel\levelnfc2\levelnfcn2\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext -\'03(\'05);}{\levelnumbers\'02;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-360\li2517\lin2517 }{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'06.;}{\levelnumbers\'01;} -\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-360\li2877\lin2877 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'07.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 -\hres0\chhres0 \fi-360\li3237\lin3237 }{\listlevel\levelnfc2\levelnfcn2\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'08.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \hres0\chhres0 \fi-360\li3597\lin3597 } -{\listname ;}\listid2106000387}}{\*\listoverridetable{\listoverride\listid256596791\listoverridecount0\ls1}{\listoverride\listid1543639483\listoverridecount0\ls2}{\listoverride\listid268852893\listoverridecount0\ls3}{\listoverride\listid678236712 -\listoverridecount0\ls4}{\listoverride\listid2022782249\listoverridecount0\ls5}{\listoverride\listid472724034\listoverridecount0\ls6}{\listoverride\listid1470391773\listoverridecount0\ls7}{\listoverride\listid2003921709\listoverridecount0\ls8} -{\listoverride\listid2055036124\listoverridecount0\ls9}{\listoverride\listid696808916\listoverridecount0\ls10}{\listoverride\listid833446968\listoverridecount0\ls11}{\listoverride\listid1743720866\listoverridecount0\ls12}{\listoverride\listid1670060985 -\listoverridecount0\ls13}{\listoverride\listid1282881056\listoverridecount0\ls14}{\listoverride\listid888110291\listoverridecount0\ls15}{\listoverride\listid2106000387\listoverridecount0\ls16}}{\*\pgptbl {\pgp\ipgp0\itap0\li0\ri0\sb0\sa0}{\pgp\ipgp0\itap0 -\li0\ri0\sb0\sa0}{\pgp\ipgp0\itap0\li0\ri0\sb0\sa0}{\pgp\ipgp0\itap0\li0\ri0\sb0\sa0}{\pgp\ipgp0\itap0\li0\ri0\sb0\sa0}{\pgp\ipgp10\itap0\li0\ri0\sb0\sa0}{\pgp\ipgp0\itap0\li0\ri0\sb0\sa0}{\pgp\ipgp0\itap0\li300\ri300\sb300\sa300}{\pgp\ipgp0\itap0\li0\ri0 -\sb0\sa0}{\pgp\ipgp0\itap0\li0\ri0\sb0\sa0}{\pgp\ipgp0\itap0\li0\ri0\sb0\sa0}{\pgp\ipgp0\itap0\li0\ri0\sb0\sa0}}{\*\rsidtbl \rsid93983\rsid217518\rsid1984289\rsid3804850\rsid5845909\rsid6173475\rsid6573559\rsid7092439\rsid7813854\rsid8005660\rsid8394862 -\rsid10169937\rsid10363382\rsid10624128\rsid13960513\rsid14557619\rsid16084641}{\mmathPr\mmathFont34\mbrkBin0\mbrkBinSub0\msmallFrac0\mdispDef1\mlMargin0\mrMargin0\mdefJc1\mwrapIndent1440\mintLim0\mnaryLim1}{\info{\author Duane Okamoto (CELA)} -{\operator Raghu Shantha}{\creatim\yr2016\mo8\dy12\hr14\min30}{\revtim\yr2016\mo8\dy12\hr14\min30}{\version2}{\edmins1}{\nofpages15}{\nofwords5899}{\nofchars33626}{\*\company Microsoft IT}{\nofcharsws39447}{\vern19}}{\*\xmlnstbl {\xmlns1 http://schemas.mi -crosoft.com/office/word/2003/wordml}}\paperw12240\paperh15840\margl1440\margr1440\margt1440\margb1440\gutter0\ltrsect -\widowctrl\ftnbj\aenddoc\trackmoves0\trackformatting1\donotembedsysfont1\relyonvml0\donotembedlingdata0\grfdocevents0\validatexml1\showplaceholdtext0\ignoremixedcontent0\saveinvalidxml0\showxmlerrors1\noxlattoyen -\expshrtn\noultrlspc\dntblnsbdb\nospaceforul\formshade\horzdoc\dgmargin\dghspace180\dgvspace180\dghorigin1440\dgvorigin1440\dghshow1\dgvshow1 -\jexpand\viewkind1\viewscale100\pgbrdrhead\pgbrdrfoot\splytwnine\ftnlytwnine\htmautsp\nolnhtadjtbl\useltbaln\alntblind\lytcalctblwd\lyttblrtgr\lnbrkrule\nobrkwrptbl\snaptogridincell\allowfieldendsel\wrppunct -\asianbrkrule\rsidroot3804850\newtblstyruls\nogrowautofit\usenormstyforlist\noindnmbrts\felnbrelev\nocxsptable\indrlsweleven\noafcnsttbl\afelev\utinl\hwelev\spltpgpar\notcvasp\notbrkcnstfrctbl\notvatxbx\krnprsnet\cachedcolbal \nouicompat \fet0 -{\*\wgrffmtfilter 2450}\nofeaturethrottle1\ilfomacatclnup0\ltrpar \sectd \ltrsect\linex0\endnhere\sectlinegrid360\sectdefaultcl\sftnbj {\*\pnseclvl1\pnucrm\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl2\pnucltr\pnstart1\pnindent720\pnhang -{\pntxta .}}{\*\pnseclvl3\pndec\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl4\pnlcltr\pnstart1\pnindent720\pnhang {\pntxta )}}{\*\pnseclvl5\pndec\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl6\pnlcltr\pnstart1\pnindent720\pnhang -{\pntxtb (}{\pntxta )}}{\*\pnseclvl7\pnlcrm\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl8\pnlcltr\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl9\pnlcrm\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}} -\pard\plain \ltrpar\s15\ql \li0\ri0\sb100\sa100\sbauto1\saauto1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid3804850 \rtlch\fcs1 \af0\afs24\alang1025 \ltrch\fcs0 \fs24\lang1033\langfe1033\cgrid\langnp1033\langfenp1033 { -\rtlch\fcs1 \af2\afs20 \ltrch\fcs0 \b\f2\fs20\cf17\lang9\langfe1033\langnp9\insrsid93983\charrsid93983 PowerShell 6.0}{\rtlch\fcs1 \af2\afs20 \ltrch\fcs0 \b\f2\fs20\cf17\lang9\langfe1033\langnp9\insrsid3804850\charrsid93983 -\par }{\rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f2\fs18\cf17\lang9\langfe1033\langnp9\insrsid3804850\charrsid93983 Copyright (c) Microsoft Corporation}{\rtlch\fcs1 \af2\afs20 \ltrch\fcs0 \f2\fs20\cf17\lang9\langfe1033\langnp9\insrsid3804850\charrsid93983 -\par }{\rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f2\fs18\cf17\lang9\langfe1033\langnp9\insrsid3804850\charrsid93983 All rights reserved.\~}{\rtlch\fcs1 \af2\afs20 \ltrch\fcs0 \f2\fs20\cf17\lang9\langfe1033\langnp9\insrsid3804850\charrsid93983 -\par }{\rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f2\fs18\cf17\lang9\langfe1033\langnp9\insrsid3804850\charrsid93983 MIT License}{\rtlch\fcs1 \af2\afs20 \ltrch\fcs0 \f2\fs20\cf17\lang9\langfe1033\langnp9\insrsid3804850\charrsid93983 -\par }{\rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f2\fs18\cf17\lang9\langfe1033\langnp9\insrsid3804850\charrsid93983 -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software - without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following cond -itions:}{\rtlch\fcs1 \af2\afs20 \ltrch\fcs0 \f2\fs20\cf17\lang9\langfe1033\langnp9\insrsid3804850\charrsid93983 -\par }{\rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f2\fs18\cf17\lang9\langfe1033\langnp9\insrsid3804850\charrsid93983 The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.}{\rtlch\fcs1 -\af2\afs20 \ltrch\fcs0 \f2\fs20\cf17\lang9\langfe1033\langnp9\insrsid3804850\charrsid93983 -\par }{\rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f2\fs18\cf17\lang9\langfe1033\langnp9\insrsid3804850\charrsid93983 THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRA -NTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT -OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.}{\rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f2\fs18\cf17\lang9\langfe1033\langnp9\insrsid3804850 -\par }{\rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f2\fs18\cf17\lang9\langfe1033\langnp9\insrsid6173475 -\par }{\rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f2\fs20\ul\cf17\lang9\langfe1033\langnp9\insrsid6173475\charrsid10363382 IMPORTANT NOTICE}{\rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f2\fs20\cf17\lang9\langfe1033\langnp9\insrsid6173475\charrsid10363382 -: THE SOFTWARE ALSO CONTAINS THIRD PARTY AND OTHER }{\rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f2\fs20\cf17\lang9\langfe1033\langnp9\insrsid10624128 PROPRIETARY }{\rtlch\fcs1 \af2\afs18 \ltrch\fcs0 -\f2\fs20\cf17\lang9\langfe1033\langnp9\insrsid6173475\charrsid10363382 SOFTWARE THAT ARE GOVERNED BY SEPARATE LICENSE TERMS. BY ACCEPTING THE LICENSE TERMS ABOVE, -YOU ALSO ACCEPT THE LICENSE TERMS GOVERNING THE THIRD PARTY AND OTHER SOFTWARE, WHICH ARE SET FORTH BELOW: -\par }{\rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f2\fs18\cf17\lang9\langfe1033\langnp9\insrsid6173475 The following components }{\rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f2\fs18\cf17\lang9\langfe1033\langnp9\insrsid16084641 listed }{\rtlch\fcs1 \af2\afs18 \ltrch\fcs0 -\f2\fs18\cf17\lang9\langfe1033\langnp9\insrsid6173475 are governed by }{\rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f2\fs18\cf17\lang9\langfe1033\langnp9\insrsid16084641 the license terms that follow }{\rtlch\fcs1 \af2\afs18 \ltrch\fcs0 -\f2\fs18\cf17\lang9\langfe1033\langnp9\insrsid6173475 the component}{\rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f2\fs18\cf17\lang9\langfe1033\langnp9\insrsid16084641 (s) name}{\rtlch\fcs1 \af2\afs18 \ltrch\fcs0 -\f2\fs18\cf17\lang9\langfe1033\langnp9\insrsid6173475 : -\par }\pard \ltrpar\s15\ql \li0\ri0\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid6173475 {\rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f2\fs18\cf17\lang9\langfe1033\langnp9\insrsid6173475 --------------------------------------}{ -\rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f2\fs18\cf17\lang9\langfe1033\langnp9\insrsid10363382 ----------------}{\rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f2\fs18\cf17\lang9\langfe1033\langnp9\insrsid6173475 -\par }\pard \ltrpar\s15\ql \li0\ri0\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid14557619 {\rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f2\fs18\cf17\lang9\langfe1033\langnp9\insrsid6173475 Libmi.so -\par }\pard \ltrpar\s15\ql \li0\ri0\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid6173475 {\rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f2\fs18\cf17\lang9\langfe1033\langnp9\insrsid6173475 --------------------------------------}{ -\rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f2\fs18\cf17\lang9\langfe1033\langnp9\insrsid10363382 ----------------}{\rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f2\fs18\cf17\lang9\langfe1033\langnp9\insrsid6173475 -\par }\pard \ltrpar\s15\ql \li0\ri0\sb100\sa100\sbauto1\saauto1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid3804850 {\rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f2\fs18\cf17\lang9\langfe1033\langnp9\insrsid6173475 -Copyright (c) Microsoft Corporation -\par }\pard \ltrpar\s15\ql \li0\ri0\sb100\sa100\sbauto1\saauto1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid6173475 {\rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f2\fs18\cf17\lang9\langfe1033\langnp9\insrsid6173475\charrsid93983 -All rights reserved.\~}{\rtlch\fcs1 \af2\afs20 \ltrch\fcs0 \f2\fs20\cf17\lang9\langfe1033\langnp9\insrsid6173475\charrsid93983 -\par }{\rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f2\fs18\cf17\lang9\langfe1033\langnp9\insrsid6173475\charrsid93983 MIT License}{\rtlch\fcs1 \af2\afs20 \ltrch\fcs0 \f2\fs20\cf17\lang9\langfe1033\langnp9\insrsid6173475\charrsid93983 -\par }{\rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f2\fs18\cf17\lang9\langfe1033\langnp9\insrsid6173475\charrsid93983 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated docu -mentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Soft -ware is furnished to do so, subject to the following conditions:}{\rtlch\fcs1 \af2\afs20 \ltrch\fcs0 \f2\fs20\cf17\lang9\langfe1033\langnp9\insrsid6173475\charrsid93983 -\par }{\rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f2\fs18\cf17\lang9\langfe1033\langnp9\insrsid6173475\charrsid93983 The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.}{\rtlch\fcs1 -\af2\afs20 \ltrch\fcs0 \f2\fs20\cf17\lang9\langfe1033\langnp9\insrsid6173475\charrsid93983 -\par }{\rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f2\fs18\cf17\lang9\langfe1033\langnp9\insrsid6173475\charrsid93983 THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, E -XPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN -ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.}{\rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f2\fs18\cf17\lang9\langfe1033\langnp9\insrsid6173475 -\par }\pard \ltrpar\s15\ql \li0\ri0\sb100\sa100\sbauto1\saauto1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid14557619 {\rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f2\fs18\cf17\lang9\langfe1033\langnp9\insrsid14557619 -The following components are governed by the MIT license, a copy of which appears below the list of components: -\par }\pard \ltrpar\s15\ql \li0\ri0\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid14557619 {\rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f2\fs18\cf17\lang9\langfe1033\langnp9\insrsid14557619 ------------------------------------------------------- -\par Newtonsoft.Json -\par ------------------------------------------------------ -\par }\pard \ltrpar\s15\ql \li0\ri0\sb100\sa100\sbauto1\saauto1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid14557619 {\rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f2\fs18\cf17\lang9\langfe1033\langnp9\insrsid14557619 -Copyright (c) 2007 James Newton-King -\par }{\rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f2\fs18\cf17\lang9\langfe1033\langnp9\insrsid14557619\charrsid93983 All rights reserved.\~}{\rtlch\fcs1 \af2\afs20 \ltrch\fcs0 \f2\fs20\cf17\lang9\langfe1033\langnp9\insrsid14557619\charrsid93983 -\par }{\rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f2\fs18\cf17\lang9\langfe1033\langnp9\insrsid14557619\charrsid93983 MIT License}{\rtlch\fcs1 \af2\afs20 \ltrch\fcs0 \f2\fs20\cf17\lang9\langfe1033\langnp9\insrsid14557619\charrsid93983 -\par }{\rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f2\fs18\cf17\lang9\langfe1033\langnp9\insrsid14557619\charrsid93983 Permission is hereby granted, free of charge, to any person - obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies o -f the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:}{\rtlch\fcs1 \af2\afs20 \ltrch\fcs0 \f2\fs20\cf17\lang9\langfe1033\langnp9\insrsid14557619\charrsid93983 -\par }{\rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f2\fs18\cf17\lang9\langfe1033\langnp9\insrsid14557619\charrsid93983 The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.}{\rtlch\fcs1 -\af2\afs20 \ltrch\fcs0 \f2\fs20\cf17\lang9\langfe1033\langnp9\insrsid14557619\charrsid93983 -\par }{\rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f2\fs18\cf17\lang9\langfe1033\langnp9\insrsid14557619\charrsid93983 THE SOFTWAR -E IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR - ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.}{\rtlch\fcs1 \af2\afs18 \ltrch\fcs0 -\f2\fs18\cf17\lang9\langfe1033\langnp9\insrsid14557619 -\par }\pard \ltrpar\s15\ql \li0\ri0\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid6573559 {\rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f2\fs18\cf17\lang9\langfe1033\langnp9\insrsid6573559 ---------------------------------------------------------- -\par Libuv v.1.9.0 -\par --------------------------------------------------------- -\par }\pard\plain \ltrpar\ql \li0\ri0\widctlpar\tx916\tx1832\tx2748\tx3664\tx4580\tx5496\tx6412\tx7328\tx8244\tx9160\tx10076\tx10992\tx11908\tx12824\tx13740\tx14656\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid6573559 \rtlch\fcs1 -\af31507\afs22\alang1025 \ltrch\fcs0 \f31506\fs22\lang1033\langfe1033\cgrid\langnp1033\langfenp1033 {\rtlch\fcs1 \af2\afs20 \ltrch\fcs0 \f2\fs18\insrsid10169937 -\par }{\rtlch\fcs1 \af2\afs20 \ltrch\fcs0 \f2\fs18\insrsid6573559\charrsid6573559 https://raw.githubusercontent.com/aspnet/libuv-package/dev/content/License.txt -\par }{\rtlch\fcs1 \af2\afs20 \ltrch\fcs0 \f2\fs20\insrsid6573559 -\par }{\rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f2\fs18\insrsid6573559\charrsid6573559 This software is licensed to you by Microsoft Corporation under the original terms of the copyright holder provided below: -\par -\par ========================================= -\par -\par libuv is part of the Node project: http://nodejs.org/ -\par libuv may be distributed alone under Node's license: -\par -\par ==== -\par -\par Copyright Joyent, Inc. and other Node contributors. All rights reserved. -\par -\par Permission is hereby granted, free of charge, to any person obtaining a copy -\par of this software and associated documentation files (the "Software"), to -\par deal in the Software without restriction, including without limitation the -\par rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -\par sell copies of the Software, and to permit persons to whom the Software is -\par furnished to do so, subject to the following conditions: -\par -\par The above copyright notice and this permission notice shall be included in -\par all copies or substantial portions of the Software. -\par -\par THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -\par IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -\par FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -\par AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -\par LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -\par FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -\par IN THE SOFTWARE. -\par -\par ==== -\par -\par This license applies to all parts of libuv that are not externally -\par maintained libraries. -\par -\par The externally maintained libraries used by libuv are: -\par -\par - tree.h (from FreeBSD), copyright Niels Provos. Two clause BSD license. -\par -\par - inet_pton and inet_ntop implementations, contained in src/inet.c, are -\par }\pard \ltrpar\ql \li450\ri0\widctlpar\tx450\tx1530\tx2700\tx3664\tx4580\tx5496\tx6412\tx7328\tx8244\tx9160\tx10076\tx10992\tx11908\tx12824\tx13740\tx14656\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin450\itap0\pararsid6573559 {\rtlch\fcs1 -\af2\afs18 \ltrch\fcs0 \f2\fs18\insrsid6573559\charrsid6573559 copyright the Internet Systems Consortium, Inc., and licensed under the ISC license. -\par }\pard \ltrpar\ql \li0\ri0\widctlpar\tx916\tx1832\tx2748\tx3664\tx4580\tx5496\tx6412\tx7328\tx8244\tx9160\tx10076\tx10992\tx11908\tx12824\tx13740\tx14656\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid6573559 {\rtlch\fcs1 -\af2\afs18 \ltrch\fcs0 \f2\fs18\insrsid6573559\charrsid6573559 -\par - stdint-msvc2008.h (from msinttypes), copyright Alexander Chemeris. Three -\par clause BSD license. -\par -\par - pthread-fixes.h, pthread-fixes.c, copyright Google Inc. and Sony Mobile -\par Communications AB. Three clause BSD license. -\par -\par }\pard \ltrpar\ql \fi-450\li450\ri0\widctlpar\tx916\tx1832\tx2748\tx3664\tx4580\tx5496\tx6412\tx7328\tx8244\tx9160\tx10076\tx10992\tx11908\tx12824\tx13740\tx14656\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin450\itap0\pararsid6573559 { -\rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f2\fs18\insrsid6573559\charrsid6573559 - android-ifaddrs.h, android-ifaddrs.c, copyright Berkeley Software Design Inc, Kenneth MacKay and Emergya (Cloud4all, FP7/2007-2013, grant agreement -\par }\pard \ltrpar\ql \li0\ri0\widctlpar\tx916\tx1832\tx2748\tx3664\tx4580\tx5496\tx6412\tx7328\tx8244\tx9160\tx10076\tx10992\tx11908\tx12824\tx13740\tx14656\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid6573559 {\rtlch\fcs1 -\af2\afs18 \ltrch\fcs0 \f2\fs18\insrsid6573559\charrsid6573559 n\'b0 289016). Three clause BSD license. -\par -\par ========================================= -\par }\pard\plain \ltrpar\s15\ql \li0\ri0\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid6573559 \rtlch\fcs1 \af0\afs24\alang1025 \ltrch\fcs0 \fs24\lang1033\langfe1033\cgrid\langnp1033\langfenp1033 {\rtlch\fcs1 \af2\afs18 -\ltrch\fcs0 \f2\fs18\cf17\lang9\langfe1033\langnp9\insrsid6573559 -\par }{\rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f2\fs18\cf17\lang9\langfe1033\langnp9\insrsid10169937 -\par }\pard \ltrpar\s15\ql \li0\ri0\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid10169937 {\rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f2\fs18\cf17\lang9\langfe1033\langnp9\insrsid6573559 ---------------------------------------------------}{\rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f2\fs18\cf17\lang9\langfe1033\langnp9\insrsid10169937 --}{\rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f2\fs18\cf17\lang9\langfe1033\langnp9\insrsid6573559 -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}}\pard\plain \ltrpar -\s16\ql \fi-360\li720\ri0\widctlpar\wrapdefault\aspalpha\aspnum\faauto\ls11\adjustright\rin0\lin720\itap0\pararsid10169937 \rtlch\fcs1 \af39\afs22\alang1025 \ltrch\fcs0 \f39\fs22\lang1033\langfe1033\cgrid\langnp1033\langfenp1033 {\rtlch\fcs1 \af2\afs18 -\ltrch\fcs0 \f2\fs18\insrsid7092439\charrsid10169937 dotnet-test-xunit 2.2.0-preview2-build1029 -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs22 \ltrch\fcs0 \f10\fs18\insrsid16084641\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}}{\rtlch\fcs1 \af2 \ltrch\fcs0 \f2\fs18\insrsid16084641\charrsid10169937 xunit -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs22 \ltrch\fcs0 \f10\fs18\insrsid16084641\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}xunit.abstractions -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs22 \ltrch\fcs0 \f10\fs18\insrsid16084641\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}xunit.assert -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs22 \ltrch\fcs0 \f10\fs18\insrsid16084641\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}xunit.core -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs22 \ltrch\fcs0 \f10\fs18\insrsid16084641\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}xunit.extensibility.core -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs22 \ltrch\fcs0 \f10\fs18\insrsid16084641\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}xunit.extensibility.execution -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs22 \ltrch\fcs0 \f10\fs18\insrsid16084641\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}xunit.runner.reporters -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs22 \ltrch\fcs0 \f10\fs18\insrsid16084641\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}xunit.runner.utility -\par }\pard\plain \ltrpar\ql \li0\ri0\sl259\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid7092439 \rtlch\fcs1 \af31507\afs22\alang1025 \ltrch\fcs0 \f31506\fs22\lang1033\langfe1033\cgrid\langnp1033\langfenp1033 { -\rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f2\fs18\insrsid7092439 ---------------------------------------------------}{\rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f2\fs18\insrsid10169937 -}{\rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f2\fs18\insrsid7092439 -\par }\pard \ltrpar\ql \li0\ri0\sl259\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid10169937 {\rtlch\fcs1 \af2\afs20 \ltrch\fcs0 \f2\fs18\insrsid10169937 -\par }{\rtlch\fcs1 \af2\afs20 \ltrch\fcs0 \f2\fs18\insrsid16084641 https://www.nuget.org/packages -\par }{\rtlch\fcs1 \af2\afs20 \ltrch\fcs0 \f2\fs18\insrsid10169937 -\par }{\rtlch\fcs1 \af2\afs20 \ltrch\fcs0 \f2\fs18\insrsid6573559\charrsid6573559 Copyright 2015 Outercurve Foundation -\par }\pard \ltrpar\ql \li0\ri0\widctlpar\tx916\tx1832\tx2748\tx3664\tx4580\tx5496\tx6412\tx7328\tx8244\tx9160\tx10076\tx10992\tx11908\tx12824\tx13740\tx14656\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid10169937 {\rtlch\fcs1 -\af2\afs20 \ltrch\fcs0 \f2\fs18\insrsid6573559\charrsid6573559 -\par }\pard \ltrpar\ql \li0\ri0\widctlpar\tx916\tx1832\tx2748\tx3664\tx4580\tx5496\tx6412\tx7328\tx8244\tx9160\tx10076\tx10992\tx11908\tx12824\tx13740\tx14656\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid6573559 {\rtlch\fcs1 -\af2\afs20 \ltrch\fcs0 \f2\fs18\insrsid6573559\charrsid6573559 Licensed under the Apache License, Version 2.0 (the "License"); -\par you may not use this file except in compliance with the License. -\par You may obtain a copy of the License at -\par -\par http://www.apache.org/licenses/LICENSE-2.0 -\par -\par Unless required by applicable law or agreed to in writing, software -\par distributed under the License is distributed on an "AS IS" BASIS, -\par WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -\par See the License for the specific language governing permissions and -\par limitations under the License.}{\rtlch\fcs1 \af2\afs20 \ltrch\fcs0 \f2\fs18\insrsid6573559 -\par }{\rtlch\fcs1 \af2\afs20 \ltrch\fcs0 \f2\fs18\insrsid7092439 -\par }{\rtlch\fcs1 \af2\afs20 \ltrch\fcs0 \f2\fs18\insrsid7092439\charrsid6573559 -\par }\pard\plain \ltrpar\s15\ql \li0\ri0\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid7092439 \rtlch\fcs1 \af0\afs24\alang1025 \ltrch\fcs0 \fs24\lang1033\langfe1033\cgrid\langnp1033\langfenp1033 {\rtlch\fcs1 \af2\afs20 -\ltrch\fcs0 \f2\fs18\cf17\lang9\langfe1033\langnp9\insrsid7092439 --------------------------------------------------}{\rtlch\fcs1 \af2\afs20 \ltrch\fcs0 \f2\fs18\cf17\lang9\langfe1033\langnp9\insrsid6573559 -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}}\pard\plain \ltrpar -\s16\ql \fi-360\li720\ri0\widctlpar\wrapdefault\aspalpha\aspnum\faauto\ls10\adjustright\rin0\lin720\itap0\pararsid10169937 \rtlch\fcs1 \af39\afs22\alang1025 \ltrch\fcs0 \f39\fs22\lang1033\langfe1033\cgrid\langnp1033\langfenp1033 {\rtlch\fcs1 \af2\afs18 -\ltrch\fcs0 \f2\fs18\insrsid7092439\charrsid10169937 Microsoft.CodeAnalysis.Analyzers -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}Microsoft.CodeAnalysis.Common -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}Microsoft.CodeAnalysis.CSharp -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}Microsoft.CodeAnalysis.VisualBasic -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}Microsoft.CSharp -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}Microsoft.DiaSymReader -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}Microsoft.DiaSymReader.Native -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}Microsoft.DotNet.Cli.Utils -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}Microsoft.DotNet.InternalAbstractions -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}Microsoft.DotNet.ProjectModel -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}Microsoft.Extensions.DependencyModel -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}Microsoft.Extensions.Testing.Abstractions -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}Microsoft.NETCore -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}Microsoft.NETCore.App -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}Microsoft.NETCore.DotNetHost -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}Microsoft.NETCore.DotNetHostPolicy -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}Microsoft.NETCore.DotNetHostResolver -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}Microsoft.NETCore.Jit -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}Microsoft.NETCore.Platforms -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}Microsoft.NETCore.Portable.Compatibility -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}Microsoft.NETCore.Runtime -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}Microsoft.NETCore.Runtime.CoreCLR -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}Microsoft.NETCore.Runtime.Native -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}Microsoft.NETCore.Targets -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}Microsoft.NETCore.Windows.ApiSets -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}Microsoft.VisualBasic -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}Microsoft.Win32.Primitives -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}}\pard \ltrpar -\s16\ql \fi-360\li720\ri0\widctlpar\wrapdefault\aspalpha\aspnum\faauto\ls9\adjustright\rin0\lin720\itap0\pararsid10169937 {\rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f2\fs18\insrsid7092439\charrsid10169937 Microsoft.Win32.Registry -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}Microsoft.Win32.Registry.AccessControl -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}NETStandard.Library -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}runtime.any.System.Collections -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}runtime.any.System.Diagnostics.Tools -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}runtime.any.System.Diagnostics.Tracing -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}runtime.any.System.Globalization -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}runtime.any.System.Globalization.Calendars -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}runtime.any.System.IO -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}runtime.any.System.Reflection -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}runtime.any.System.Reflection.Extensions -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}runtime.any.System.Reflection.Primitives -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}runtime.any.System.Resources.ResourceManager -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}runtime.any.System.Runtime -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}runtime.any.System.Runtime.Handles -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}runtime.any.System.Runtime.InteropServices -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}runtime.any.System.Text.Encoding -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}runtime.any.System.Text.Encoding.Extensions -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}runtime.any.System.Threading.Tasks -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}runtime.any.System.Threading.Timer -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}runtime.debian.8-x64.Microsoft.NETCore.DotNetHost -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}runtime.debian.8-x64.Microsoft.NETCore.DotNetHostPolicy -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}runtime.debian.8-x64.Microsoft.NETCore.DotNetHostResolver -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}runtime.debian.8-x64.Microsoft.NETCore.Jit -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}runtime.debian.8-x64.Microsoft.NETCore.Runtime.CoreCLR -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}runtime.debian.8-x64.runtime.native.System -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}runtime.debian.8-x64.runtime.native.System.IO.Compression -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}runtime.debian.8-x64.runtime.native.System.Net.Http -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}runtime.debian.8-x64.runtime.native.System.Net.Security -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}runtime.debian.8-x64.runtime.native.System.Security.Cryptography -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}runtime.native.System -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}runtime.native.System.Data.SqlClient.sni -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}runtime.native.System.IO.Compression -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}runtime.native.System.Net.Http -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}runtime.native.System.Net.Security -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}runtime.native.System.Security.Cryptography -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}runtime.osx.10.10-x64.Microsoft.NETCore.DotNetHost -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}runtime.osx.10.10-x64.Microsoft.NETCore.DotNetHostPolicy -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}runtime.osx.10.10-x64.Microsoft.NETCore.DotNetHostResolver -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}runtime.osx.10.10-x64.Microsoft.NETCore.Jit -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}runtime.osx.10.10-x64.Microsoft.NETCore.Runtime.CoreCLR -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}runtime.osx.10.10-x64.runtime.native.System -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}runtime.osx.10.10-x64.runtime.native.System.IO.Compression -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}runtime.osx.10.10-x64.runtime.native.System.Net.Http -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}runtime.osx.10.10-x64.runtime.native.System.Net.Security -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}runtime.rhel.7-x64.Microsoft.NETCore.DotNetHost -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}runtime.rhel.7-x64.Microsoft.NETCore.DotNetHostPolicy -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}runtime.rhel.7-x64.Microsoft.NETCore.DotNetHostResolver -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}runtime.rhel.7-x64.Microsoft.NETCore.Jit -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}runtime.rhel.7-x64.Microsoft.NETCore.Runtime.CoreCLR -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}runtime.rhel.7-x64.runtime.native.System -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}runtime.rhel.7-x64.runtime.native.System.IO.Compression -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}runtime.rhel.7-x64.runtime.native.System.Net.Http -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}runtime.rhel.7-x64.runtime.native.System.Net.Security -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}runtime.rhel.7-x64.runtime.native.System.Security.Cryptography -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}runtime.ubuntu.14.04-x64.Microsoft.NETCore.DotNetHost -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}runtime.ubuntu.14.04-x64.Microsoft.NETCore.DotNetHostPolicy -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}runtime.ubuntu.14.04-x64.Microsoft.NETCore.DotNetHostResolver -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}runtime.ubuntu.14.04-x64.Microsoft.NETCore.Jit -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}runtime.ubuntu.14.04-x64.Microsoft.NETCore.Runtime.CoreCLR -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}runtime.ubuntu.14.04-x64.runtime.native.System -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}runtime.ubuntu.14.04-x64.runtime.native.System.IO.Compression -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}runtime.ubuntu.14.04-x64.runtime.native.System.Net.Http -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}runtime.ubuntu.14.04-x64.runtime.native.System.Net.Security -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}runtime.ubuntu.16.04-x64.Microsoft.NETCore.DotNetHost -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}runtime.ubuntu.16.04-x64.Microsoft.NETCore.DotNetHostPolicy -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}}\pard \ltrpar -\s16\ql \fi-360\li720\ri0\widctlpar\wrapdefault\aspalpha\aspnum\faauto\ls8\adjustright\rin0\lin720\itap0\pararsid10169937 {\rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f2\fs18\insrsid7092439\charrsid10169937 -runtime.ubuntu.16.04-x64.Microsoft.NETCore.DotNetHostResolver -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}runtime.ubuntu.16.04-x64.Microsoft.NETCore.Jit -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}runtime.ubuntu.16.04-x64.Microsoft.NETCore.Runtime.CoreCLR -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}runtime.ubuntu.16.04-x64.runtime.native.System -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}runtime.ubuntu.16.04-x64.runtime.native.System.IO.Compression -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}runtime.ubuntu.16.04-x64.runtime.native.System.Net.Http -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}runtime.ubuntu.16.04-x64.runtime.native.System.Net.Security -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}runtime.unix.Microsoft.Win32.Primitives -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}runtime.unix.System.Console -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}runtime.unix.System.Diagnostics.Debug -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}runtime.unix.System.IO.FileSystem -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}runtime.unix.System.Net.Primitives -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}runtime.unix.System.Net.Sockets -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}runtime.unix.System.Private.Uri -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}runtime.unix.System.Runtime.Extensions -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}runtime.win.Microsoft.Win32.Primitives -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}runtime.win.System.Console -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}runtime.win.System.Diagnostics.Debug -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}runtime.win.System.IO.FileSystem -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}runtime.win.System.Net.Primitives -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}runtime.win.System.Net.Sockets -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}runtime.win.System.Runtime.Extensions -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}runtime.win7-x64.Microsoft.NETCore.DotNetHost -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}runtime.win7-x64.Microsoft.NETCore.DotNetHostPolicy -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}runtime.win7-x64.Microsoft.NETCore.DotNetHostResolver -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}runtime.win7-x64.Microsoft.NETCore.Jit -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}runtime.win7-x64.Microsoft.NETCore.Runtime.CoreCLR -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}runtime.win7-x64.Microsoft.NETCore.Windows.ApiSets -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}runtime.win7-x64.runtime.native.System.Data.SqlClient.sni -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}runtime.win7-x64.runtime.native.System.IO.Compression -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}runtime.win7-x86.runtime.native.System.Data.SqlClient.sni -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}runtime.win7.System.Private.Uri -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}runtime.win81-x64.Microsoft.NETCore.Windows.ApiSets -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.AppContext -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.Buffers -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.Collections -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.Collections.Concurrent -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.Collections.Immutable -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.Collections.NonGeneric -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.Collections.Specialized -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.ComponentModel -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.ComponentModel.Annotations -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.ComponentModel.EventBasedAsync -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.ComponentModel.Primitives -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.ComponentModel.TypeConverter -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.Console -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.Data.Common -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.Data.SqlClient -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.Diagnostics.Contracts -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.Diagnostics.Debug -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.Diagnostics.DiagnosticSource -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.Diagnostics.FileVersionInfo -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.Diagnostics.Process -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.Diagnostics.StackTrace -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.Diagnostics.TextWriterTraceListener -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.Diagnostics.Tools -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.Diagnostics.TraceSource -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.Diagnostics.Tracing -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.Dynamic.Runtime -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.Globalization -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.Globalization.Calendars -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.Globalization.Extensions -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.IO -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.IO.Compression -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.IO.Compression.ZipFile -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.IO.FileSystem -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.IO.FileSystem.AccessControl -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}}\pard \ltrpar -\s16\ql \fi-360\li720\ri0\widctlpar\wrapdefault\aspalpha\aspnum\faauto\ls7\adjustright\rin0\lin720\itap0\pararsid10169937 {\rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f2\fs18\insrsid7092439\charrsid10169937 System.IO.FileSystem.DriveInfo -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.IO.FileSystem.Primitives -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.IO.FileSystem.Watcher -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.IO.MemoryMappedFiles -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.IO.Packaging -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.IO.Pipes -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.IO.UnmanagedMemoryStream -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.Linq -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.Linq.Expressions -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.Linq.Parallel -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.Linq.Queryable -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.Net.Http -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.Net.Http.WinHttpHandler -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.Net.NameResolution -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.Net.NetworkInformation -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.Net.Ping -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.Net.Primitives -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.Net.Requests -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.Net.Security -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.Net.Sockets -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.Net.WebHeaderCollection -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.Net.WebSockets -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.Net.WebSockets.Client -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.Numerics.Vectors -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.ObjectModel -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.Private.DataContractSerialization -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.Private.ServiceModel -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.Private.Uri -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.Reflection -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.Reflection.DispatchProxy -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.Reflection.Emit -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.Reflection.Emit.ILGeneration -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.Reflection.Emit.Lightweight -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.Reflection.Extensions -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.Reflection.Metadata -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.Reflection.Primitives -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.Reflection.TypeExtensions -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.Resources.Reader -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}}\pard \ltrpar -\s16\ql \fi-360\li720\ri0\widctlpar\wrapdefault\aspalpha\aspnum\faauto\ls6\adjustright\rin0\lin720\itap0\pararsid10169937 {\rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f2\fs18\insrsid7092439\charrsid10169937 System.Resources.ResourceManager -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.Runtime -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.Runtime.CompilerServices.VisualC -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.Runtime.Extensions -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.Runtime.Handles -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.Runtime.InteropServices -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.Runtime.InteropServices.PInvoke -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.Runtime.InteropServices.RuntimeInformation -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.Runtime.Loader -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.Runtime.Numerics -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.Runtime.Serialization.Json -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.Runtime.Serialization.Primitives -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.Runtime.Serialization.Xml -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.Security.AccessControl -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.Security.Claims -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.Security.Cryptography.Algorithms -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.Security.Cryptography.Cng -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.Security.Cryptography.Csp -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.Security.Cryptography.Encoding -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.Security.Cryptography.OpenSsl -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.Security.Cryptography.Pkcs -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.Security.Cryptography.Primitives -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.Security.Cryptography.X509Certificates -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.Security.Principal -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.Security.Principal.Windows -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.Security.SecureString -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.ServiceModel.Duplex -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.ServiceModel.Http -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.ServiceModel.NetTcp -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.ServiceModel.Primitives -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}}\pard \ltrpar -\s16\ql \fi-360\li720\ri0\widctlpar\wrapdefault\aspalpha\aspnum\faauto\ls5\adjustright\rin0\lin720\itap0\pararsid10169937 {\rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f2\fs18\insrsid7092439\charrsid10169937 System.ServiceModel.Security -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.ServiceProcess.ServiceController -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.Text.Encoding -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.Text.Encoding.CodePages -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.Text.Encoding.Extensions -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.Text.Encodings.Web -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.Text.RegularExpressions -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.Threading -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.Threading.AccessControl -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.Threading.Overlapped -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.Threading.Tasks -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.Threading.Tasks.Dataflow -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.Threading.Tasks.Extensions -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.Threading.Tasks.Parallel -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.Threading.Thread -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.Threading.ThreadPool -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.Threading.Timer -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.Xml.ReaderWriter -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.Xml.XDocument -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.Xml.XmlDocument -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.Xml.XmlSerializer -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.Xml.XPath -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.Xml.XPath.XDocument -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}System.Xml.XPath.XmlDocument -\par }\pard\plain \ltrpar\s15\ql \li0\ri0\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid7092439 \rtlch\fcs1 \af0\afs24\alang1025 \ltrch\fcs0 \fs24\lang1033\langfe1033\cgrid\langnp1033\langfenp1033 {\rtlch\fcs1 \af2\afs20 -\ltrch\fcs0 \f2\fs18\cf17\lang9\langfe1033\langnp9\insrsid7092439 ------------------------------------------------------- -\par -\par }\pard\plain \ltrpar\ql \li0\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid7092439 \rtlch\fcs1 \af31507\afs22\alang1025 \ltrch\fcs0 \f31506\fs22\lang1033\langfe1033\cgrid\langnp1033\langfenp1033 { -\rtlch\fcs1 \ab\af40\afs24 \ltrch\fcs0 \b\f40\fs24\cf1\insrsid7092439\charrsid7092439 MICROSOFT SOFTWARE LICENSE TERMS }{\rtlch\fcs1 \ab\af40\afs28 \ltrch\fcs0 \b\f40\fs28\cf1\insrsid7092439\charrsid7092439 -\par }{\rtlch\fcs1 \ab\af40\afs24 \ltrch\fcs0 \b\f40\fs24\cf1\insrsid7092439\charrsid7092439 MICROSOFT .NET LIBRARY }{\rtlch\fcs1 \ab\af40\afs28 \ltrch\fcs0 \b\f40\fs28\cf1\insrsid7092439\charrsid7092439 -\par }{\rtlch\fcs1 \ab\af40\afs19 \ltrch\fcs0 \b\f40\fs19\cf1\insrsid7092439\charrsid7092439 -These license terms are an agreement between Microsoft Corporation (or based on where you live, one of its affiliates) and you. Please read them. They apply to the software named above, which includes the media on which you received it, if any. The terms -also apply to any Microsoft -\par }\pard \ltrpar\ql \fi-363\li720\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin720\itap0\pararsid7092439 {\rtlch\fcs1 \af40\afs19 \ltrch\fcs0 \f3\fs19\cf1\insrsid7092439\charrsid7092439 \'b7}{\rtlch\fcs1 \af0\afs14 -\ltrch\fcs0 \f0\fs14\cf1\insrsid7092439\charrsid7092439 \~\~\~\~\~\~\~\~\~}{\rtlch\fcs1 \af40\afs19 \ltrch\fcs0 \f40\fs19\cf1\insrsid7092439\charrsid7092439 updates,}{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \f0\fs24\insrsid7092439\charrsid7092439 }{ -\rtlch\fcs1 \af40\afs19 \ltrch\fcs0 \f40\fs19\cf1\insrsid7092439\charrsid7092439 -\par }{\rtlch\fcs1 \af40\afs19 \ltrch\fcs0 \f3\fs19\cf1\insrsid7092439\charrsid7092439 \'b7}{\rtlch\fcs1 \af0\afs14 \ltrch\fcs0 \f0\fs14\cf1\insrsid7092439\charrsid7092439 \~\~\~\~\~\~\~\~\~}{\rtlch\fcs1 \af40\afs19 \ltrch\fcs0 -\f40\fs19\cf1\insrsid7092439\charrsid7092439 supplements,}{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \f0\fs24\insrsid7092439\charrsid7092439 }{\rtlch\fcs1 \af40\afs19 \ltrch\fcs0 \f40\fs19\cf1\insrsid7092439\charrsid7092439 -\par }{\rtlch\fcs1 \af40\afs19 \ltrch\fcs0 \f3\fs19\cf1\insrsid7092439\charrsid7092439 \'b7}{\rtlch\fcs1 \af0\afs14 \ltrch\fcs0 \f0\fs14\cf1\insrsid7092439\charrsid7092439 \~\~\~\~\~\~\~\~\~}{\rtlch\fcs1 \af40\afs19 \ltrch\fcs0 -\f40\fs19\cf1\insrsid7092439\charrsid7092439 Internet-based services, and -\par }{\rtlch\fcs1 \af40\afs19 \ltrch\fcs0 \f3\fs19\cf1\insrsid7092439\charrsid7092439 \'b7}{\rtlch\fcs1 \af0\afs14 \ltrch\fcs0 \f0\fs14\cf1\insrsid7092439\charrsid7092439 \~\~\~\~\~\~\~\~\~}{\rtlch\fcs1 \af40\afs19 \ltrch\fcs0 -\f40\fs19\cf1\insrsid7092439\charrsid7092439 support services -\par }\pard \ltrpar\ql \li0\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid7092439 {\rtlch\fcs1 \ab\af40\afs19 \ltrch\fcs0 \b\f40\fs19\cf1\insrsid7092439\charrsid7092439 -for this software, unless other terms accompany those items. If so, those terms apply. -\par BY USING THE SOFTWARE, YOU ACCEPT THESE TERMS. IF YOU DO NOT ACCEPT THEM, DO NOT USE THE SOFTWARE. -\par IF YOU COMPLY WITH THESE LICENSE TERMS, YOU HAVE THE PERPETUAL RIGHTS BELOW. -\par }\pard \ltrpar\ql \fi-357\li357\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\outlinelevel0\adjustright\rin0\lin357\itap0\pararsid7092439 {\rtlch\fcs1 \ab\af40\afs20 \ltrch\fcs0 \b\f40\fs20\cf1\kerning36\insrsid7092439\charrsid7092439 1.}{ -\rtlch\fcs1 \ab\af0\afs24 \ltrch\fcs0 \b\f0\fs24\insrsid7092439\charrsid7092439 }{\rtlch\fcs1 \af0\afs14 \ltrch\fcs0 \f0\fs14\cf1\kerning36\insrsid7092439\charrsid7092439 \~\~\~\~}{\rtlch\fcs1 \ab\af0\afs24 \ltrch\fcs0 -\b\f0\fs24\insrsid7092439\charrsid7092439 }{\rtlch\fcs1 \ab\af40\afs19 \ltrch\fcs0 \b\f40\fs19\cf1\kerning36\insrsid7092439\charrsid7092439 INSTALLATION AND USE RIGHTS. -\par }\pard \ltrpar\ql \fi-363\li720\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\outlinelevel1\adjustright\rin0\lin720\itap0\pararsid7092439 {\rtlch\fcs1 \ab\af40\afs20 \ltrch\fcs0 \b\f40\fs20\cf1\insrsid7092439\charrsid7092439 a.}{ -\rtlch\fcs1 \ab\af0\afs24 \ltrch\fcs0 \b\f0\fs24\insrsid7092439\charrsid7092439 }{\rtlch\fcs1 \af0\afs14 \ltrch\fcs0 \f0\fs14\cf1\insrsid7092439\charrsid7092439 \~\~\~\~}{\rtlch\fcs1 \ab\af0\afs24 \ltrch\fcs0 \b\f0\fs24\insrsid7092439\charrsid7092439 }{ -\rtlch\fcs1 \ab\af40\afs19 \ltrch\fcs0 \b\f40\fs19\cf1\insrsid7092439\charrsid7092439 Installation and Use.}{\rtlch\fcs1 \ab\af0\afs24 \ltrch\fcs0 \b\f0\fs24\insrsid7092439\charrsid7092439 }{\rtlch\fcs1 \af40\afs20 \ltrch\fcs0 -\f40\fs20\cf1\insrsid7092439\charrsid7092439 \~You may install and use any number of copies of the software to design, develop and test your programs. }{\rtlch\fcs1 \ab\af40\afs19 \ltrch\fcs0 \b\f40\fs19\cf1\insrsid7092439\charrsid7092439 -\par }{\rtlch\fcs1 \ab\af40\afs20 \ltrch\fcs0 \b\f40\fs20\cf1\insrsid7092439\charrsid7092439 b.}{\rtlch\fcs1 \ab\af0\afs24 \ltrch\fcs0 \b\f0\fs24\insrsid7092439\charrsid7092439 }{\rtlch\fcs1 \af0\afs14 \ltrch\fcs0 \f0\fs14\cf1\insrsid7092439\charrsid7092439 -\~\~\~\~}{\rtlch\fcs1 \ab\af0\afs24 \ltrch\fcs0 \b\f0\fs24\insrsid7092439\charrsid7092439 }{\rtlch\fcs1 \ab\af40\afs19 \ltrch\fcs0 \b\f40\fs19\cf1\insrsid7092439\charrsid7092439 Third Party Programs.}{\rtlch\fcs1 \ab\af0\afs24 \ltrch\fcs0 -\b\f0\fs24\insrsid7092439\charrsid7092439 }{\rtlch\fcs1 \af40\afs20 \ltrch\fcs0 \f40\fs20\cf1\insrsid7092439\charrsid7092439 \~ -The software may include third party programs that Microsoft, not the third party, licenses to you under this agreement. Notices, if any, for the third party program are included for your information only. }{\rtlch\fcs1 \ab\af40\afs19 \ltrch\fcs0 -\b\f40\fs19\cf1\insrsid7092439\charrsid7092439 -\par }\pard \ltrpar\ql \fi-357\li357\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\outlinelevel0\adjustright\rin0\lin357\itap0\pararsid7092439 {\rtlch\fcs1 \ab\af40\afs20 \ltrch\fcs0 \b\f40\fs20\cf1\kerning36\insrsid7092439\charrsid7092439 2.}{ -\rtlch\fcs1 \ab\af0\afs24 \ltrch\fcs0 \b\f0\fs24\insrsid7092439\charrsid7092439 }{\rtlch\fcs1 \af0\afs14 \ltrch\fcs0 \f0\fs14\cf1\kerning36\insrsid7092439\charrsid7092439 \~\~\~\~}{\rtlch\fcs1 \ab\af0\afs24 \ltrch\fcs0 -\b\f0\fs24\insrsid7092439\charrsid7092439 }{\rtlch\fcs1 \ab\af40\afs19 \ltrch\fcs0 \b\f40\fs19\cf1\kerning36\insrsid7092439\charrsid7092439 DATA.\~}{\rtlch\fcs1 \ab\af0\afs24 \ltrch\fcs0 \b\f0\fs24\insrsid7092439\charrsid7092439 }{\rtlch\fcs1 -\af40\afs20 \ltrch\fcs0 \f40\fs20\cf1\insrsid7092439\charrsid7092439 The software may collect information about you and your use of the software, and send that to Microsoft. Microsoft may use this information to improve our products and services.\~ -You can learn more about data collection and use in the help documentation and the privacy statement at\~ }{\field\fldedit{\*\fldinst {\rtlch\fcs1 \af31507 \ltrch\fcs0 \insrsid11493340 - HYPERLINK "http://go.microsoft.com/fwlink/?LinkId=528096&clcid=0x409" \\t "_blank" }}{\fldrslt {\rtlch\fcs1 \af40\afs20 \ltrch\fcs0 \f40\fs20\ul\cf2\insrsid7092439\charrsid7092439 http://go.microsoft.com/fwlink/?LinkId=528096}{\rtlch\fcs1 \af0\afs24 -\ltrch\fcs0 \f0\fs24\ul\cf1\insrsid7092439\charrsid7092439 }}}\sectd \ltrsect\linex0\endnhere\sectlinegrid360\sectdefaultcl\sftnbj {\rtlch\fcs1 \af40\afs20 \ltrch\fcs0 \f40\fs20\cf1\insrsid7092439\charrsid7092439 -. Your use of the software operates as your consent to these practices. }{\rtlch\fcs1 \ab\af40\afs19 \ltrch\fcs0 \b\f40\fs19\cf1\kerning36\insrsid7092439\charrsid7092439 -\par }{\rtlch\fcs1 \ab\af40\afs20 \ltrch\fcs0 \b\f40\fs20\cf1\kerning36\insrsid7092439\charrsid7092439 3.}{\rtlch\fcs1 \ab\af0\afs24 \ltrch\fcs0 \b\f0\fs24\insrsid7092439\charrsid7092439 }{\rtlch\fcs1 \af0\afs14 \ltrch\fcs0 -\f0\fs14\cf1\kerning36\insrsid7092439\charrsid7092439 \~\~\~\~}{\rtlch\fcs1 \ab\af0\afs24 \ltrch\fcs0 \b\f0\fs24\insrsid7092439\charrsid7092439 }{\rtlch\fcs1 \ab\af40\afs20 \ltrch\fcs0 \b\f40\fs20\cf1\kerning36\insrsid7092439\charrsid7092439 -ADDITIONAL LICENSING REQUIREMENTS AND/OR USE RIGHTS. }{\rtlch\fcs1 \ab\af40\afs19 \ltrch\fcs0 \b\f40\fs19\cf1\kerning36\insrsid7092439\charrsid7092439 -\par }\pard \ltrpar\ql \fi-363\li720\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\outlinelevel1\adjustright\rin0\lin720\itap0\pararsid7092439 {\rtlch\fcs1 \ab\af40\afs20 \ltrch\fcs0 \b\f40\fs20\cf1\insrsid7092439\charrsid7092439 a.}{ -\rtlch\fcs1 \ab\af0\afs24 \ltrch\fcs0 \b\f0\fs24\insrsid7092439\charrsid7092439 }{\rtlch\fcs1 \af0\afs14 \ltrch\fcs0 \f0\fs14\cf1\insrsid7092439\charrsid7092439 \~\~\~\~}{\rtlch\fcs1 \ab\af0\afs24 \ltrch\fcs0 \b\f0\fs24\insrsid7092439\charrsid7092439 }{ -\rtlch\fcs1 \ab\af40\afs20 \ltrch\fcs0 \b\f40\fs20\cf1\insrsid7092439\charrsid7092439 DISTRIBUTABLE CODE.\~\~}{\rtlch\fcs1 \ab\af0\afs24 \ltrch\fcs0 \b\f0\fs24\insrsid7092439\charrsid7092439 }{\rtlch\fcs1 \af40\afs20 \ltrch\fcs0 -\f40\fs20\cf1\insrsid7092439\charrsid7092439 The software is comprised of Distributable Code. \'93Distributable Code\'94 is code that you are permitted to distribute in programs you develop if you comply with the terms below. }{\rtlch\fcs1 \ab\af40\afs19 -\ltrch\fcs0 \b\f40\fs19\cf1\insrsid7092439\charrsid7092439 -\par }\pard \ltrpar\ql \fi-357\li1077\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\outlinelevel2\adjustright\rin0\lin1077\itap0\pararsid7092439 {\rtlch\fcs1 \ab\af40\afs20 \ltrch\fcs0 \b\f40\fs20\cf1\insrsid7092439\charrsid7092439 i}{ -\rtlch\fcs1 \ab\af0\afs24 \ltrch\fcs0 \b\f0\fs24\insrsid7092439\charrsid7092439 }{\rtlch\fcs1 \ab\af40\afs20 \ltrch\fcs0 \b\f40\fs20\cf1\insrsid7092439\charrsid7092439 .}{\rtlch\fcs1 \ab\af0\afs24 \ltrch\fcs0 \b\f0\fs24\insrsid7092439\charrsid7092439 }{ -\rtlch\fcs1 \af0\afs14 \ltrch\fcs0 \f0\fs14\cf1\insrsid7092439\charrsid7092439 \~\~\~\~\~\~}{\rtlch\fcs1 \ab\af0\afs24 \ltrch\fcs0 \b\f0\fs24\insrsid7092439\charrsid7092439 }{\rtlch\fcs1 \ab\af40\afs20 \ltrch\fcs0 -\b\f40\fs20\cf1\insrsid7092439\charrsid7092439 Right to Use and Distribute.}{\rtlch\fcs1 \ab\af0\afs24 \ltrch\fcs0 \b\f0\fs24\insrsid7092439\charrsid7092439 }{\rtlch\fcs1 \af40\afs19 \ltrch\fcs0 \f40\fs19\cf1\insrsid7092439\charrsid7092439 -\par }\pard \ltrpar\ql \fi-357\li1434\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin1434\itap0\pararsid7092439 {\rtlch\fcs1 \af40\afs20 \ltrch\fcs0 \f3\fs20\cf1\insrsid7092439\charrsid7092439 \'b7}{\rtlch\fcs1 \af0\afs14 -\ltrch\fcs0 \f0\fs14\cf1\insrsid7092439\charrsid7092439 \~\~\~\~\~\~\~\~\~}{\rtlch\fcs1 \af40\afs20 \ltrch\fcs0 \f40\fs20\cf1\insrsid7092439\charrsid7092439 You may copy and distribute the object code form of the software. }{\rtlch\fcs1 \af40\afs19 -\ltrch\fcs0 \f40\fs19\cf1\insrsid7092439\charrsid7092439 -\par }{\rtlch\fcs1 \af40\afs20 \ltrch\fcs0 \f3\fs20\cf1\insrsid7092439\charrsid7092439 \'b7}{\rtlch\fcs1 \af0\afs14 \ltrch\fcs0 \f0\fs14\cf1\insrsid7092439\charrsid7092439 \~\~\~\~\~\~\~\~\~}{\rtlch\fcs1 \af40\afs20 \ltrch\fcs0 -\f40\fs20\cf1\insrsid7092439\charrsid7092439 Third Party Distribution. You may permit distributors of your programs to copy and distribute the Distributable Code as part of those programs. }{\rtlch\fcs1 \af40\afs19 \ltrch\fcs0 -\f40\fs19\cf1\insrsid7092439\charrsid7092439 -\par }\pard \ltrpar\ql \fi-357\li1077\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\outlinelevel2\adjustright\rin0\lin1077\itap0\pararsid7092439 {\rtlch\fcs1 \ab\af40\afs20 \ltrch\fcs0 \b\f40\fs20\cf1\insrsid7092439\charrsid7092439 ii.}{ -\rtlch\fcs1 \ab\af0\afs24 \ltrch\fcs0 \b\f0\fs24\insrsid7092439\charrsid7092439 }{\rtlch\fcs1 \af0\afs14 \ltrch\fcs0 \f0\fs14\cf1\insrsid7092439\charrsid7092439 \~\~\~\~}{\rtlch\fcs1 \ab\af0\afs24 \ltrch\fcs0 \b\f0\fs24\insrsid7092439\charrsid7092439 }{ -\rtlch\fcs1 \ab\af40\afs20 \ltrch\fcs0 \b\f40\fs20\cf1\insrsid7092439\charrsid7092439 Distribution Requirements.}{\rtlch\fcs1 \ab\af0\afs24 \ltrch\fcs0 \b\f0\fs24\insrsid7092439\charrsid7092439 }{\rtlch\fcs1 \af40\afs20 \ltrch\fcs0 -\f40\fs20\cf1\insrsid7092439\charrsid7092439 \~}{\rtlch\fcs1 \ab\af40\afs20 \ltrch\fcs0 \b\f40\fs20\cf1\insrsid7092439\charrsid7092439 For any Distributable Code you distribute, you must }{\rtlch\fcs1 \af40\afs19 \ltrch\fcs0 -\f40\fs19\cf1\insrsid7092439\charrsid7092439 -\par }\pard \ltrpar\ql \fi-357\li1434\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin1434\itap0\pararsid7092439 {\rtlch\fcs1 \af40\afs20 \ltrch\fcs0 \f3\fs20\cf1\insrsid7092439\charrsid7092439 \'b7}{\rtlch\fcs1 \af0\afs14 -\ltrch\fcs0 \f0\fs14\cf1\insrsid7092439\charrsid7092439 \~\~\~\~\~\~\~\~\~}{\rtlch\fcs1 \af40\afs20 \ltrch\fcs0 \f40\fs20\cf1\insrsid7092439\charrsid7092439 add significant primary functionality to it in your programs; }{\rtlch\fcs1 \af40\afs19 -\ltrch\fcs0 \f40\fs19\cf1\insrsid7092439\charrsid7092439 -\par }{\rtlch\fcs1 \af40\afs20 \ltrch\fcs0 \f3\fs20\cf1\insrsid7092439\charrsid7092439 \'b7}{\rtlch\fcs1 \af0\afs14 \ltrch\fcs0 \f0\fs14\cf1\insrsid7092439\charrsid7092439 \~\~\~\~\~\~\~\~\~}{\rtlch\fcs1 \af40\afs20 \ltrch\fcs0 -\f40\fs20\cf1\insrsid7092439\charrsid7092439 require distributors and external end users to agree to terms that protect it at least as much as this agreement; }{\rtlch\fcs1 \af40\afs19 \ltrch\fcs0 \f40\fs19\cf1\insrsid7092439\charrsid7092439 -\par }{\rtlch\fcs1 \af40\afs20 \ltrch\fcs0 \f3\fs20\cf1\insrsid7092439\charrsid7092439 \'b7}{\rtlch\fcs1 \af0\afs14 \ltrch\fcs0 \f0\fs14\cf1\insrsid7092439\charrsid7092439 \~\~\~\~\~\~\~\~\~}{\rtlch\fcs1 \af40\afs20 \ltrch\fcs0 -\f40\fs20\cf1\insrsid7092439\charrsid7092439 display your valid copyright notice on your programs; and }{\rtlch\fcs1 \af40\afs19 \ltrch\fcs0 \f40\fs19\cf1\insrsid7092439\charrsid7092439 -\par }{\rtlch\fcs1 \af40\afs20 \ltrch\fcs0 \f3\fs20\cf1\insrsid7092439\charrsid7092439 \'b7}{\rtlch\fcs1 \af0\afs14 \ltrch\fcs0 \f0\fs14\cf1\insrsid7092439\charrsid7092439 \~\~\~\~\~\~\~\~\~}{\rtlch\fcs1 \af40\afs20 \ltrch\fcs0 -\f40\fs20\cf1\insrsid7092439\charrsid7092439 indemnify, defend, and hold harmless Microsoft from any claims, including attorneys\rquote fees, related to the distribution or use of your programs. }{\rtlch\fcs1 \af40\afs19 \ltrch\fcs0 -\f40\fs19\cf1\insrsid7092439\charrsid7092439 -\par }\pard \ltrpar\ql \fi-357\li1077\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\outlinelevel2\adjustright\rin0\lin1077\itap0\pararsid7092439 {\rtlch\fcs1 \ab\af40\afs20 \ltrch\fcs0 \b\f40\fs20\cf1\insrsid7092439\charrsid7092439 iii.}{ -\rtlch\fcs1 \ab\af0\afs24 \ltrch\fcs0 \b\f0\fs24\insrsid7092439\charrsid7092439 }{\rtlch\fcs1 \af0\afs14 \ltrch\fcs0 \f0\fs14\cf1\insrsid7092439\charrsid7092439 \~\~\~}{\rtlch\fcs1 \ab\af0\afs24 \ltrch\fcs0 \b\f0\fs24\insrsid7092439\charrsid7092439 }{ -\rtlch\fcs1 \ab\af40\afs20 \ltrch\fcs0 \b\f40\fs20\cf1\insrsid7092439\charrsid7092439 Distribution Restrictions.}{\rtlch\fcs1 \ab\af0\afs24 \ltrch\fcs0 \b\f0\fs24\insrsid7092439\charrsid7092439 }{\rtlch\fcs1 \af40\afs20 \ltrch\fcs0 -\f40\fs20\cf1\insrsid7092439\charrsid7092439 \~}{\rtlch\fcs1 \ab\af40\afs20 \ltrch\fcs0 \b\f40\fs20\cf1\insrsid7092439\charrsid7092439 You may not}{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \f0\fs24\insrsid7092439\charrsid7092439 }{\rtlch\fcs1 \af40\afs19 -\ltrch\fcs0 \f40\fs19\cf1\insrsid7092439\charrsid7092439 -\par }\pard \ltrpar\ql \fi-357\li1434\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin1434\itap0\pararsid7092439 {\rtlch\fcs1 \af40\afs20 \ltrch\fcs0 \f3\fs20\cf1\insrsid7092439\charrsid7092439 \'b7}{\rtlch\fcs1 \af0\afs14 -\ltrch\fcs0 \f0\fs14\cf1\insrsid7092439\charrsid7092439 \~\~\~\~\~\~\~\~\~}{\rtlch\fcs1 \af40\afs20 \ltrch\fcs0 \f40\fs20\cf1\insrsid7092439\charrsid7092439 alter any copyright, trademark or patent notice in the Distributable Code; }{\rtlch\fcs1 -\af40\afs19 \ltrch\fcs0 \f40\fs19\cf1\insrsid7092439\charrsid7092439 -\par }{\rtlch\fcs1 \af40\afs20 \ltrch\fcs0 \f3\fs20\cf1\insrsid7092439\charrsid7092439 \'b7}{\rtlch\fcs1 \af0\afs14 \ltrch\fcs0 \f0\fs14\cf1\insrsid7092439\charrsid7092439 \~\~\~\~\~\~\~\~\~}{\rtlch\fcs1 \af40\afs20 \ltrch\fcs0 -\f40\fs20\cf1\insrsid7092439\charrsid7092439 use Microsoft\rquote s trademarks in your programs\rquote names or in a way that suggests your programs come from or are endorsed by Microsoft; }{\rtlch\fcs1 \af40\afs19 \ltrch\fcs0 -\f40\fs19\cf1\insrsid7092439\charrsid7092439 -\par }{\rtlch\fcs1 \af40\afs20 \ltrch\fcs0 \f3\fs20\cf1\insrsid7092439\charrsid7092439 \'b7}{\rtlch\fcs1 \af0\afs14 \ltrch\fcs0 \f0\fs14\cf1\insrsid7092439\charrsid7092439 \~\~\~\~\~\~\~\~\~}{\rtlch\fcs1 \af40\afs20 \ltrch\fcs0 -\f40\fs20\cf1\insrsid7092439\charrsid7092439 include Distributable Code in malicious, deceptive or unlawful programs; or }{\rtlch\fcs1 \af40\afs19 \ltrch\fcs0 \f40\fs19\cf1\insrsid7092439\charrsid7092439 -\par }{\rtlch\fcs1 \af40\afs20 \ltrch\fcs0 \f3\fs20\cf1\insrsid7092439\charrsid7092439 \'b7}{\rtlch\fcs1 \af0\afs14 \ltrch\fcs0 \f0\fs14\cf1\insrsid7092439\charrsid7092439 \~\~\~\~\~\~\~\~\~}{\rtlch\fcs1 \af40\afs20 \ltrch\fcs0 -\f40\fs20\cf1\insrsid7092439\charrsid7092439 - modify or distribute the source code of any Distributable Code so that any part of it becomes subject to an Excluded License. An Excluded License is one that requires, as a condition of use, modification or distribution, that }{\rtlch\fcs1 \af40\afs19 -\ltrch\fcs0 \f40\fs19\cf1\insrsid7092439\charrsid7092439 -\par }\pard \ltrpar\ql \fi-358\li1792\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin1792\itap0\pararsid7092439 {\rtlch\fcs1 \af40\afs20 \ltrch\fcs0 \f3\fs20\cf1\insrsid7092439\charrsid7092439 \'b7}{\rtlch\fcs1 \af0\afs14 -\ltrch\fcs0 \f0\fs14\cf1\insrsid7092439\charrsid7092439 \~\~\~\~\~\~\~\~\~}{\rtlch\fcs1 \af40\afs20 \ltrch\fcs0 \f40\fs20\cf1\insrsid7092439\charrsid7092439 the code be disclosed or distributed in source code form; or }{\rtlch\fcs1 \af40\afs19 -\ltrch\fcs0 \f40\fs19\cf1\insrsid7092439\charrsid7092439 -\par }{\rtlch\fcs1 \af40\afs20 \ltrch\fcs0 \f3\fs20\cf1\insrsid7092439\charrsid7092439 \'b7}{\rtlch\fcs1 \af0\afs14 \ltrch\fcs0 \f0\fs14\cf1\insrsid7092439\charrsid7092439 \~\~\~\~\~\~\~\~\~}{\rtlch\fcs1 \af40\afs20 \ltrch\fcs0 -\f40\fs20\cf1\insrsid7092439\charrsid7092439 others have the right to modify it. }{\rtlch\fcs1 \af40\afs19 \ltrch\fcs0 \f40\fs19\cf1\insrsid7092439\charrsid7092439 -\par }\pard \ltrpar\ql \fi-357\li357\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\outlinelevel0\adjustright\rin0\lin357\itap0\pararsid7092439 {\rtlch\fcs1 \ab\af40\afs20 \ltrch\fcs0 \b\f40\fs20\cf1\kerning36\insrsid7092439\charrsid7092439 4.}{ -\rtlch\fcs1 \ab\af0\afs24 \ltrch\fcs0 \b\f0\fs24\insrsid7092439\charrsid7092439 }{\rtlch\fcs1 \af0\afs14 \ltrch\fcs0 \f0\fs14\cf1\kerning36\insrsid7092439\charrsid7092439 \~\~\~\~}{\rtlch\fcs1 \ab\af0\afs24 \ltrch\fcs0 -\b\f0\fs24\insrsid7092439\charrsid7092439 }{\rtlch\fcs1 \ab\af40\afs19 \ltrch\fcs0 \b\f40\fs19\cf1\kerning36\insrsid7092439\charrsid7092439 SCOPE OF LICENSE.\~}{\rtlch\fcs1 \ab\af0\afs24 \ltrch\fcs0 \b\f0\fs24\insrsid7092439\charrsid7092439 }{ -\rtlch\fcs1 \af40\afs19 \ltrch\fcs0 \f40\fs19\cf1\kerning36\insrsid7092439\charrsid7092439 -The software is licensed, not sold. This agreement only gives you some rights to use the software. Microsoft reserves all other rights. Unless applicable law gives you more rights despite this limitation, you may use the software only as expressly permitt -ed in this agreement. In doing so, you must comply with any technical limitations in the software that only allow you to use it in certain ways. You may not }{\rtlch\fcs1 \ab\af40\afs19 \ltrch\fcs0 \b\f40\fs19\cf1\kerning36\insrsid7092439\charrsid7092439 - -\par }\pard \ltrpar\ql \fi-363\li720\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin720\itap0\pararsid7092439 {\rtlch\fcs1 \af40\afs19 \ltrch\fcs0 \f3\fs19\cf1\insrsid7092439\charrsid7092439 \'b7}{\rtlch\fcs1 \af0\afs14 -\ltrch\fcs0 \f0\fs14\cf1\insrsid7092439\charrsid7092439 \~\~\~\~\~\~\~\~\~}{\rtlch\fcs1 \af40\afs19 \ltrch\fcs0 \f40\fs19\cf1\insrsid7092439\charrsid7092439 work around any technical limitations in the software; -\par }{\rtlch\fcs1 \af40\afs19 \ltrch\fcs0 \f3\fs19\cf1\insrsid7092439\charrsid7092439 \'b7}{\rtlch\fcs1 \af0\afs14 \ltrch\fcs0 \f0\fs14\cf1\insrsid7092439\charrsid7092439 \~\~\~\~\~\~\~\~\~}{\rtlch\fcs1 \af40\afs19 \ltrch\fcs0 -\f40\fs19\cf1\insrsid7092439\charrsid7092439 reverse engineer, decompile or disassemble the software, except and only to the extent that applicable law expressly permits, despite this limitation; -\par }{\rtlch\fcs1 \af40\afs19 \ltrch\fcs0 \f3\fs19\cf1\insrsid7092439\charrsid7092439 \'b7}{\rtlch\fcs1 \af0\afs14 \ltrch\fcs0 \f0\fs14\cf1\insrsid7092439\charrsid7092439 \~\~\~\~\~\~\~\~\~}{\rtlch\fcs1 \af40\afs19 \ltrch\fcs0 -\f40\fs19\cf1\insrsid7092439\charrsid7092439 publish the software for others to copy; -\par }{\rtlch\fcs1 \af40\afs19 \ltrch\fcs0 \f3\fs19\cf1\insrsid7092439\charrsid7092439 \'b7}{\rtlch\fcs1 \af0\afs14 \ltrch\fcs0 \f0\fs14\cf1\insrsid7092439\charrsid7092439 \~\~\~\~\~\~\~\~\~}{\rtlch\fcs1 \af40\afs19 \ltrch\fcs0 -\f40\fs19\cf1\insrsid7092439\charrsid7092439 rent, lease or lend the software; -\par }{\rtlch\fcs1 \af40\afs19 \ltrch\fcs0 \f3\fs19\cf1\insrsid7092439\charrsid7092439 \'b7}{\rtlch\fcs1 \af0\afs14 \ltrch\fcs0 \f0\fs14\cf1\insrsid7092439\charrsid7092439 \~\~\~\~\~\~\~\~\~}{\rtlch\fcs1 \af40\afs19 \ltrch\fcs0 -\f40\fs19\cf1\insrsid7092439\charrsid7092439 transfer the software or this agreement to any third party; or -\par }{\rtlch\fcs1 \af40\afs19 \ltrch\fcs0 \f3\fs19\cf1\insrsid7092439\charrsid7092439 \'b7}{\rtlch\fcs1 \af0\afs14 \ltrch\fcs0 \f0\fs14\cf1\insrsid7092439\charrsid7092439 \~\~\~\~\~\~\~\~\~}{\rtlch\fcs1 \af40\afs19 \ltrch\fcs0 -\f40\fs19\cf1\insrsid7092439\charrsid7092439 use the software for commercial software hosting services. -\par }\pard \ltrpar\ql \fi-357\li357\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\outlinelevel0\adjustright\rin0\lin357\itap0\pararsid7092439 {\rtlch\fcs1 \ab\af40\afs20 \ltrch\fcs0 \b\f40\fs20\cf1\kerning36\insrsid7092439\charrsid7092439 5.}{ -\rtlch\fcs1 \ab\af0\afs24 \ltrch\fcs0 \b\f0\fs24\insrsid7092439\charrsid7092439 }{\rtlch\fcs1 \af0\afs14 \ltrch\fcs0 \f0\fs14\cf1\kerning36\insrsid7092439\charrsid7092439 \~\~\~\~}{\rtlch\fcs1 \ab\af0\afs24 \ltrch\fcs0 -\b\f0\fs24\insrsid7092439\charrsid7092439 }{\rtlch\fcs1 \ab\af40\afs19 \ltrch\fcs0 \b\f40\fs19\cf1\kerning36\insrsid7092439\charrsid7092439 BACKUP COPY.\~}{\rtlch\fcs1 \ab\af0\afs24 \ltrch\fcs0 \b\f0\fs24\insrsid7092439\charrsid7092439 }{\rtlch\fcs1 -\af40\afs19 \ltrch\fcs0 \f40\fs19\cf1\kerning36\insrsid7092439\charrsid7092439 You may make one backup copy of the software. You may use it only to reinstall the software. }{\rtlch\fcs1 \ab\af40\afs19 \ltrch\fcs0 -\b\f40\fs19\cf1\kerning36\insrsid7092439\charrsid7092439 -\par }{\rtlch\fcs1 \ab\af40\afs20 \ltrch\fcs0 \b\f40\fs20\cf1\kerning36\insrsid7092439\charrsid7092439 6.}{\rtlch\fcs1 \ab\af0\afs24 \ltrch\fcs0 \b\f0\fs24\insrsid7092439\charrsid7092439 }{\rtlch\fcs1 \af0\afs14 \ltrch\fcs0 -\f0\fs14\cf1\kerning36\insrsid7092439\charrsid7092439 \~\~\~\~}{\rtlch\fcs1 \ab\af0\afs24 \ltrch\fcs0 \b\f0\fs24\insrsid7092439\charrsid7092439 }{\rtlch\fcs1 \ab\af40\afs19 \ltrch\fcs0 \b\f40\fs19\cf1\kerning36\insrsid7092439\charrsid7092439 -DOCUMENTATION.\~}{\rtlch\fcs1 \ab\af0\afs24 \ltrch\fcs0 \b\f0\fs24\insrsid7092439\charrsid7092439 }{\rtlch\fcs1 \af40\afs19 \ltrch\fcs0 \f40\fs19\cf1\kerning36\insrsid7092439\charrsid7092439 -Any person that has valid access to your computer or internal network may copy and use the documentation for your internal, reference purposes. }{\rtlch\fcs1 \ab\af40\afs19 \ltrch\fcs0 \b\f40\fs19\cf1\kerning36\insrsid7092439\charrsid7092439 -\par }{\rtlch\fcs1 \ab\af40\afs20 \ltrch\fcs0 \b\f40\fs20\cf1\kerning36\insrsid7092439\charrsid7092439 7.}{\rtlch\fcs1 \ab\af0\afs24 \ltrch\fcs0 \b\f0\fs24\insrsid7092439\charrsid7092439 }{\rtlch\fcs1 \af0\afs14 \ltrch\fcs0 -\f0\fs14\cf1\kerning36\insrsid7092439\charrsid7092439 \~\~\~\~}{\rtlch\fcs1 \ab\af0\afs24 \ltrch\fcs0 \b\f0\fs24\insrsid7092439\charrsid7092439 }{\rtlch\fcs1 \ab\af40\afs19 \ltrch\fcs0 \b\f40\fs19\cf1\kerning36\insrsid7092439\charrsid7092439 -EXPORT RESTRICTIONS.\~ }{\rtlch\fcs1 \af40\afs19 \ltrch\fcs0 \f40\fs19\cf1\kerning36\insrsid7092439\charrsid7092439 -The software is subject to United States export laws and regulations. You must comply with all domestic and international export laws and regulations that apply to the software. These laws include restrictions on destin -ations, end users and end use. For additional information, see\~ }{\rtlch\fcs1 \af40\afs20 \ltrch\fcs0 \f40\fs20\kerning36\insrsid7092439\charrsid7092439 www.microsoft.com/exporting}{\rtlch\fcs1 \af40\afs19 \ltrch\fcs0 -\f40\fs19\cf1\kerning36\insrsid7092439\charrsid7092439 .}{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \f0\fs24\insrsid7092439\charrsid7092439 }{\rtlch\fcs1 \ab\af40\afs19 \ltrch\fcs0 \b\f40\fs19\cf1\kerning36\insrsid7092439\charrsid7092439 -\par }{\rtlch\fcs1 \ab\af40\afs20 \ltrch\fcs0 \b\f40\fs20\cf1\kerning36\insrsid7092439\charrsid7092439 8.}{\rtlch\fcs1 \ab\af0\afs24 \ltrch\fcs0 \b\f0\fs24\insrsid7092439\charrsid7092439 }{\rtlch\fcs1 \af0\afs14 \ltrch\fcs0 -\f0\fs14\cf1\kerning36\insrsid7092439\charrsid7092439 \~\~\~\~}{\rtlch\fcs1 \ab\af0\afs24 \ltrch\fcs0 \b\f0\fs24\insrsid7092439\charrsid7092439 }{\rtlch\fcs1 \ab\af40\afs19 \ltrch\fcs0 \b\f40\fs19\cf1\kerning36\insrsid7092439\charrsid7092439 -SUPPORT SERVICES.\~}{\rtlch\fcs1 \ab\af0\afs24 \ltrch\fcs0 \b\f0\fs24\insrsid7092439\charrsid7092439 }{\rtlch\fcs1 \af40\afs19 \ltrch\fcs0 \f40\fs19\cf1\kerning36\insrsid7092439\charrsid7092439 Because this software is \'93as is,\'94 - we may not provide support services for it. }{\rtlch\fcs1 \ab\af40\afs19 \ltrch\fcs0 \b\f40\fs19\cf1\kerning36\insrsid7092439\charrsid7092439 -\par }{\rtlch\fcs1 \ab\af40\afs20 \ltrch\fcs0 \b\f40\fs20\cf1\kerning36\insrsid7092439\charrsid7092439 9.}{\rtlch\fcs1 \ab\af0\afs24 \ltrch\fcs0 \b\f0\fs24\insrsid7092439\charrsid7092439 }{\rtlch\fcs1 \af0\afs14 \ltrch\fcs0 -\f0\fs14\cf1\kerning36\insrsid7092439\charrsid7092439 \~\~\~\~}{\rtlch\fcs1 \ab\af0\afs24 \ltrch\fcs0 \b\f0\fs24\insrsid7092439\charrsid7092439 }{\rtlch\fcs1 \ab\af40\afs19 \ltrch\fcs0 \b\f40\fs19\cf1\kerning36\insrsid7092439\charrsid7092439 -ENTIRE AGREEMENT.\~}{\rtlch\fcs1 \ab\af0\afs24 \ltrch\fcs0 \b\f0\fs24\insrsid7092439\charrsid7092439 }{\rtlch\fcs1 \af40\afs19 \ltrch\fcs0 \f40\fs19\cf1\kerning36\insrsid7092439\charrsid7092439 -This agreement, and the terms for supplements, updates, Internet-based services and support services that you use, are the entire agreement for the software and support services. }{\rtlch\fcs1 \ab\af40\afs19 \ltrch\fcs0 -\b\f40\fs19\cf1\kerning36\insrsid7092439\charrsid7092439 -\par }{\rtlch\fcs1 \ab\af40\afs20 \ltrch\fcs0 \b\f40\fs20\cf1\kerning36\insrsid7092439\charrsid7092439 10.}{\rtlch\fcs1 \ab\af0\afs24 \ltrch\fcs0 \b\f0\fs24\insrsid7092439\charrsid7092439 }{\rtlch\fcs1 \af0\afs14 \ltrch\fcs0 -\f0\fs14\cf1\kerning36\insrsid7092439\charrsid7092439 \~\~\~}{\rtlch\fcs1 \ab\af0\afs24 \ltrch\fcs0 \b\f0\fs24\insrsid7092439\charrsid7092439 }{\rtlch\fcs1 \ab\af40\afs19 \ltrch\fcs0 \b\f40\fs19\cf1\kerning36\insrsid7092439\charrsid7092439 -APPLICABLE LAW.}{\rtlch\fcs1 \ab\af0\afs24 \ltrch\fcs0 \b\f0\fs24\insrsid7092439\charrsid7092439 }{\rtlch\fcs1 \ab\af40\afs19 \ltrch\fcs0 \b\f40\fs19\cf1\kerning36\insrsid7092439\charrsid7092439 -\par }\pard \ltrpar\ql \fi-363\li720\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\outlinelevel1\adjustright\rin0\lin720\itap0\pararsid7092439 {\rtlch\fcs1 \ab\af40\afs20 \ltrch\fcs0 \b\f40\fs20\cf1\insrsid7092439\charrsid7092439 a.}{ -\rtlch\fcs1 \ab\af0\afs24 \ltrch\fcs0 \b\f0\fs24\insrsid7092439\charrsid7092439 }{\rtlch\fcs1 \af0\afs14 \ltrch\fcs0 \f0\fs14\cf1\insrsid7092439\charrsid7092439 \~\~\~\~}{\rtlch\fcs1 \ab\af0\afs24 \ltrch\fcs0 \b\f0\fs24\insrsid7092439\charrsid7092439 }{ -\rtlch\fcs1 \ab\af40\afs19 \ltrch\fcs0 \b\f40\fs19\cf1\insrsid7092439\charrsid7092439 United States.\~}{\rtlch\fcs1 \ab\af0\afs24 \ltrch\fcs0 \b\f0\fs24\insrsid7092439\charrsid7092439 }{\rtlch\fcs1 \af40\afs19 \ltrch\fcs0 -\f40\fs19\cf1\insrsid7092439\charrsid7092439 -If you acquired the software in the United States, Washington state law governs the interpretation of this agreement and applies to claims for breach of it, regardless of conflict of laws principles. The laws of the state where you live govern all other c -laims, including claims under state consumer protection laws, unfair competition laws, and in tort. }{\rtlch\fcs1 \ab\af40\afs19 \ltrch\fcs0 \b\f40\fs19\cf1\insrsid7092439\charrsid7092439 -\par }{\rtlch\fcs1 \ab\af40\afs20 \ltrch\fcs0 \b\f40\fs20\cf1\insrsid7092439\charrsid7092439 b.}{\rtlch\fcs1 \ab\af0\afs24 \ltrch\fcs0 \b\f0\fs24\insrsid7092439\charrsid7092439 }{\rtlch\fcs1 \af0\afs14 \ltrch\fcs0 \f0\fs14\cf1\insrsid7092439\charrsid7092439 -\~\~\~\~}{\rtlch\fcs1 \ab\af0\afs24 \ltrch\fcs0 \b\f0\fs24\insrsid7092439\charrsid7092439 }{\rtlch\fcs1 \ab\af40\afs19 \ltrch\fcs0 \b\f40\fs19\cf1\insrsid7092439\charrsid7092439 -Outside the United States. If you acquired the software in any other country, the laws of that country apply. -\par }\pard \ltrpar\ql \fi-357\li357\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\outlinelevel0\adjustright\rin0\lin357\itap0\pararsid7092439 {\rtlch\fcs1 \ab\af40\afs20 \ltrch\fcs0 \b\f40\fs20\cf1\kerning36\insrsid7092439\charrsid7092439 11.}{ -\rtlch\fcs1 \ab\af0\afs24 \ltrch\fcs0 \b\f0\fs24\insrsid7092439\charrsid7092439 }{\rtlch\fcs1 \af0\afs14 \ltrch\fcs0 \f0\fs14\cf1\kerning36\insrsid7092439\charrsid7092439 \~\~}{\rtlch\fcs1 \ab\af0\afs24 \ltrch\fcs0 -\b\f0\fs24\insrsid7092439\charrsid7092439 }{\rtlch\fcs1 \ab\af40\afs19 \ltrch\fcs0 \b\f40\fs19\cf1\kerning36\insrsid7092439\charrsid7092439 LEGAL EFFECT.\~}{\rtlch\fcs1 \ab\af0\afs24 \ltrch\fcs0 \b\f0\fs24\insrsid7092439\charrsid7092439 }{\rtlch\fcs1 -\af40\afs19 \ltrch\fcs0 \f40\fs19\cf1\kerning36\insrsid7092439\charrsid7092439 -This agreement describes certain legal rights. You may have other rights under the laws of your country. You may also have rights with respect to the party from whom you acquired the software. This agreement does not change your rights under the laws of y -our country if the laws of your country do not permit it to do so. }{\rtlch\fcs1 \ab\af40\afs19 \ltrch\fcs0 \b\f40\fs19\cf1\kerning36\insrsid7092439\charrsid7092439 -\par }{\rtlch\fcs1 \ab\af40\afs20 \ltrch\fcs0 \b\f40\fs20\cf1\kerning36\insrsid7092439\charrsid7092439 12.}{\rtlch\fcs1 \ab\af0\afs24 \ltrch\fcs0 \b\f0\fs24\insrsid7092439\charrsid7092439 }{\rtlch\fcs1 \af0\afs14 \ltrch\fcs0 -\f0\fs14\cf1\kerning36\insrsid7092439\charrsid7092439 \~\~}{\rtlch\fcs1 \ab\af0\afs24 \ltrch\fcs0 \b\f0\fs24\insrsid7092439\charrsid7092439 }{\rtlch\fcs1 \ab\af40\afs19 \ltrch\fcs0 \b\f40\fs19\cf1\kerning36\insrsid7092439\charrsid7092439 -DISCLAIMER OF WARRANTY. THE SOFTWARE IS LICENSED \'93AS-IS.\'94 YOU BEAR THE RISK OF USING IT. MICROSOFT GIVES NO EXPRESS WARRANTIES, GUARANTEES OR CONDITIONS. YOU MAY HAVE ADDITIONAL CONS -UMER RIGHTS OR STATUTORY GUARANTEES UNDER YOUR LOCAL LAWS WHICH THIS AGREEMENT CANNOT CHANGE. TO THE EXTENT PERMITTED UNDER YOUR LOCAL LAWS, MICROSOFT EXCLUDES THE IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMEN -T. -\par }\pard \ltrpar\ql \li357\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin357\itap0\pararsid7092439 {\rtlch\fcs1 \ab\af40\afs19 \ltrch\fcs0 \b\f40\fs19\cf1\insrsid7092439\charrsid7092439 FOR AUSTRALIA \endash - YOU HAVE STATUTORY GUARANTEES UNDER THE AUSTRALIAN CONSUMER LAW AND NOTHING IN THESE TERMS IS INTENDED TO AFFECT THOSE RIGHTS. }{\rtlch\fcs1 \af40\afs19 \ltrch\fcs0 \f40\fs19\cf1\insrsid7092439\charrsid7092439 -\par }\pard \ltrpar\ql \fi-357\li357\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\outlinelevel0\adjustright\rin0\lin357\itap0\pararsid7092439 {\rtlch\fcs1 \ab\af40\afs20 \ltrch\fcs0 \b\f40\fs20\cf1\kerning36\insrsid7092439\charrsid7092439 13.}{ -\rtlch\fcs1 \ab\af0\afs24 \ltrch\fcs0 \b\f0\fs24\insrsid7092439\charrsid7092439 }{\rtlch\fcs1 \af0\afs14 \ltrch\fcs0 \f0\fs14\cf1\kerning36\insrsid7092439\charrsid7092439 \~\~}{\rtlch\fcs1 \ab\af0\afs24 \ltrch\fcs0 -\b\f0\fs24\insrsid7092439\charrsid7092439 }{\rtlch\fcs1 \ab\af40\afs19 \ltrch\fcs0 \b\f40\fs19\cf1\kerning36\insrsid7092439\charrsid7092439 LIMITATION ON AND EXCLUSION OF REMEDIES AND DAMAGES. YOU CAN RECOVER FROM MICROSOFT AND ITS SUPPLIERS -ONLY DIRECT DAMAGES UP TO U.S. $5.00. YOU CANNOT RECOVER ANY OTHER DAMAGES, INCLUDING CONSEQUENTIAL, LOST PROFITS, SPECIAL, INDIRECT OR INCIDENTAL DAMAGES. -\par }\pard \ltrpar\ql \li357\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin357\itap0\pararsid7092439 {\rtlch\fcs1 \af40\afs19 \ltrch\fcs0 \f40\fs19\cf1\insrsid7092439\charrsid7092439 This limitation applies to}{\rtlch\fcs1 -\af0\afs24 \ltrch\fcs0 \f0\fs24\insrsid7092439\charrsid7092439 }{\rtlch\fcs1 \af40\afs19 \ltrch\fcs0 \f40\fs19\cf1\insrsid7092439\charrsid7092439 -\par }\pard \ltrpar\ql \fi-363\li720\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin720\itap0\pararsid7092439 {\rtlch\fcs1 \af40\afs19 \ltrch\fcs0 \f3\fs19\cf1\insrsid7092439\charrsid7092439 \'b7}{\rtlch\fcs1 \af0\afs14 -\ltrch\fcs0 \f0\fs14\cf1\insrsid7092439\charrsid7092439 \~\~\~\~\~\~\~\~\~}{\rtlch\fcs1 \af40\afs19 \ltrch\fcs0 \f40\fs19\cf1\insrsid7092439\charrsid7092439 - anything related to the software, services, content (including code) on third party Internet sites, or third party programs; and -\par }{\rtlch\fcs1 \af40\afs19 \ltrch\fcs0 \f3\fs19\cf1\insrsid7092439\charrsid7092439 \'b7}{\rtlch\fcs1 \af0\afs14 \ltrch\fcs0 \f0\fs14\cf1\insrsid7092439\charrsid7092439 \~\~\~\~\~\~\~\~\~}{\rtlch\fcs1 \af40\afs19 \ltrch\fcs0 -\f40\fs19\cf1\insrsid7092439\charrsid7092439 claims for breach of contract, breach of warranty, guarantee or condition, strict liability, negligence, or other tort to the extent permitted by applicable law. -\par }\pard \ltrpar\ql \li0\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid7092439 {\rtlch\fcs1 \af40\afs19 \ltrch\fcs0 \f40\fs19\cf1\insrsid7092439\charrsid7092439 -It also applies even if Microsoft knew or should have known about the possibility of the damages. The above limitation or exclusion may not apply to you because your country may not allow the exclusion or limitation of incidental, consequential or other d -amages. -\par }{\rtlch\fcs1 \af40\afs19 \ltrch\fcs0 \f40\fs19\cf1\lang9\langfe1033\langnp9\insrsid7092439\charrsid7092439 Please note: As this software is distributed in Quebec, Canada, some of the clauses in this agreement are provided below in French. }{\rtlch\fcs1 -\af40\afs19 \ltrch\fcs0 \f40\fs19\cf1\insrsid7092439\charrsid7092439 -\par }{\rtlch\fcs1 \af40\afs19 \ltrch\fcs0 \f40\fs19\cf1\lang9\langfe1033\langnp9\insrsid7092439\charrsid7092439 Remarque : Ce logiciel \'e9tant distribu\'e9 au Qu\'e9bec, Canada, certaines des clauses dans ce contrat sont fournies ci-dessous en fran\'e7ais. } -{\rtlch\fcs1 \af40\afs19 \ltrch\fcs0 \f40\fs19\cf1\insrsid7092439\charrsid7092439 -\par }\pard \ltrpar\ql \li0\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\outlinelevel0\adjustright\rin0\lin0\itap0\pararsid7092439 {\rtlch\fcs1 \ab\af40\afs19 \ltrch\fcs0 \b\f40\fs19\cf1\kerning36\insrsid7092439\charrsid7092439 EXON\'c9 -RATION DE GARANTIE.\~}{\rtlch\fcs1 \ab\af0\afs24 \ltrch\fcs0 \b\f0\fs24\insrsid7092439\charrsid7092439 }{\rtlch\fcs1 \af40\afs19 \ltrch\fcs0 \f40\fs19\cf1\kerning36\insrsid7092439\charrsid7092439 Le logiciel vis\'e9 par une licence est offert \'ab - tel quel \'bb. Toute utilisation de ce logiciel est \'e0 votre seule risque et p\'e9ril. Microsoft n\rquote accorde aucune autre garantie expresse. Vous pouvez b\'e9n\'e9ficier de droits - additionnels en vertu du droit local sur la protection des consommateurs, que ce contrat ne peut modifier. La ou elles sont permises par le droit locale, les garanties implicites de qualit\'e9 marchande, d\rquote ad\'e9quation \'e0 - un usage particulier et d\rquote absence de contrefa\'e7on sont exclues. }{\rtlch\fcs1 \ab\af40\afs19 \ltrch\fcs0 \b\f40\fs19\cf1\kerning36\insrsid7092439\charrsid7092439 -\par LIMITATION DES DOMMAGES-INT\'c9R\'caTS ET EXCLUSION DE RESPONSABILIT\'c9 POUR LES DOMMAGES.\~ }{\rtlch\fcs1 \af40\afs19 \ltrch\fcs0 \f40\fs19\cf1\kerning36\insrsid7092439\charrsid7092439 Vous}{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 -\f0\fs24\insrsid7092439\charrsid7092439 }{\rtlch\fcs1 \af40\afs19 \ltrch\fcs0 \f40\fs19\cf1\kerning36\insrsid7092439\charrsid7092439 pouvez obtenir de Microsoft et de ses fournisseurs une indemnisation en cas de dommages directs uniquement \'e0 - hauteur de 5,00 $ US. Vous ne pouvez pr\'e9tendre \'e0 aucune indemnisation pour les autres dommages, y compris les dommages sp\'e9ciaux, indirects ou accessoires et pertes de b\'e9n\'e9fices. }{\rtlch\fcs1 \ab\af40\afs19 \ltrch\fcs0 -\b\f40\fs19\cf1\kerning36\insrsid7092439\charrsid7092439 -\par }\pard \ltrpar\ql \li0\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid7092439 {\rtlch\fcs1 \af40\afs19 \ltrch\fcs0 \f40\fs19\cf1\lang9\langfe1033\langnp9\insrsid7092439\charrsid7092439 Cette}{\rtlch\fcs1 -\af0\afs24 \ltrch\fcs0 \f0\fs24\insrsid7092439\charrsid7092439 }{\rtlch\fcs1 \af40\afs19 \ltrch\fcs0 \f40\fs19\cf1\lang9\langfe1033\langnp9\insrsid7092439\charrsid7092439 limitationconcerne:}{\rtlch\fcs1 \af0\afs24 \ltrch\fcs0 -\f0\fs24\insrsid7092439\charrsid7092439 }{\rtlch\fcs1 \af40\afs19 \ltrch\fcs0 \f40\fs19\cf1\insrsid7092439\charrsid7092439 -\par }\pard \ltrpar\ql \li720\ri0\sb120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin720\itap0\pararsid7092439 {\rtlch\fcs1 \af40\afs19 \ltrch\fcs0 \f3\fs19\cf1\lang9\langfe1033\langnp9\insrsid7092439\charrsid7092439 \'b7}{\rtlch\fcs1 -\af0\afs14 \ltrch\fcs0 \f0\fs14\cf1\lang9\langfe1033\langnp9\insrsid7092439\charrsid7092439 \~\~\~\~\~\~\~\~\~}{\rtlch\fcs1 \af40\afs19 \ltrch\fcs0 \f40\fs19\cf1\lang9\langfe1033\langnp9\insrsid7092439\charrsid7092439 tout ce qui est reli\'e9 - au logiciel, aux services ou au contenu (y compris le code) figurant sur des sites Internet tiers ou dans des programmes tiers ; et }{\rtlch\fcs1 \af40\afs19 \ltrch\fcs0 \f40\fs19\cf1\insrsid7092439\charrsid7092439 -\par }\pard \ltrpar\ql \li720\ri0\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin720\itap0\pararsid7092439 {\rtlch\fcs1 \af40\afs19 \ltrch\fcs0 \f3\fs19\cf1\lang9\langfe1033\langnp9\insrsid7092439\charrsid7092439 \'b7}{\rtlch\fcs1 -\af0\afs14 \ltrch\fcs0 \f0\fs14\cf1\lang9\langfe1033\langnp9\insrsid7092439\charrsid7092439 \~\~\~\~\~\~\~\~\~}{\rtlch\fcs1 \af40\afs19 \ltrch\fcs0 \f40\fs19\cf1\lang9\langfe1033\langnp9\insrsid7092439\charrsid7092439 les r\'e9 -clamations au titre de violation de contrat ou de garantie, ou au titre de responsabilit\'e9 stricte, de n\'e9gligence ou d\rquote une autre faute dans la limite autoris\'e9e par la loi en vigueur. }{\rtlch\fcs1 \af40\afs19 \ltrch\fcs0 -\f40\fs19\cf1\insrsid7092439\charrsid7092439 -\par }\pard \ltrpar\ql \li0\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid7092439 {\rtlch\fcs1 \af40\afs19 \ltrch\fcs0 \f40\fs19\cf1\lang9\langfe1033\langnp9\insrsid7092439\charrsid7092439 Elle s\rquote -applique \'e9galement, m\'eame si Microsoft connaissait ou devrait conna\'eetre l\rquote \'e9ventualit\'e9 d\rquote un tel dommage. Si votre pays n\rquote autorise pas l\rquote exclusion ou la limitation de responsabilit\'e9 - pour les dommages indirects, accessoires ou de quelque nature que ce soit, il se peut que la limitation ou l\rquote exclusion ci-dessus ne s\rquote appliquera pas \'e0 votre \'e9gard. }{\rtlch\fcs1 \af40\afs19 \ltrch\fcs0 -\f40\fs19\cf1\insrsid7092439\charrsid7092439 -\par }\pard \ltrpar\ql \li0\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\outlinelevel0\adjustright\rin0\lin0\itap0\pararsid7092439 {\rtlch\fcs1 \ab\af40\afs19 \ltrch\fcs0 \b\f40\fs19\cf1\kerning36\insrsid7092439\charrsid7092439 EFFET JURIDIQUE. -\~}{\rtlch\fcs1 \ab\af0\afs24 \ltrch\fcs0 \b\f0\fs24\insrsid7092439\charrsid7092439 }{\rtlch\fcs1 \af40\afs19 \ltrch\fcs0 \f40\fs19\cf1\kerning36\insrsid7092439\charrsid7092439 Le pr\'e9sent contrat d\'e9 -crit certains droits juridiques. Vous pourriez avoir d\rquote autres droits pr\'e9vus par les lois de votre pays. Le pr\'e9sent contrat ne modifie pas les droits que vous conf\'e8rent les lois de votre pays si celles-ci ne le permettent pas. }{ -\rtlch\fcs1 \ab\af40\afs19 \ltrch\fcs0 \b\f40\fs19\cf1\kerning36\insrsid7092439\charrsid7092439 -\par }\pard\plain \ltrpar\s15\ql \li0\ri0\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid7092439 \rtlch\fcs1 \af0\afs24\alang1025 \ltrch\fcs0 \fs24\lang1033\langfe1033\cgrid\langnp1033\langfenp1033 {\rtlch\fcs1 \af2\afs20 -\ltrch\fcs0 \f2\fs18\cf17\lang9\langfe1033\langnp9\insrsid7092439 -\par }{\rtlch\fcs1 \af2\afs20 \ltrch\fcs0 \f2\fs18\cf17\lang9\langfe1033\langnp9\insrsid16084641 -\par }{\rtlch\fcs1 \af2\afs20 \ltrch\fcs0 \f2\fs18\cf17\lang9\langfe1033\langnp9\insrsid7092439 -------------------------------------------------------- -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs22 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}}\pard\plain \ltrpar -\s16\ql \fi-360\li720\ri0\widctlpar\wrapdefault\aspalpha\aspnum\faauto\ls4\adjustright\rin0\lin720\itap0\pararsid10169937 \rtlch\fcs1 \af39\afs22\alang1025 \ltrch\fcs0 \f39\fs22\lang1033\langfe1033\cgrid\langnp1033\langfenp1033 {\rtlch\fcs1 \af2 -\ltrch\fcs0 \f2\fs18\insrsid7092439\charrsid10169937 NuGet.Common -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs22 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}NuGet.Configuration -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs22 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}NuGet.DependencyResolver.Core -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs22 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}NuGet.Frameworks -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs22 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}NuGet.LibraryModel -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs22 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}NuGet.Packaging -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs22 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}NuGet.Packaging.Core -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs22 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}NuGet.Packaging.Core.Types -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs22 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}NuGet.ProjectModel -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs22 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}NuGet.Protocol.Core.Types -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs22 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}NuGet.Protocol.Core.v3 -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs22 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}NuGet.Repositories -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs22 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}NuGet.RuntimeModel -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs22 \ltrch\fcs0 \f10\fs18\insrsid7092439\charrsid10169937 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}NuGet.Versioning -\par }\pard\plain \ltrpar\s15\ql \li0\ri0\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid7092439 \rtlch\fcs1 \af0\afs24\alang1025 \ltrch\fcs0 \fs24\lang1033\langfe1033\cgrid\langnp1033\langfenp1033 {\rtlch\fcs1 \af2\afs20 -\ltrch\fcs0 \f2\fs18\cf17\lang9\langfe1033\langnp9\insrsid7092439 ---------------------------------------------------------- -\par -\par }\pard\plain \ltrpar\ql \li0\ri0\widctlpar\tx916\tx1832\tx2748\tx3664\tx4580\tx5496\tx6412\tx7328\tx8244\tx9160\tx10076\tx10992\tx11908\tx12824\tx13740\tx14656\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid7092439 \rtlch\fcs1 -\af31507\afs22\alang1025 \ltrch\fcs0 \f31506\fs22\lang1033\langfe1033\cgrid\langnp1033\langfenp1033 {\rtlch\fcs1 \af2\afs18 \ltrch\fcs0 \f2\fs18\insrsid7092439\charrsid7092439 Copyright (c) .NET Foundation. All rights reserved. -\par -\par Licensed under the Apache License, Version 2.0 (the "License"); you may not use -\par these files except in compliance with the License. You may obtain a copy of the -\par License at -\par -\par http://www.apache.org/licenses/LICENSE-2.0 -\par -\par Unless required by applicable law or agreed to in writing, software distributed -\par under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -\par CONDITIONS OF ANY KIND, either express or implied. See the License for the -\par specific language governing permissions and limitations under the License. -\par }\pard\plain \ltrpar\s15\ql \li0\ri0\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid7092439 \rtlch\fcs1 \af0\afs24\alang1025 \ltrch\fcs0 \fs24\lang1033\langfe1033\cgrid\langnp1033\langfenp1033 {\rtlch\fcs1 \af2\afs18 -\ltrch\fcs0 \f2\fs18\cf17\lang9\langfe1033\langnp9\insrsid7092439\charrsid7092439 -\par }{\rtlch\fcs1 \af2\afs20 \ltrch\fcs0 \f2\fs18\cf17\lang9\langfe1033\langnp9\insrsid7092439 -\par }\pard \ltrpar\s15\ql \li0\ri0\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid6173475 {\rtlch\fcs1 \af2\afs20 \ltrch\fcs0 \f2\fs18\cf17\lang9\langfe1033\langnp9\insrsid6173475 -----------------------------------------}{ -\rtlch\fcs1 \af2\afs20 \ltrch\fcs0 \f2\fs18\cf17\lang9\langfe1033\langnp9\insrsid10363382 ----------------}{\rtlch\fcs1 \af2\afs20 \ltrch\fcs0 \f2\fs18\cf17\lang9\langfe1033\langnp9\insrsid6173475 -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs22 \ltrch\fcs0 \f10\fs18\insrsid6173475\charrsid6173475 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}}\pard\plain \ltrpar -\s16\ql \fi-360\li540\ri0\widctlpar\wrapdefault\aspalpha\aspnum\faauto\ls2\ilvl2\adjustright\rin0\lin540\itap0\pararsid10363382 \rtlch\fcs1 \af39\afs22\alang1025 \ltrch\fcs0 \f39\fs22\lang1033\langfe1033\cgrid\langnp1033\langfenp1033 {\rtlch\fcs1 \af2 -\ltrch\fcs0 \f2\fs18\insrsid6173475\charrsid6173475 Microsoft.Management.Infrastructure.dll -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs22 \ltrch\fcs0 \f10\fs18\insrsid6173475\charrsid6173475 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}Microsoft.Management.Infrastructure.Native.dll -\par {\listtext\pard\plain\ltrpar \s16 \rtlch\fcs1 \af2\afs22 \ltrch\fcs0 \f10\fs18\insrsid6173475\charrsid6173475 \loch\af10\dbch\af31506\hich\f10 \'a7\tab}Microsoft.Management.Infrastructure.Unmanaged.dll -\par }\pard\plain \ltrpar\ql \li0\ri0\sa160\sl259\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid10363382 \rtlch\fcs1 \af31507\afs22\alang1025 \ltrch\fcs0 \f31506\fs22\lang1033\langfe1033\cgrid\langnp1033\langfenp1033 -{\rtlch\fcs1 \af2 \ltrch\fcs0 \f2\fs18\insrsid10363382 ----------------------------------------------------------}{\rtlch\fcs1 \af2 \ltrch\fcs0 \f2\fs18\insrsid10363382\charrsid10363382 -\par }\pard\plain \ltrpar\s32\ql \li0\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid7813854 \rtlch\fcs1 \ab\af40\afs28\alang1025 \ltrch\fcs0 -\fs28\lang1033\langfe1033\loch\af40\hich\af40\dbch\af11\cgrid\langnp1033\langfenp1033 {\rtlch\fcs1 \af42 \ltrch\fcs0 \f42\insrsid7813854\charrsid6112664 \hich\af42\dbch\af11\loch\f42 MICROSOFT SOFTWARE LICENSE TERMS -\par }\pard\plain \ltrpar\s33\ql \li0\ri0\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid7813854 \rtlch\fcs1 \ab\af40\afs28\alang1025 \ltrch\fcs0 -\fs28\lang1033\langfe1033\loch\af40\hich\af40\dbch\af11\cgrid\langnp1033\langfenp1033 {\rtlch\fcs1 \af42 \ltrch\fcs0 \f42\fs24\insrsid7813854\charrsid6843334 \hich\af42\dbch\af11\loch\f42 MANAGEMENT.INFRASTRUCTURE.DLL }{\rtlch\fcs1 \af42 \ltrch\fcs0 -\f42\fs24\insrsid7813854 -\par }{\rtlch\fcs1 \af42 \ltrch\fcs0 \f42\fs24\insrsid7813854\charrsid6843334 \hich\af42\dbch\af11\loch\f42 MANAGEMENT.INFRASTRUCTURE.NATIVE.DLL\hich\af42\dbch\af11\loch\f42 MANAGEMENT.INFRASTRUCTURE.UNMANAGED.DLL -\par }\pard \ltrpar\s33\ql \li0\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid7813854 {\rtlch\fcs1 \af42 \ltrch\fcs0 \f42\insrsid11493340 -{\pict{\*\picprop\shplid1025{\sp{\sn shapeType}{\sv 1}}{\sp{\sn fFlipH}{\sv 0}}{\sp{\sn fFlipV}{\sv 0}}{\sp{\sn fillColor}{\sv 10526880}}{\sp{\sn fFilled}{\sv 1}}{\sp{\sn fLine}{\sv 0}}{\sp{\sn alignHR}{\sv 1}}{\sp{\sn dxHeightHR}{\sv 30}} -{\sp{\sn fStandardHR}{\sv 1}}{\sp{\sn fHorizRule}{\sv 1}}{\sp{\sn fLayoutInCell}{\sv 1}}}\picscalex1\picscaley1\piccropl0\piccropr0\piccropt0\piccropb0\picw7620\pich7620\picwgoal4320\pichgoal4320\wmetafile8}}{\rtlch\fcs1 \af42 \ltrch\fcs0 -\f42\fs24\insrsid7813854\charrsid6112664 -\par }\sectd \ltrsect\linex0\endnhere\sectlinegrid360\sectdefaultcl\sftnbj {\*\pnseclvl1\pnucrm\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl2\pnucltr\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl3\pndec\pnstart1\pnindent720\pnhang {\pntxta .}} -{\*\pnseclvl4\pnlcltr\pnstart1\pnindent720\pnhang {\pntxta )}}{\*\pnseclvl5\pndec\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl6\pnlcltr\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl7\pnlcrm\pnstart1\pnindent720\pnhang -{\pntxtb (}{\pntxta )}}{\*\pnseclvl8\pnlcltr\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl9\pnlcrm\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}\pard\plain \ltrpar -\s34\ql \li0\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid7813854 \rtlch\fcs1 \ab\af40\afs19\alang1025 \ltrch\fcs0 \fs19\lang1033\langfe1033\loch\af40\hich\af40\dbch\af11\cgrid\langnp1033\langfenp1033 { -\rtlch\fcs1 \af42 \ltrch\fcs0 \f42\lang9\langfe1033\langnp9\insrsid7813854\charrsid6112664 \hich\af42\dbch\af11\loch\f42 -These license terms are an agreement between you and Microsoft Corporation (or one of its affiliates). They apply to the software named above and any Microsoft services or software updates (except to the extent su\hich\af42\dbch\af11\loch\f42 -ch services or updates are accompanied by new or }{\rtlch\fcs1 \af42 \ltrch\fcs0 \f42\insrsid7813854\charrsid6112664 \hich\af42\dbch\af11\loch\f42 additional terms, in which case those different terms {\*\bkmkstart OLE_LINK84}{\*\bkmkstart OLE_LINK85} -apply prospectively {\*\bkmkend OLE_LINK84}{\*\bkmkend OLE_LINK85}and do not alter your or Microsoft\hich\f42 \rquote \loch\f42 s rights relating to pre-updated software or services}{\rtlch\fcs1 \af42 \ltrch\fcs0 -\f42\lang9\langfe1033\langnp9\insrsid7813854\charrsid6112664 \hich\af42\dbch\af11\loch\f42 ). IF YOU COMPLY WITH THESE LICENSE TERMS, YO\hich\af42\dbch\af11\loch\f42 U HAVE THE RIGHTS BELOW. }{\rtlch\fcs1 \af42 \ltrch\fcs0 -\f42\insrsid7813854\charrsid6112664 \hich\af42\dbch\af11\loch\f42 BY USING THE SOFTWARE, YOU ACCEPT THESE TERMS.}{\rtlch\fcs1 \af42 \ltrch\fcs0 \f42\lang9\langfe1033\langnp9\insrsid7813854\charrsid6112664 -\par {\listtext\pard\plain\ltrpar \s1 \rtlch\fcs1 \ab\af40\afs20 \ltrch\fcs0 \b\fs20\loch\af40\hich\af40\dbch\af11\insrsid7813854 \hich\af40\dbch\af11\loch\f40 1.\tab}}\pard\plain \ltrpar\s1\ql \fi-357\li357\ri0\sb120\sa120\widctlpar -\jclisttab\tx360\wrapdefault\aspalpha\aspnum\faauto\ls12\outlinelevel0\adjustright\rin0\lin357\itap0\pararsid7813854 \rtlch\fcs1 \ab\af40\afs19\alang1025 \ltrch\fcs0 \fs19\lang1033\langfe1033\loch\af40\hich\af40\dbch\af11\cgrid\langnp1033\langfenp1033 { -\rtlch\fcs1 \af42 \ltrch\fcs0 \b\f42\insrsid7813854 \hich\af42\dbch\af11\loch\f42 INSTALLATION AND USE RIGHTS}{\rtlch\fcs1 \af42 \ltrch\fcs0 \b\f42\insrsid7813854\charrsid6112664 -\par {\*\bkmkstart OLE_LINK124}{\*\bkmkstart OLE_LINK125}{\listtext\pard\plain\ltrpar \s2 \rtlch\fcs1 \ab\af42\afs19 \ltrch\fcs0 \b\fs19\loch\af42\hich\af42\dbch\af11\insrsid7813854\charrsid6112664 \hich\af42\dbch\af11\loch\f42 a)\tab}}\pard\plain \ltrpar -\s2\ql \fi-360\li717\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\ls13\outlinelevel1\adjustright\rin0\lin717\itap0\pararsid7813854 \rtlch\fcs1 \ab\af40\afs19\alang1025 \ltrch\fcs0 -\fs19\lang1033\langfe1033\loch\af40\hich\af40\dbch\af11\cgrid\langnp1033\langfenp1033 {\rtlch\fcs1 \af42 \ltrch\fcs0 \b\f42\insrsid7813854\charrsid6112664 \hich\af42\dbch\af11\loch\f42 General.}{\rtlch\fcs1 \af42 \ltrch\fcs0 -\f42\insrsid7813854\charrsid6112664 \hich\af42\dbch\af11\loch\f42 You may install and use any number of copies of the software on your devices. -\par {\*\bkmkend OLE_LINK124}{\*\bkmkend OLE_LINK125}{\listtext\pard\plain\ltrpar \s2 \rtlch\fcs1 \ab\af42\afs19 \ltrch\fcs0 \b\fs19\loch\af42\hich\af42\dbch\af11\insrsid7813854\charrsid6112664 \hich\af42\dbch\af11\loch\f42 b)\tab}}{\rtlch\fcs1 \af42 -\ltrch\fcs0 \b\f42\insrsid7813854\charrsid6112664 \hich\af42\dbch\af11\loch\f42 Included Microsoft Applications.}{\rtlch\fcs1 \af42 \ltrch\fcs0 \f42\insrsid7813854\charrsid6112664 \hich\af42\dbch\af11\loch\f42 - The software may include other Microsoft applications. These license terms apply to those included applications, if any, {\*\bkmkstart OLE_LINK80}{\*\bkmkstart OLE_LINK81}unless other license terms are provided with the other Microsoft applications -{\*\bkmkend OLE_LINK80}{\*\bkmkend OLE_LINK81}.}{\rtlch\fcs1 \af42 \ltrch\fcs0 \b\f42\insrsid7813854\charrsid6112664 -\par {\listtext\pard\plain\ltrpar \s2 \rtlch\fcs1 \ab\af42\afs19 \ltrch\fcs0 \b\fs19\loch\af42\hich\af42\dbch\af11\insrsid7813854\charrsid6112664 \hich\af42\dbch\af11\loch\f42 c)\tab}\hich\af42\dbch\af11\loch\f42 Third Party Software.}{\rtlch\fcs1 \af42 -\ltrch\fcs0 \f42\insrsid7813854\charrsid6112664 \hich\af42\dbch\af11\loch\f42 The software may include third pa\hich\af42\dbch\af11\loch\f42 -rty applications that are licensed to you under this agreement or under their own terms. License terms, notices, and acknowledgements, if any, for the third party applications may be accessible online at }{\field\fldedit{\*\fldinst {\rtlch\fcs1 \af40 -\ltrch\fcs0 \insrsid11493340 \hich\af40\dbch\af11\loch\f40 HYPERLINK "http://aka.ms/thirdpartynotices" }}{\fldrslt {\rtlch\fcs1 \af42 \ltrch\fcs0 \cs19\f42\ul\cf1\insrsid7813854\charrsid6112664 \hich\af42\dbch\af11\loch\f42 http -\hich\af42\dbch\af11\loch\f42 ://aka.ms/thirdpartynotices}}}\sectd \ltrsect\linex0\endnhere\sectlinegrid360\sectdefaultcl\sftnbj {\rtlch\fcs1 \af42 \ltrch\fcs0 \f42\insrsid7813854\charrsid6112664 \hich\af42\dbch\af11\loch\f42 - or in an accompanying notices file. Even if such applications are governed by other agreements, the disclaimer, limitations on, and exclusions of damages below also apply to the extent allowed by applicable law.}{\rtlch\fcs1 \af42 \ltrch\fcs0 -\b\f42\insrsid7813854\charrsid6112664 -\par {\listtext\pard\plain\ltrpar \s1 \rtlch\fcs1 \ab\af40\afs20 \ltrch\fcs0 \b\fs20\loch\af40\hich\af40\dbch\af11\insrsid7813854 \hich\af40\dbch\af11\loch\f40 2.\tab}}\pard\plain \ltrpar\s1\ql \fi-357\li357\ri0\sb120\sa120\widctlpar -\jclisttab\tx360\wrapdefault\aspalpha\aspnum\faauto\ls12\outlinelevel0\adjustright\rin0\lin357\itap0\pararsid7813854 \rtlch\fcs1 \ab\af40\afs19\alang1025 \ltrch\fcs0 \fs19\lang1033\langfe1033\loch\af40\hich\af40\dbch\af11\cgrid\langnp1033\langfenp1033 { -\rtlch\fcs1 \af42 \ltrch\fcs0 \b\f42\insrsid7813854 \hich\af42\dbch\af11\loch\f42 TIME-SENSITIVE S\hich\af42\dbch\af11\loch\f42 OFTWARE}{\rtlch\fcs1 \af42 \ltrch\fcs0 \b\f42\insrsid7813854\charrsid6112664 -\par {\listtext\pard\plain\ltrpar \s2 \rtlch\fcs1 \ab\af42\afs19 \ltrch\fcs0 \b\fs19\loch\af42\hich\af42\dbch\af11\insrsid7813854\charrsid6112664 \hich\af42\dbch\af11\loch\f42 a)\tab}}\pard\plain \ltrpar -\s2\ql \fi-360\li717\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\ls14\outlinelevel1\adjustright\rin0\lin717\itap0\pararsid7813854 \rtlch\fcs1 \ab\af40\afs19\alang1025 \ltrch\fcs0 -\fs19\lang1033\langfe1033\loch\af40\hich\af40\dbch\af11\cgrid\langnp1033\langfenp1033 {\rtlch\fcs1 \af42 \ltrch\fcs0 \b\f42\insrsid7813854\charrsid6112664 \hich\af42\dbch\af11\loch\f42 Period.}{\rtlch\fcs1 \af42 \ltrch\fcs0 -\f42\insrsid7813854\charrsid6112664 \hich\af42\dbch\af11\loch\f42 The software is time-sensitive and may stop running on a date that is defined in the software.}{\rtlch\fcs1 \af42 \ltrch\fcs0 \b\f42\insrsid7813854\charrsid6112664 -\par {\listtext\pard\plain\ltrpar \s2 \rtlch\fcs1 \ab\af42\afs19 \ltrch\fcs0 \b\fs19\loch\af42\hich\af42\dbch\af11\insrsid7813854\charrsid6112664 \hich\af42\dbch\af11\loch\f42 b)\tab}\hich\af42\dbch\af11\loch\f42 Notice.}{\rtlch\fcs1 \af42 \ltrch\fcs0 -\f42\insrsid7813854\charrsid6112664 \hich\af42\dbch\af11\loch\f42 You may receive periodic reminder notices of this date through the software.}{\rtlch\fcs1 \af42 \ltrch\fcs0 \b\f42\insrsid7813854\charrsid6112664 -\par {\listtext\pard\plain\ltrpar \s2 \rtlch\fcs1 \ab\af42\afs19 \ltrch\fcs0 \b\fs19\loch\af42\hich\af42\dbch\af11\insrsid7813854\charrsid6112664 \hich\af42\dbch\af11\loch\f42 c)\tab}\hich\af42\dbch\af11\loch\f42 Access to data.}{\rtlch\fcs1 \af42 \ltrch\fcs0 -\f42\insrsid7813854\charrsid6112664 \hich\af42\dbch\af11\loch\f42 You may not be able to access data used in the software when it stops running.}{\rtlch\fcs1 \af42 \ltrch\fcs0 \b\f42\insrsid7813854\charrsid6112664 -\par {\listtext\pard\plain\ltrpar \s1 \rtlch\fcs1 \ab\af40\afs20 \ltrch\fcs0 \b\fs20\loch\af40\hich\af40\dbch\af11\insrsid7813854\charrsid6112664 \hich\af40\dbch\af11\loch\f40 3.\tab}}\pard\plain \ltrpar\s1\ql \fi-357\li357\ri0\sb120\sa120\widctlpar -\jclisttab\tx360\wrapdefault\aspalpha\aspnum\faauto\ls12\outlinelevel0\adjustright\rin0\lin357\itap0\pararsid7813854 \rtlch\fcs1 \ab\af40\afs19\alang1025 \ltrch\fcs0 \fs19\lang1033\langfe1033\loch\af40\hich\af40\dbch\af11\cgrid\langnp1033\langfenp1033 { -\rtlch\fcs1 \af42 \ltrch\fcs0 \b\f42\insrsid7813854\charrsid6112664 \hich\af42\dbch\af11\loch\f42 PRE-RELEASE SOFTWARE.}{\rtlch\fcs1 \af42 \ltrch\fcs0 \f42\insrsid7813854\charrsid6112664 \hich\af42\dbch\af11\loch\f42 - The software is a pre-release version. It may not operate correctly. It may be different from the commercially released version.}{\rtlch\fcs1 \af42 \ltrch\fcs0 \b\f42\insrsid7813854\charrsid6112664 -\par {\listtext\pard\plain\ltrpar \s1 \rtlch\fcs1 \ab\af40\afs20 \ltrch\fcs0 \b\fs20\loch\af40\hich\af40\dbch\af11\insrsid7813854\charrsid6112664 \hich\af40\dbch\af11\loch\f40 4.\tab}\hich\af42\dbch\af11\loch\f42 FEEDBACK.}{\rtlch\fcs1 \af42 \ltrch\fcs0 -\f42\insrsid7813854\charrsid6112664 \hich\af42\dbch\af11\loch\f42 If you give fee\hich\af42\dbch\af11\loch\f42 -dback about the software to Microsoft, you give to Microsoft, without charge, the right to use, share and commercialize your feedback in any way and for any purpose. You will not give feedback that is subject to a license that requires Microsoft to licens -\hich\af42\dbch\af11\loch\f42 e\hich\af42\dbch\af11\loch\f42 its software or documentation to third parties because Microsoft includes your feedback in them. These rights survive this agreement.}{\rtlch\fcs1 \af42 \ltrch\fcs0 -\b\f42\insrsid7813854\charrsid6112664 -\par {\listtext\pard\plain\ltrpar \s1 \rtlch\fcs1 \ab\af40\afs20 \ltrch\fcs0 \b\fs20\loch\af40\hich\af40\dbch\af11\insrsid7813854\charrsid6112664 \hich\af40\dbch\af11\loch\f40 5.\tab}\hich\af42\dbch\af11\loch\f42 DATA COLLECTION.}{\rtlch\fcs1 \af42 -\ltrch\fcs0 \f42\insrsid7813854\charrsid6112664 \hich\af42\dbch\af11\loch\f42 The software may collect information about you and your use of the software and send that to Microsoft. \hich\af42\dbch\af11\loch\f42 -Microsoft may use this information to provide services and improve Microsoft\hich\f42 \rquote \loch\f42 -s products and services. Your opt-out rights, if any, are described in the product documentation. Some features in the software may enable collection of data from users of your a\hich\af42\dbch\af11\loch\f42 p\hich\af42\dbch\af11\loch\f42 -plications that access or use the software. If you use these features to enable data collection in your applications, you must comply with applicable law, including getting any required user consent, and maintain a prominent privacy policy that accurately -\hich\af42\dbch\af11\loch\f42 \hich\af42\dbch\af11\loch\f42 informs users about how you use, collect, and share their data. You can learn more about Microsoft\hich\f42 \rquote \loch\f42 -s data collection and use in the product documentation and the Microsoft Privacy Statement}{\rtlch\fcs1 \af42 \ltrch\fcs0 \f42\insrsid7813854\delrsid16459130\charrsid6112664 \hich\af42\dbch\af11\loch\f42 }{\rtlch\fcs1 \af42 \ltrch\fcs0 -\f42\insrsid7813854\charrsid6112664 \hich\af42\dbch\af11\loch\f42 at }{\field\fldedit{\*\fldinst {\rtlch\fcs1 \af40 \ltrch\fcs0 \insrsid11493340 \hich\af40\dbch\af11\loch\f40 HYPERLINK "https://go.microsoft.com/fwlink/?LinkId=521839" }}{\fldrslt { -\rtlch\fcs1 \af42 \ltrch\fcs0 \cs19\f42\ul\cf1\insrsid7813854\charrsid6112664 \hich\af42\dbch\af11\loch\f42 https://go.microsoft.com/fwlink/?LinkId=521839}}}\sectd \ltrsect\linex0\endnhere\sectlinegrid360\sectdefaultcl\sftnbj {\rtlch\fcs1 \af42 -\ltrch\fcs0 \f42\insrsid7813854\charrsid6112664 \hich\af42\dbch\af11\loch\f42 . You agree to comply with all applicable provisions of the Microsoft Privacy Statement.}{\rtlch\fcs1 \af42 \ltrch\fcs0 \b\f42\insrsid7813854\charrsid6112664 -\par {\listtext\pard\plain\ltrpar \s1 \rtlch\fcs1 \ab\af40\afs20 \ltrch\fcs0 \b\fs20\loch\af40\hich\af40\dbch\af11\insrsid7813854\charrsid6112664 \hich\af40\dbch\af11\loch\f40 6.\tab}\hich\af42\dbch\af11\loch\f42 FONTS.}{\rtlch\fcs1 \af42 \ltrch\fcs0 -\f42\insrsid7813854\charrsid6112664 \hich\af42\dbch\af11\loch\f42 While the software is running, you may use its fonts to display and print content. You may only (i) embed fonts in\hich\af42\dbch\af11\loch\f42 - content as permitted by the embedding restrictions in the fonts; and (ii) temporarily download them to a printer or other output device to help print content.}{\rtlch\fcs1 \af42 \ltrch\fcs0 \b\f42\insrsid7813854\charrsid6112664 -\par {\listtext\pard\plain\ltrpar \s1 \rtlch\fcs1 \ab\af40\afs20 \ltrch\fcs0 \b\fs20\loch\af40\hich\af40\dbch\af11\insrsid7813854\charrsid6112664 \hich\af40\dbch\af11\loch\f40 7.\tab}\hich\af42\dbch\af11\loch\f42 SCOPE OF LICENSE.}{\rtlch\fcs1 \af42 -\ltrch\fcs0 \f42\insrsid7813854\charrsid6112664 \hich\af42\dbch\af11\loch\f42 The software is licensed, not sold. Microsoft reserves all other rights. Unles\hich\af42\dbch\af11\loch\f42 -s applicable law gives you more rights despite this limitation, you will not (and have no right to):}{\rtlch\fcs1 \af42 \ltrch\fcs0 \b\f42\insrsid7813854\charrsid6112664 -\par {\listtext\pard\plain\ltrpar \s2 \rtlch\fcs1 \ab\af42\afs19 \ltrch\fcs0 \b\fs19\loch\af42\hich\af42\dbch\af11\insrsid7813854\charrsid6112664 \hich\af42\dbch\af11\loch\f42 a)\tab}}\pard\plain \ltrpar -\s2\ql \fi-360\li717\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\ls15\outlinelevel1\adjustright\rin0\lin717\itap0\pararsid7813854 \rtlch\fcs1 \ab\af40\afs19\alang1025 \ltrch\fcs0 -\fs19\lang1033\langfe1033\loch\af40\hich\af40\dbch\af11\cgrid\langnp1033\langfenp1033 {\rtlch\fcs1 \af42 \ltrch\fcs0 \f42\insrsid7813854\charrsid6112664 \hich\af42\dbch\af11\loch\f42 -work around any technical limitations in the software that only allow you to use it in certain ways; -\par {\listtext\pard\plain\ltrpar \s2 \rtlch\fcs1 \ab\af42\afs19 \ltrch\fcs0 \b\fs19\loch\af42\hich\af42\dbch\af11\insrsid7813854\charrsid6112664 \hich\af42\dbch\af11\loch\f42 b)\tab}\hich\af42\dbch\af11\loch\f42 -reverse engineer, decompile or disassemble the softwar\hich\af42\dbch\af11\loch\f42 e; -\par {\listtext\pard\plain\ltrpar \s2 \rtlch\fcs1 \ab\af42\afs19 \ltrch\fcs0 \b\fs19\loch\af42\hich\af42\dbch\af11\insrsid7813854\charrsid6112664 \hich\af42\dbch\af11\loch\f42 c)\tab}\hich\af42\dbch\af11\loch\f42 -remove, minimize, block, or modify any notices of Microsoft or its suppliers in the software; -\par {\listtext\pard\plain\ltrpar \s2 \rtlch\fcs1 \ab\af42\afs19 \ltrch\fcs0 \b\fs19\loch\af42\hich\af42\dbch\af11\insrsid7813854\charrsid6112664 \hich\af42\dbch\af11\loch\f42 d)\tab}\hich\af42\dbch\af11\loch\f42 -use the software in any way that is against the law or to create or propagate malware; or -\par {\listtext\pard\plain\ltrpar \s2 \rtlch\fcs1 \ab\af42\afs19 \ltrch\fcs0 \b\fs19\loch\af42\hich\af42\dbch\af11\insrsid7813854\charrsid6112664 \hich\af42\dbch\af11\loch\f42 e)\tab}\hich\af42\dbch\af11\loch\f42 -share, publish, distribute, or lend the software, provide the softwar\hich\af42\dbch\af11\loch\f42 e as a stand-alone hosted solution for others to use, or transfer the software or this agreement to any third party. -\par {\listtext\pard\plain\ltrpar \s1 \rtlch\fcs1 \ab\af40\afs20 \ltrch\fcs0 \b\fs20\loch\af40\hich\af40\dbch\af11\insrsid7813854\charrsid6112664 \hich\af40\dbch\af11\loch\f40 8.\tab}}\pard\plain \ltrpar\s1\ql \fi-357\li357\ri0\sb120\sa120\widctlpar -\jclisttab\tx360\wrapdefault\aspalpha\aspnum\faauto\ls12\outlinelevel0\adjustright\rin0\lin357\itap0\pararsid7813854 \rtlch\fcs1 \ab\af40\afs19\alang1025 \ltrch\fcs0 \fs19\lang1033\langfe1033\loch\af40\hich\af40\dbch\af11\cgrid\langnp1033\langfenp1033 { -\rtlch\fcs1 \af42 \ltrch\fcs0 \b\f42\insrsid7813854\charrsid6112664 \hich\af42\dbch\af11\loch\f42 EXPORT RESTRICTIONS.}{\rtlch\fcs1 \af42 \ltrch\fcs0 \f42\insrsid7813854\charrsid6112664 \hich\af42\dbch\af11\loch\f42 - You must comply with all domestic and international export laws and regulations that apply to the software, which incl\hich\af42\dbch\af11\loch\f42 -ude restrictions on destinations, end users, and end use. For further information on export restrictions, visit }{\field\fldedit{\*\fldinst {\rtlch\fcs1 \af40 \ltrch\fcs0 \insrsid11493340 \hich\af40\dbch\af11\loch\f40 HYPERLINK "http://aka.ms/exporting" -}}{\fldrslt {\rtlch\fcs1 \af42 \ltrch\fcs0 \cs19\f42\ul\cf1\insrsid7813854\charrsid6112664 \hich\af42\dbch\af11\loch\f42 http://aka.ms/exporting}}}\sectd \ltrsect\linex0\endnhere\sectlinegrid360\sectdefaultcl\sftnbj {\rtlch\fcs1 \af42 \ltrch\fcs0 -\f42\insrsid7813854\charrsid6112664 .}{\rtlch\fcs1 \af42 \ltrch\fcs0 \b\f42\insrsid7813854\charrsid6112664 -\par {\listtext\pard\plain\ltrpar \s1 \rtlch\fcs1 \ab\af40\afs20 \ltrch\fcs0 \b\fs20\loch\af40\hich\af40\dbch\af11\insrsid7813854\charrsid6112664 \hich\af40\dbch\af11\loch\f40 9.\tab}\hich\af42\dbch\af11\loch\f42 SUPPORT SERVICES.}{\rtlch\fcs1 \af42 -\ltrch\fcs0 \f42\insrsid7813854\charrsid6112664 \hich\af42\dbch\af11\loch\f42 Microsoft is not obligated under this agreement to provide an\hich\af42\dbch\af11\loch\f42 \hich\f42 y support services for the software. Any support provided is \'93\loch\f42 -\hich\f42 as is\'94\loch\f42 \hich\f42 , \'93\loch\f42 \hich\f42 with all faults\'94\loch\f42 , and without warranty of any kind.}{\rtlch\fcs1 \af42 \ltrch\fcs0 \b\f42\insrsid7813854\charrsid6112664 -\par {\listtext\pard\plain\ltrpar \s1 \rtlch\fcs1 \ab\af40\afs20 \ltrch\fcs0 \b\fs20\loch\af40\hich\af40\dbch\af11\insrsid7813854\charrsid6112664 \hich\af40\dbch\af11\loch\f40 10.\tab}\hich\af42\dbch\af11\loch\f42 UPDATES.}{\rtlch\fcs1 \af42 \ltrch\fcs0 -\f42\insrsid7813854\charrsid6112664 \hich\af42\dbch\af11\loch\f42 The software may periodically check for updates, and download and install them for you. You may obtain updates only from Mic\hich\af42\dbch\af11\loch\f42 -rosoft or authorized sources. Microsoft may need to update your system to provide you with updates. You agree to receive these automatic updates without any additional notice. Updates may not include or support all existing software features, services, or -\hich\af42\dbch\af11\loch\f42 \hich\af42\dbch\af11\loch\f42 peripheral devices.}{\rtlch\fcs1 \af42 \ltrch\fcs0 \b\f42\insrsid7813854\charrsid6112664 -\par {\listtext\pard\plain\ltrpar \s1 \rtlch\fcs1 \ab\af40\afs20 \ltrch\fcs0 \b\fs20\loch\af40\hich\af40\dbch\af11\insrsid7813854\charrsid6112664 \hich\af40\dbch\af11\loch\f40 11.\tab}\hich\af42\dbch\af11\loch\f42 ENTIRE AGREEMENT.}{\rtlch\fcs1 \af42 -\ltrch\fcs0 \f42\insrsid7813854\charrsid6112664 \hich\af42\dbch\af11\loch\f42 This agreement, and any other terms Microsoft may provide for supplements, updates, or third-party applications, is the entire agreement for the software. -\par {\*\bkmkstart OLE_LINK170}{\*\bkmkstart OLE_LINK171}{\listtext\pard\plain\ltrpar \s1 \rtlch\fcs1 \ab\af40\afs20 \ltrch\fcs0 \b\fs20\loch\af40\hich\af40\dbch\af11\insrsid7813854\charrsid6112664 \hich\af40\dbch\af11\loch\f40 12.\tab}}{\rtlch\fcs1 \af42 -\ltrch\fcs0 \b\f42\insrsid7813854\charrsid6112664 \hich\af42\dbch\af11\loch\f42 APPLICABLE LAW AND PLACE TO RESOLVE DISPUTES.}{\rtlch\fcs1 \af42 \ltrch\fcs0 \f42\insrsid7813854\charrsid6112664 \hich\af42\dbch\af11\loch\f42 - If you acquired the software in the United States or Canada, the laws of the state or province where you live (or, if a business, where your principal place of business is located) govern the interpretation of this agreement, claims for its breach, and a -\hich\af42\dbch\af11\loch\f42 l\hich\af42\dbch\af11\loch\f42 -l other claims (including consumer protection, unfair competition, and tort claims), regardless of conflict of laws principles. If you acquired the software in any other country, its laws apply. If U.S. federal jurisdiction exists, you and Microsoft conse -\hich\af42\dbch\af11\loch\f42 n\hich\af42\dbch\af11\loch\f42 -t to exclusive jurisdiction and venue in the federal court in King County, Washington for all disputes heard in court. If not, you and Microsoft consent to exclusive jurisdiction and venue in the Superior Court of King County, Washington for all disputes -\hich\af42\dbch\af11\loch\f42 h\hich\af42\dbch\af11\loch\f42 eard in court.}{\rtlch\fcs1 \af42 \ltrch\fcs0 \b\f42\insrsid7813854\charrsid6112664 -\par {\*\bkmkend OLE_LINK170}{\*\bkmkend OLE_LINK171}{\listtext\pard\plain\ltrpar \s1 \rtlch\fcs1 \ab\af40\afs20 \ltrch\fcs0 \b\fs20\loch\af40\hich\af40\dbch\af11\insrsid7813854\charrsid6112664 \hich\af40\dbch\af11\loch\f40 13.\tab} -\hich\af42\dbch\af11\loch\f42 CONSUMER RIGHTS; REGIONAL VARIATIONS.}{\rtlch\fcs1 \af42 \ltrch\fcs0 \f42\insrsid7813854\charrsid6112664 \hich\af42\dbch\af11\loch\f42 - This agreement describes certain legal rights. You may have other rights, including consumer rights, under the laws of your state, province, or country. Separate and apart from your relationship with Mic\hich\af42\dbch\af11\loch\f42 -rosoft, you may also have rights with respect to the party from which you acquired the software. This agreement does not change those other rights if the laws of your state, province, or country do not permit it to do so. For example, if you acquired the -\hich\af42\dbch\af11\loch\f42 s\hich\af42\dbch\af11\loch\f42 oftware in one of the below regions, or mandatory country law applies, then the following provisions apply to you:}{\rtlch\fcs1 \af42 \ltrch\fcs0 \b\f42\insrsid7813854\charrsid6112664 -\par {\listtext\pard\plain\ltrpar \s2 \rtlch\fcs1 \ab\af42\afs19 \ltrch\fcs0 \b\fs19\loch\af42\hich\af42\dbch\af11\insrsid7813854\charrsid6112664 \hich\af42\dbch\af11\loch\f42 a)\tab}}\pard\plain \ltrpar -\s2\ql \fi-360\li717\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\ls16\outlinelevel1\adjustright\rin0\lin717\itap0\pararsid7813854 \rtlch\fcs1 \ab\af40\afs19\alang1025 \ltrch\fcs0 -\fs19\lang1033\langfe1033\loch\af40\hich\af40\dbch\af11\cgrid\langnp1033\langfenp1033 {\rtlch\fcs1 \af42 \ltrch\fcs0 \b\f42\insrsid7813854\charrsid6112664 \hich\af42\dbch\af11\loch\f42 Australia.}{\rtlch\fcs1 \af42 \ltrch\fcs0 -\f42\insrsid7813854\charrsid6112664 \hich\af42\dbch\af11\loch\f42 You have statutory guarantees under the Australian Consumer Law and nothing in this agreement is intended to affect those rights. -\par {\listtext\pard\plain\ltrpar \s2 \rtlch\fcs1 \ab\af42\afs19 \ltrch\fcs0 \b\fs19\loch\af42\hich\af42\dbch\af11\insrsid7813854\charrsid6112664 \hich\af42\dbch\af11\loch\f42 b)\tab}}{\rtlch\fcs1 \af42 \ltrch\fcs0 \b\f42\insrsid7813854\charrsid6112664 -\hich\af42\dbch\af11\loch\f42 Canada.}{\rtlch\fcs1 \af42 \ltrch\fcs0 \f42\insrsid7813854\charrsid6112664 \hich\af42\dbch\af11\loch\f42 - If you acquired this software in Canada, you may stop receiving updates by turning off the automatic update feature, disconnecting your device from the Internet (if and when you re-connect to the Internet, however, the software will resume checking -\hich\af42\dbch\af11\loch\f42 for and installing updates), or uninstalling the software. The product documentation, if any, may also specify how to turn off updates for your specific device or software. -\par {\listtext\pard\plain\ltrpar \s2 \rtlch\fcs1 \ab\af42\afs19 \ltrch\fcs0 \b\fs19\loch\af42\hich\af42\dbch\af11\insrsid7813854\charrsid6112664 \hich\af42\dbch\af11\loch\f42 c)\tab}}{\rtlch\fcs1 \af42 \ltrch\fcs0 \b\f42\insrsid7813854\charrsid6112664 -\hich\af42\dbch\af11\loch\f42 Germany and Austria. -\par }\pard\plain \ltrpar\ql \fi-360\li1080\ri0\sa160\sl259\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin1080\itap0\pararsid7813854 \rtlch\fcs1 \af31507\afs22\alang1025 \ltrch\fcs0 -\f31506\fs22\lang1033\langfe1033\cgrid\langnp1033\langfenp1033 {\rtlch\fcs1 \af42 \ltrch\fcs0 \f42\insrsid7813854\charrsid6112664 i.\tab Warranty. The properly licensed software will perform subs -tantially as described in any Microsoft materials that accompany the software. However, Microsoft gives no contractual guarantee in relation to the licensed software. -\par ii.\tab Limitation of Liability. In case of intentional conduct, gross negligence, claims based on the Product Liability Act, as well as, in case of death or personal or physical injury, Microsoft is liable according to the statutory law. -\par }\pard\plain \ltrpar\s1\ql \li717\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\outlinelevel0\adjustright\rin0\lin717\itap0\pararsid7813854 \rtlch\fcs1 \ab\af40\afs19\alang1025 \ltrch\fcs0 -\fs19\lang1033\langfe1033\loch\af40\hich\af40\dbch\af11\cgrid\langnp1033\langfenp1033 {\rtlch\fcs1 \af42 \ltrch\fcs0 \f42\insrsid7813854\charrsid6112664 \hich\af42\dbch\af11\loch\f42 -Subject to the foregoing clause ii., Microsoft will only be liable for slight negligence if Microsoft is in br\hich\af42\dbch\af11\loch\f42 -each of such material contractual obligations, the fulfillment of which facilitate the due performance of this agreement, the breach of which would endanger the purpose of this agreement and the compliance with which a party may constantly trust in (so-ca -\hich\af42\dbch\af11\loch\f42 l\hich\af42\dbch\af11\loch\f42 led "cardinal obligations"). In other cases of slight negligence, Microsoft will not be liable for slight negligence. -\par {\listtext\pard\plain\ltrpar \s1 \rtlch\fcs1 \ab\af40\afs20 \ltrch\fcs0 \b\fs20\loch\af40\hich\af40\dbch\af11\insrsid7813854\charrsid6112664 \hich\af40\dbch\af11\loch\f40 14.\tab}}\pard \ltrpar\s1\ql \fi-357\li357\ri0\sb120\sa120\widctlpar -\jclisttab\tx360\wrapdefault\aspalpha\aspnum\faauto\ls12\outlinelevel0\adjustright\rin0\lin357\itap0\pararsid7813854 {\rtlch\fcs1 \af42 \ltrch\fcs0 \b\f42\insrsid7813854\charrsid6112664 \hich\af42\dbch\af11\loch\f42 \hich\f42 -DISCLAIMER OF WARRANTY. THE SOFTWARE IS LICENSED \'93\loch\f42 \hich\f42 AS IS.\'94\loch\f42 YOU BEAR THE RISK OF USING IT. MICROSOFT GIVES NO EXPRESS WARRANTIES, \hich\af42\dbch\af11\loch\f42 -GUARANTEES, OR CONDITIONS. TO THE EXTENT PERMITTED UNDER APPLICABLE LAWS, MICROSOFT EXCLUDES ALL IMPLIED WARRANTIES, INCLUDING MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. -\par {\listtext\pard\plain\ltrpar \s1 \rtlch\fcs1 \ab\af40\afs20 \ltrch\fcs0 \b\fs20\loch\af40\hich\af40\dbch\af11\insrsid7813854\charrsid6112664 \hich\af40\dbch\af11\loch\f40 15.\tab}\hich\af42\dbch\af11\loch\f42 -LIMITATION ON AND EXCLUSION OF DAMAGES. IF YOU HAVE ANY \hich\af42\dbch\af11\loch\f42 -BASIS FOR RECOVERING DAMAGES DESPITE THE PRECEDING DISCLAIMER OF WARRANTY, YOU CAN RECOVER FROM MICROSOFT AND ITS SUPPLIERS ONLY DIRECT DAMAGES UP TO U.S. $5.00. YOU CANNOT RECOVER ANY OTHER DAMAGES, INCLUDING CONSEQUENTIAL, LOST PROFITS, SPECIAL, INDIREC -\hich\af42\dbch\af11\loch\f42 T\hich\af42\dbch\af11\loch\f42 OR INCIDENTAL DAMAGES. -\par }\pard\plain \ltrpar\s31\ql \li357\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin357\itap0\pararsid7813854 \rtlch\fcs1 \af40\afs19\alang1025 \ltrch\fcs0 -\b\fs19\lang1033\langfe1033\loch\af40\hich\af40\dbch\af11\cgrid\langnp1033\langfenp1033 {\rtlch\fcs1 \af42 \ltrch\fcs0 \f42\insrsid7813854\charrsid6112664 \hich\af42\dbch\af11\loch\f42 -This limitation applies to (a) anything related to the software, services, content (including code) on third party Internet sites, or third party applications; and (b) {\*\bkmkstart OLE_LINK76}{\*\bkmkstart OLE_LINK77} -claims for breach of contract, warranty, guarantee, or condition\hich\af42\dbch\af11\loch\f42 ; strict liability, negligence, or other tort; or any other claim; in each case to the extent permitted by applicable law.{\*\bkmkend OLE_LINK76} -{\*\bkmkend OLE_LINK77} -\par \hich\af42\dbch\af11\loch\f42 It also applies even if Microsoft knew or should have known about the possibility of the damages. The above limitation or exclusion m\hich\af42\dbch\af11\loch\f42 -ay not apply to you because your state, province, or country may not allow the exclusion or limitation of incidental, consequential, or other damages. -\par }\pard\plain \ltrpar\s2\ql \li0\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\outlinelevel1\adjustright\rin0\lin0\itap0\pararsid7813854 \rtlch\fcs1 \ab\af40\afs19\alang1025 \ltrch\fcs0 -\fs19\lang1033\langfe1033\loch\af40\hich\af40\dbch\af11\cgrid\langnp1033\langfenp1033 {\rtlch\fcs1 \af42 \ltrch\fcs0 \f42\insrsid7813854\charrsid6112664 -\par }\pard\plain \ltrpar\s31\ql \li0\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid7813854 \rtlch\fcs1 \af40\afs19\alang1025 \ltrch\fcs0 -\b\fs19\lang1033\langfe1033\loch\af40\hich\af40\dbch\af11\cgrid\langnp1033\langfenp1033 {\rtlch\fcs1 \af42 \ltrch\fcs0 \f42\insrsid7813854\charrsid6112664 \hich\af42\dbch\af11\loch\f42 -Please note: As this software is distributed in Canada, some of the clauses in this agreement are provid\hich\af42\dbch\af11\loch\f42 ed below in French. -\par \hich\af42\dbch\af11\loch\f42 \hich\f42 Remarque: Ce logiciel \'e9\loch\f42 \hich\f42 tant distribu\'e9\loch\f42 \hich\f42 au Canada, certaines des clauses dans ce contrat sont fournies ci-dessous en fran\'e7\loch\f42 ais. -\par \hich\af42\dbch\af11\loch\f42 \hich\f42 EXON\'c9\loch\f42 \hich\f42 RATION DE GARANTIE. Le logiciel vis\'e9\loch\f42 \hich\f42 par une licence est offert \'ab\loch\f42 \hich\f42 tel quel \'bb\loch\f42 . Toute utilisation de ce logi -\hich\af42\dbch\af11\loch\f42 \hich\f42 ciel est \'e0\loch\f42 \hich\f42 votre seule risque et p\'e9\loch\f42 ril. Microsoft n\hich\f42 \rquote \loch\f42 \hich\f42 accorde aucune autre garantie expresse. Vous pouvez b\'e9\loch\f42 \hich\f42 n\'e9 -\loch\f42 ficier de droits additionnels en vertu du droit local sur la protection des consommateurs, que ce contrat ne peut modifier. La ou elles sont permis\hich\af42\dbch\af11\loch\f42 e\hich\af42\dbch\af11\loch\f42 \hich\f42 -s par le droit locale, les garanties implicites de qualit\'e9\loch\f42 marchande, d\hich\f42 \rquote \loch\f42 \hich\f42 ad\'e9\loch\f42 \hich\f42 quation \'e0\loch\f42 un usage particulier et d\hich\f42 \rquote \loch\f42 \hich\f42 absence de contrefa -\'e7\loch\f42 on sont exclues. -\par \hich\af42\dbch\af11\loch\f42 \hich\f42 LIMITATION DES DOMMAGES-INT\'c9\loch\f42 \hich\f42 R\'ca\loch\f42 \hich\f42 TS ET EXCLUSION DE RESPONSABILIT\'c9\loch\f42 POUR LES DOMMAGES. Vous pouvez obtenir de Mi\hich\af42\dbch\af11\loch\f42 \hich\f42 -crosoft et de ses fournisseurs une indemnisation en cas de dommages directs uniquement \'e0\loch\f42 \hich\f42 hauteur de 5,00 $ US. Vous ne pouvez pr\'e9\loch\f42 \hich\f42 tendre \'e0\loch\f42 \hich\f42 - aucune indemnisation pour les autres dommages, y compris les dommages sp\'e9\loch\f42 ciaux, indirects ou accessoires et pertes de\hich\af42\dbch\af11\loch\f42 \hich\af42\dbch\af11\loch\f42 \hich\f42 b\'e9\loch\f42 \hich\f42 n\'e9\loch\f42 fices. -\par \hich\af42\dbch\af11\loch\f42 Cette limitation concerne: -\par }\pard \ltrpar\s31\ql \fi-360\li360\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin360\itap0\pararsid7813854 {\rtlch\fcs1 \af42 \ltrch\fcs0 \f42\insrsid7813854\charrsid6112664 \bullet \tab \hich\af42\dbch\af11\loch\f42 -\hich\f42 tout ce qui est reli\'e9\loch\f42 au logiciel, aux services ou au contenu (y compris le code) figurant sur des sites Internet tiers ou dans des programmes tiers; et -\par \bullet \tab \hich\af42\dbch\af11\loch\f42 \hich\f42 les r\'e9\loch\f42 clamations au titre de violation de contrat ou de garan\hich\af42\dbch\af11\loch\f42 \hich\f42 tie, ou au titre de responsabilit\'e9\loch\f42 \hich\f42 stricte, de n\'e9\loch\f42 -gligence ou d\hich\f42 \rquote \loch\f42 \hich\f42 une autre faute dans la limite autoris\'e9\loch\f42 e par la loi en vigueur. -\par }\pard \ltrpar\s31\ql \li0\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid7813854 {\rtlch\fcs1 \af42 \ltrch\fcs0 \f42\insrsid7813854\charrsid6112664 \hich\af42\dbch\af11\loch\f42 Elle s\hich\f42 \rquote -\loch\f42 \hich\f42 applique \'e9\loch\f42 \hich\f42 galement, m\'ea\loch\f42 \hich\f42 me si Microsoft connaissait ou devrait conna\'ee\loch\f42 tre l\hich\f42 \rquote \'e9\loch\f42 \hich\f42 ventualit\'e9\loch\f42 d\hich\f42 \rquote \loch\f42 -un tel dommage. Si votre pays n\hich\f42 \rquote \loch\f42 aut\hich\af42\dbch\af11\loch\f42 orise pas l\hich\f42 \rquote \loch\f42 \hich\f42 exclusion ou la limitation de responsabilit\'e9\loch\f42 - pour les dommages indirects, accessoires ou de quelque nature que ce soit, il se peut que la limitation ou l\hich\f42 \rquote \loch\f42 exclusion ci-dessus ne s\hich\f42 \rquote \loch\f42 \hich\f42 appliquera pas \'e0\loch\f42 \hich\f42 votre \'e9 -\loch\f42 gard. -\par \hich\af42\dbch\af11\loch\f42 \hich\f42 EFFET JURIDIQUE. Le pr\'e9\loch\f42 sent contrat\hich\af42\dbch\af11\loch\f42 \hich\f42 d\'e9\loch\f42 crit certains droits juridiques. Vous pourriez avoir d\hich\f42 \rquote \loch\f42 \hich\f42 autres droits pr -\'e9\loch\f42 \hich\f42 vus par les lois de votre pays. Le pr\'e9\loch\f42 \hich\f42 sent contrat ne modifie pas les droits que vous conf\'e8\loch\f42 rent les lois de votre pays si celles-ci ne le permettent pas. -\par }\pard\plain \ltrpar\ql \li0\ri0\sl259\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid6173475 \rtlch\fcs1 \af31507\afs22\alang1025 \ltrch\fcs0 \f31506\fs22\lang1033\langfe1033\cgrid\langnp1033\langfenp1033 { -\rtlch\fcs1 \af31507 \ltrch\fcs0 \insrsid8394862 -\par }{\rtlch\fcs1 \af31507 \ltrch\fcs0 \insrsid6573559 -\par }{\*\themedata 504b030414000600080000002100e9de0fbfff0000001c020000130000005b436f6e74656e745f54797065735d2e786d6cac91cb4ec3301045f748fc83e52d4a -9cb2400825e982c78ec7a27cc0c8992416c9d8b2a755fbf74cd25442a820166c2cd933f79e3be372bd1f07b5c3989ca74aaff2422b24eb1b475da5df374fd9ad -5689811a183c61a50f98f4babebc2837878049899a52a57be670674cb23d8e90721f90a4d2fa3802cb35762680fd800ecd7551dc18eb899138e3c943d7e503b6 -b01d583deee5f99824e290b4ba3f364eac4a430883b3c092d4eca8f946c916422ecab927f52ea42b89a1cd59c254f919b0e85e6535d135a8de20f20b8c12c3b0 -0c895fcf6720192de6bf3b9e89ecdbd6596cbcdd8eb28e7c365ecc4ec1ff1460f53fe813d3cc7f5b7f020000ffff0300504b030414000600080000002100a5d6 -a7e7c0000000360100000b0000005f72656c732f2e72656c73848fcf6ac3300c87ef85bd83d17d51d2c31825762fa590432fa37d00e1287f68221bdb1bebdb4f -c7060abb0884a4eff7a93dfeae8bf9e194e720169aaa06c3e2433fcb68e1763dbf7f82c985a4a725085b787086a37bdbb55fbc50d1a33ccd311ba548b6309512 -0f88d94fbc52ae4264d1c910d24a45db3462247fa791715fd71f989e19e0364cd3f51652d73760ae8fa8c9ffb3c330cc9e4fc17faf2ce545046e37944c69e462 -a1a82fe353bd90a865aad41ed0b5b8f9d6fd010000ffff0300504b0304140006000800000021006b799616830000008a0000001c0000007468656d652f746865 -6d652f7468656d654d616e616765722e786d6c0ccc4d0ac3201040e17da17790d93763bb284562b2cbaebbf600439c1a41c7a0d29fdbd7e5e38337cedf14d59b -4b0d592c9c070d8a65cd2e88b7f07c2ca71ba8da481cc52c6ce1c715e6e97818c9b48d13df49c873517d23d59085adb5dd20d6b52bd521ef2cdd5eb9246a3d8b -4757e8d3f729e245eb2b260a0238fd010000ffff0300504b03041400060008000000210007b740aaca0600008f1a0000160000007468656d652f7468656d652f -7468656d65312e786d6cec595b8bdb46147e2ff43f08bd3bbe49be2cf1065bb69336bb49889d943cceda636bb2238dd18c776342a0244f7d2914d2d28706fad6 -87521a68a0a12ffd310b1bdaf447f4cc489667ec71f6420aa1640d8b34face996fce39face48ba7aed51449d239c70c2e2965bbe52721d1c8fd898c4d3967b6f -d82f345c870b148f1165316eb90bccdd6bbb9f7e7215ed881047d801fb98efa0961b0a31db2916f9088611bfc26638866b13964448c069322d8e13740c7e235a -ac944ab5628448ec3a318ac0ededc9848cb033942edddda5f31e85d358703930a2c940bac68685c28e0fcb12c1173ca089738468cb8579c6ec78881f09d7a188 -0bb8d0724beacf2dee5e2da29dcc888a2db69a5d5ffd657699c1f8b0a2e64ca607f9a49ee77bb576ee5f01a8d8c4f5eabd5aaf96fb5300341ac14a532eba4fbf -d3ec74fd0cab81d2438bef6ebd5b2d1b78cd7f758373db973f03af40a97f6f03dfef07104503af4029dedfc07b5ebd1278065e81527c6d035f2fb5bb5eddc02b -5048497cb8812ef9b56ab05c6d0e99307ac30a6ffa5ebf5ec99caf50500d7975c929262c16db6a2d420f59d2078004522448ec88c50c4fd008aa3840941c24c4 -d923d3100a6f8662c661b85429f54b55f82f7f9e3a5211413b1869d6921730e11b43928fc34709998996fb39787535c8e9ebd7274f5f9d3cfdfde4d9b393a7bf -66732b5786dd0d144f75bbb73f7df3cf8b2f9dbf7ffbf1edf36fd3a9d7f15cc7bff9e5ab377ffcf92ef7b0e255284ebf7bf9e6d5cbd3efbffeebe7e716efed04 -1de8f0218930776ee163e72e8b608116fef820b998c5304444b768c7538e622467b1f8ef89d040df5a208a2cb80e36e3783f01a9b101afcf1f1a8407613217c4 -e2f1661819c07dc6688725d628dc947369611ecee3a97df264aee3ee2274649b3b40b191e5de7c061a4b6c2e83101b34ef50140b34c531168ebcc60e31b6acee -0121465cf7c928619c4d84f380381d44ac21199203a39a56463748047959d80842be8dd8ecdf773a8cda56ddc5472612ee0d442de487981a61bc8ee602453697 -4314513de07b48843692834532d2713d2e20d3534c99d31b63ce6d36b71358af96f49b2033f6b4efd345642213410e6d3ef710633ab2cb0e831045331b7640e2 -50c77ec60fa144917387091b7c9f9977883c873ca0786bbaef136ca4fb6c35b8070aab535a1588bc324f2cb9bc8e9951bf83059d20aca4061a80a1eb1189cf14 -f93579f7ff3b7907113dfde1856545ef47d2ed8e8d7c5c50ccdb09b1de4d37d6247c1b6e5db803968cc987afdb5d348fef60b855369bd747d9fe28dbeeff5eb6 -b7ddcfef5fac57fa0cd22db7ade9765d6ddea3ad7bf709a174201614ef71b57de7d095c67d189476eab915e7cf72b3100ee59d0c1318b86982948d9330f10511 -e1204433d8e3975de964ca33d753eecc1887adbf1ab6fa96783a8ff6d9387d642d97e5e3692a1e1c89d578c9cfc7e17143a4e85a7df51896bb576ca7ea717949 -40da5e8484369949a26a21515f0eca20a98773089a85845ad97b61d1b4b06848f7cb546db0006a795660dbe4c066abe5fa1e9880113c55218ac7324f69aa97d9 -55c97c9f99de164ca302600fb1ac8055a69b92ebd6e5c9d5a5a5768e4c1b24b4723349a8c8a81ec64334c65975cad1f3d0b868ae9bab941af46428d47c505a2b -1af5c6bb585c36d760b7ae0d34d69582c6ce71cbad557d2899119ab5dc093cfac3613483dae172bb8be814de9f8d4492def097519659c24517f1300db8129d54 -0d222270e25012b55cb9fc3c0d34561aa2b8952b20081f2cb926c8ca87460e926e26194f267824f4b46b2332d2e929287caa15d6abcafcf26069c9e690ee4138 -3e760ee83cb98ba0c4fc7a5906704c38bc012aa7d11c1378a5990bd9aafed61a5326bbfa3b455543e938a2b310651d4517f314aea43ca7a3cef2186867d99a21 -a05a48b2467830950d560faad14df3ae9172d8da75cf369291d34473d5330d55915dd3ae62c60ccb36b016cbcb35798dd532c4a0697a874fa57b5d729b4bad5b -db27e45d02029ec7cfd275cfd110346aabc90c6a92f1a60c4bcdce46cddeb15ce019d4ced32434d5af2dddaec52def11d6e960f0529d1fecd6ab168626cb7da5 -8ab4faf6a17f9e60070f413cbaf022784e0557a9848f0f09820dd140ed4952d9805be491c86e0d3872e60969b98f4b7edb0b2a7e502835fc5ec1ab7aa542c36f -570b6ddfaf967b7eb9d4ed549e4063116154f6d3ef2e7d780d4517d9d71735bef105265abe69bb32625191a92f2c45455c7d812957b67f81710888cee35aa5df -ac363bb542b3daee17bc6ea7516806b54ea15b0beadd7e37f01bcdfe13d7395260af5d0dbc5aaf51a89583a0e0d54a927ea359a87b954adbabb71b3daffd24db -c6c0ca53f9c86201e155bc76ff050000ffff0300504b0304140006000800000021000dd1909fb60000001b010000270000007468656d652f7468656d652f5f72 -656c732f7468656d654d616e616765722e786d6c2e72656c73848f4d0ac2301484f78277086f6fd3ba109126dd88d0add40384e4350d363f2451eced0dae2c08 -2e8761be9969bb979dc9136332de3168aa1a083ae995719ac16db8ec8e4052164e89d93b64b060828e6f37ed1567914b284d262452282e3198720e274a939cd0 -8a54f980ae38a38f56e422a3a641c8bbd048f7757da0f19b017cc524bd62107bd5001996509affb3fd381a89672f1f165dfe514173d9850528a2c6cce0239baa -4c04ca5bbabac4df000000ffff0300504b01022d0014000600080000002100e9de0fbfff0000001c0200001300000000000000000000000000000000005b436f -6e74656e745f54797065735d2e786d6c504b01022d0014000600080000002100a5d6a7e7c0000000360100000b00000000000000000000000000300100005f72 -656c732f2e72656c73504b01022d00140006000800000021006b799616830000008a0000001c00000000000000000000000000190200007468656d652f746865 -6d652f7468656d654d616e616765722e786d6c504b01022d001400060008000000210007b740aaca0600008f1a00001600000000000000000000000000d60200 -007468656d652f7468656d652f7468656d65312e786d6c504b01022d00140006000800000021000dd1909fb60000001b01000027000000000000000000000000 -00d40900007468656d652f7468656d652f5f72656c732f7468656d654d616e616765722e786d6c2e72656c73504b050600000000050005005d010000cf0a00000000} -{\*\colorschememapping 3c3f786d6c2076657273696f6e3d22312e302220656e636f64696e673d225554462d3822207374616e64616c6f6e653d22796573223f3e0d0a3c613a636c724d -617020786d6c6e733a613d22687474703a2f2f736368656d61732e6f70656e786d6c666f726d6174732e6f72672f64726177696e676d6c2f323030362f6d6169 -6e22206267313d226c743122207478313d22646b3122206267323d226c743222207478323d22646b322220616363656e74313d22616363656e74312220616363 -656e74323d22616363656e74322220616363656e74333d22616363656e74332220616363656e74343d22616363656e74342220616363656e74353d22616363656e74352220616363656e74363d22616363656e74362220686c696e6b3d22686c696e6b2220666f6c486c696e6b3d22666f6c486c696e6b222f3e} -{\*\latentstyles\lsdstimax373\lsdlockeddef0\lsdsemihiddendef0\lsdunhideuseddef0\lsdqformatdef0\lsdprioritydef99{\lsdlockedexcept \lsdqformat1 \lsdpriority0 \lsdlocked0 Normal;\lsdqformat1 \lsdlocked0 heading 1; -\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdlocked0 heading 2;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdlocked0 heading 3;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdlocked0 heading 4; -\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdlocked0 heading 5;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdlocked0 heading 6;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdlocked0 heading 7; -\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdlocked0 heading 8;\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdlocked0 heading 9;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 1;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 2; -\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 5;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 6; -\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 7;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 8;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index 9;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 1; -\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 2;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 3;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 4; -\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 5;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 6;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 7; -\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 8;\lsdsemihidden1 \lsdunhideused1 \lsdpriority39 \lsdlocked0 toc 9;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Normal Indent;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 footnote text; -\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 annotation text;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 header;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 footer;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 index heading; -\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority35 \lsdlocked0 caption;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 table of figures;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 envelope address; -\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 envelope return;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 footnote reference;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 annotation reference;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 line number; -\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 page number;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 endnote reference;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 endnote text;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 table of authorities; -\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 macro;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 toa heading;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Bullet; -\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Number;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List 4; -\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List 5;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Bullet 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Bullet 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Bullet 4; -\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Bullet 5;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Number 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Number 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Number 4; -\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Number 5;\lsdqformat1 \lsdpriority10 \lsdlocked0 Title;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Closing;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Signature; -\lsdsemihidden1 \lsdunhideused1 \lsdpriority1 \lsdlocked0 Default Paragraph Font;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text Indent;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Continue; -\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Continue 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Continue 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Continue 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 List Continue 5; -\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Message Header;\lsdqformat1 \lsdpriority11 \lsdlocked0 Subtitle;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Salutation;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Date; -\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text First Indent;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text First Indent 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Note Heading;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text 2; -\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text Indent 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Body Text Indent 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Block Text; -\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Hyperlink;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 FollowedHyperlink;\lsdqformat1 \lsdpriority22 \lsdlocked0 Strong;\lsdqformat1 \lsdpriority20 \lsdlocked0 Emphasis; -\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Document Map;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Plain Text;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 E-mail Signature;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Top of Form; -\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Bottom of Form;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Normal (Web);\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Acronym;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Address; -\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Cite;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Code;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Definition;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Keyboard; -\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Preformatted;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Sample;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Typewriter;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 HTML Variable; -\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 annotation subject;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 No List;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Outline List 1;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Outline List 2; -\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Outline List 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Simple 1;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Simple 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Simple 3; -\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Classic 1;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Classic 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Classic 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Classic 4; -\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Colorful 1;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Colorful 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Colorful 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Columns 1; -\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Columns 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Columns 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Columns 4;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Columns 5; -\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Grid 1;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Grid 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Grid 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Grid 4; -\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Grid 5;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Grid 6;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Grid 7;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Grid 8; -\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table List 1;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table List 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table List 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table List 4; -\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table List 5;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table List 6;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table List 7;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table List 8; -\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table 3D effects 1;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table 3D effects 2;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table 3D effects 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Contemporary; -\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Elegant;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Professional;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Subtle 1;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Subtle 2; -\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Web 1;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Table Web 3;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Balloon Text;\lsdpriority39 \lsdlocked0 Table Grid;\lsdsemihidden1 \lsdlocked0 Placeholder Text; -\lsdqformat1 \lsdpriority1 \lsdlocked0 No Spacing;\lsdpriority60 \lsdlocked0 Light Shading;\lsdpriority61 \lsdlocked0 Light List;\lsdpriority62 \lsdlocked0 Light Grid;\lsdpriority63 \lsdlocked0 Medium Shading 1;\lsdpriority64 \lsdlocked0 Medium Shading 2; -\lsdpriority65 \lsdlocked0 Medium List 1;\lsdpriority66 \lsdlocked0 Medium List 2;\lsdpriority67 \lsdlocked0 Medium Grid 1;\lsdpriority68 \lsdlocked0 Medium Grid 2;\lsdpriority69 \lsdlocked0 Medium Grid 3;\lsdpriority70 \lsdlocked0 Dark List; -\lsdpriority71 \lsdlocked0 Colorful Shading;\lsdpriority72 \lsdlocked0 Colorful List;\lsdpriority73 \lsdlocked0 Colorful Grid;\lsdpriority60 \lsdlocked0 Light Shading Accent 1;\lsdpriority61 \lsdlocked0 Light List Accent 1; -\lsdpriority62 \lsdlocked0 Light Grid Accent 1;\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 1;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 1;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 1;\lsdsemihidden1 \lsdlocked0 Revision; -\lsdqformat1 \lsdpriority34 \lsdlocked0 List Paragraph;\lsdqformat1 \lsdpriority29 \lsdlocked0 Quote;\lsdqformat1 \lsdpriority30 \lsdlocked0 Intense Quote;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 1;\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 1; -\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 1;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 1;\lsdpriority70 \lsdlocked0 Dark List Accent 1;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 1;\lsdpriority72 \lsdlocked0 Colorful List Accent 1; -\lsdpriority73 \lsdlocked0 Colorful Grid Accent 1;\lsdpriority60 \lsdlocked0 Light Shading Accent 2;\lsdpriority61 \lsdlocked0 Light List Accent 2;\lsdpriority62 \lsdlocked0 Light Grid Accent 2;\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 2; -\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 2;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 2;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 2;\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 2;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 2; -\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 2;\lsdpriority70 \lsdlocked0 Dark List Accent 2;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 2;\lsdpriority72 \lsdlocked0 Colorful List Accent 2;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 2; -\lsdpriority60 \lsdlocked0 Light Shading Accent 3;\lsdpriority61 \lsdlocked0 Light List Accent 3;\lsdpriority62 \lsdlocked0 Light Grid Accent 3;\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 3;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 3; -\lsdpriority65 \lsdlocked0 Medium List 1 Accent 3;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 3;\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 3;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 3;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 3; -\lsdpriority70 \lsdlocked0 Dark List Accent 3;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 3;\lsdpriority72 \lsdlocked0 Colorful List Accent 3;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 3;\lsdpriority60 \lsdlocked0 Light Shading Accent 4; -\lsdpriority61 \lsdlocked0 Light List Accent 4;\lsdpriority62 \lsdlocked0 Light Grid Accent 4;\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 4;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 4;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 4; -\lsdpriority66 \lsdlocked0 Medium List 2 Accent 4;\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 4;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 4;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 4;\lsdpriority70 \lsdlocked0 Dark List Accent 4; -\lsdpriority71 \lsdlocked0 Colorful Shading Accent 4;\lsdpriority72 \lsdlocked0 Colorful List Accent 4;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 4;\lsdpriority60 \lsdlocked0 Light Shading Accent 5;\lsdpriority61 \lsdlocked0 Light List Accent 5; -\lsdpriority62 \lsdlocked0 Light Grid Accent 5;\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 5;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 5;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 5;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 5; -\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 5;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 5;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 5;\lsdpriority70 \lsdlocked0 Dark List Accent 5;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 5; -\lsdpriority72 \lsdlocked0 Colorful List Accent 5;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 5;\lsdpriority60 \lsdlocked0 Light Shading Accent 6;\lsdpriority61 \lsdlocked0 Light List Accent 6;\lsdpriority62 \lsdlocked0 Light Grid Accent 6; -\lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 6;\lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 6;\lsdpriority65 \lsdlocked0 Medium List 1 Accent 6;\lsdpriority66 \lsdlocked0 Medium List 2 Accent 6; -\lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 6;\lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 6;\lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 6;\lsdpriority70 \lsdlocked0 Dark List Accent 6;\lsdpriority71 \lsdlocked0 Colorful Shading Accent 6; -\lsdpriority72 \lsdlocked0 Colorful List Accent 6;\lsdpriority73 \lsdlocked0 Colorful Grid Accent 6;\lsdqformat1 \lsdpriority19 \lsdlocked0 Subtle Emphasis;\lsdqformat1 \lsdpriority21 \lsdlocked0 Intense Emphasis; -\lsdqformat1 \lsdpriority31 \lsdlocked0 Subtle Reference;\lsdqformat1 \lsdpriority32 \lsdlocked0 Intense Reference;\lsdqformat1 \lsdpriority33 \lsdlocked0 Book Title;\lsdsemihidden1 \lsdunhideused1 \lsdpriority37 \lsdlocked0 Bibliography; -\lsdsemihidden1 \lsdunhideused1 \lsdqformat1 \lsdpriority39 \lsdlocked0 TOC Heading;\lsdpriority41 \lsdlocked0 Plain Table 1;\lsdpriority42 \lsdlocked0 Plain Table 2;\lsdpriority43 \lsdlocked0 Plain Table 3;\lsdpriority44 \lsdlocked0 Plain Table 4; -\lsdpriority45 \lsdlocked0 Plain Table 5;\lsdpriority40 \lsdlocked0 Grid Table Light;\lsdpriority46 \lsdlocked0 Grid Table 1 Light;\lsdpriority47 \lsdlocked0 Grid Table 2;\lsdpriority48 \lsdlocked0 Grid Table 3;\lsdpriority49 \lsdlocked0 Grid Table 4; -\lsdpriority50 \lsdlocked0 Grid Table 5 Dark;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful;\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful;\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 1;\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 1; -\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 1;\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 1;\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 1;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 1; -\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 1;\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 2;\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 2;\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 2; -\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 2;\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 2;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 2;\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 2; -\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 3;\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 3;\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 3;\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 3; -\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 3;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 3;\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 3;\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 4; -\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 4;\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 4;\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 4;\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 4; -\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 4;\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 4;\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 5;\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 5; -\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 5;\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 5;\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 5;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 5; -\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 5;\lsdpriority46 \lsdlocked0 Grid Table 1 Light Accent 6;\lsdpriority47 \lsdlocked0 Grid Table 2 Accent 6;\lsdpriority48 \lsdlocked0 Grid Table 3 Accent 6; -\lsdpriority49 \lsdlocked0 Grid Table 4 Accent 6;\lsdpriority50 \lsdlocked0 Grid Table 5 Dark Accent 6;\lsdpriority51 \lsdlocked0 Grid Table 6 Colorful Accent 6;\lsdpriority52 \lsdlocked0 Grid Table 7 Colorful Accent 6; -\lsdpriority46 \lsdlocked0 List Table 1 Light;\lsdpriority47 \lsdlocked0 List Table 2;\lsdpriority48 \lsdlocked0 List Table 3;\lsdpriority49 \lsdlocked0 List Table 4;\lsdpriority50 \lsdlocked0 List Table 5 Dark; -\lsdpriority51 \lsdlocked0 List Table 6 Colorful;\lsdpriority52 \lsdlocked0 List Table 7 Colorful;\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 1;\lsdpriority47 \lsdlocked0 List Table 2 Accent 1;\lsdpriority48 \lsdlocked0 List Table 3 Accent 1; -\lsdpriority49 \lsdlocked0 List Table 4 Accent 1;\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 1;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 1;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 1; -\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 2;\lsdpriority47 \lsdlocked0 List Table 2 Accent 2;\lsdpriority48 \lsdlocked0 List Table 3 Accent 2;\lsdpriority49 \lsdlocked0 List Table 4 Accent 2; -\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 2;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 2;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 2;\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 3; -\lsdpriority47 \lsdlocked0 List Table 2 Accent 3;\lsdpriority48 \lsdlocked0 List Table 3 Accent 3;\lsdpriority49 \lsdlocked0 List Table 4 Accent 3;\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 3; -\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 3;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 3;\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 4;\lsdpriority47 \lsdlocked0 List Table 2 Accent 4; -\lsdpriority48 \lsdlocked0 List Table 3 Accent 4;\lsdpriority49 \lsdlocked0 List Table 4 Accent 4;\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 4;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 4; -\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 4;\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 5;\lsdpriority47 \lsdlocked0 List Table 2 Accent 5;\lsdpriority48 \lsdlocked0 List Table 3 Accent 5; -\lsdpriority49 \lsdlocked0 List Table 4 Accent 5;\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 5;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 5;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 5; -\lsdpriority46 \lsdlocked0 List Table 1 Light Accent 6;\lsdpriority47 \lsdlocked0 List Table 2 Accent 6;\lsdpriority48 \lsdlocked0 List Table 3 Accent 6;\lsdpriority49 \lsdlocked0 List Table 4 Accent 6; -\lsdpriority50 \lsdlocked0 List Table 5 Dark Accent 6;\lsdpriority51 \lsdlocked0 List Table 6 Colorful Accent 6;\lsdpriority52 \lsdlocked0 List Table 7 Colorful Accent 6;\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Mention; -\lsdsemihidden1 \lsdunhideused1 \lsdlocked0 Smart Hyperlink;}}{\*\datastore 0105000002000000180000004d73786d6c322e534158584d4c5265616465722e362e3000000000000000000000060000 -d0cf11e0a1b11ae1000000000000000000000000000000003e000300feff090006000000000000000000000001000000010000000000000000100000feffffff00000000feffffff0000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -fffffffffffffffffdfffffffeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff -ffffffffffffffffffffffffffffffff52006f006f007400200045006e00740072007900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000016000500ffffffffffffffffffffffff0c6ad98892f1d411a65f0040963251e5000000000000000000000000c051 -85b5e0f4d101feffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000 -00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000 -000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff000000000000000000000000000000000000000000000000 -0000000000000000000000000000000000000000000000000105000000000000}} \ No newline at end of file diff --git a/assets/macDialog.png b/assets/macDialog.png new file mode 100644 index 00000000000..432aeca610e Binary files /dev/null and b/assets/macDialog.png differ diff --git a/assets/manpage/pwsh.1 b/assets/manpage/pwsh.1 new file mode 100644 index 00000000000..14c191241a9 --- /dev/null +++ b/assets/manpage/pwsh.1 @@ -0,0 +1,10 @@ +.\" generated with Ronn/v0.7.3 +.\" http://github.com/rtomayko/ronn/tree/0.7.3 +. +.TH "PWSH" "1" "October 2023" "" "" +. +.SH "NAME" +\fBpwsh\fR \- PowerShell command\-line shell and \.NET REPL +. +.SH "SYNOPSIS" +\fBpwsh\fR [\fB\-Login\fR] [ [\fB\-File\fR] \fIfilePath\fR [args] ] [\fB\-Command\fR { \- | \fIscript\-block\fR [\fB\-args\fR \fIarg\-array\fR] | \fIstring\fR [\fICommandParameters\fR] } ] [\fB\-ConfigurationFile\fR \fIfilePath\fR] [\fB\-ConfigurationName\fR \fIstring\fR] [\fB\-CustomPipeName\fR \fIstring\fR] [\fB\-EncodedArguments\fR \fIBase64EncodedArguments\fR] [\fB\-EncodedCommand\fR \fIBase64EncodedCommand\fR] [\fB\-ExecutionPolicy\fR \fIExecutionPolicy\fR] [\fB\-Help\fR] [\fB\-InputFormat\fR {Text | XML}] [\fB\-Interactive\fR] [\fB\-MTA\fR] [\fB\-NoExit\fR] [\fB\-NoLogo\fR] [\fB\-NonInteractive\fR] [\fB\-NoProfile\fR] [\fB\-NoProfileLoadTime\fR] [\fB\-OutputFormat\fR {Text | XML}] [\fB\-SettingsFile\fR \fIfilePath\fR] [\fB\-SSHServerMode\fR] [\fB\-STA\fR] [\fB\-Version\fR] [\fB\-WindowStyle\fR diff --git a/assets/manpage/pwsh.1.ronn b/assets/manpage/pwsh.1.ronn new file mode 100644 index 00000000000..98320cc60c8 --- /dev/null +++ b/assets/manpage/pwsh.1.ronn @@ -0,0 +1,230 @@ +pwsh(1) -- PowerShell command-line shell and .NET REPL +================================================= + +## SYNOPSIS + +`pwsh` [`-Login`] [ [`-File`] [args] ] +[`-Command` { - | [`-args` ] | +[] } ] [`-ConfigurationFile` ] +[`-ConfigurationName` ] [`-CustomPipeName` ] +[`-EncodedArguments` ] +[`-EncodedCommand` ] +[`-ExecutionPolicy` ] [`-Help`] [`-InputFormat` {Text | XML}] +[`-Interactive`] [`-MTA`] [`-NoExit`] [`-NoLogo`] [`-NonInteractive`] +[`-NoProfile`] [`-NoProfileLoadTime`] [`-OutputFormat` {Text | XML}] +[`-SettingsFile` ] [`-SSHServerMode`] [`-STA`] [`-Version`] +[`-WindowStyle` + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Page-1 + + + + + Start/End + User has idea! + + + + + + + + + + + + + + + + + + + User has idea! + + Dynamic connector + + + + Process + User files issue with rationale and use cases + + + + + + + + + + + + + + + + + + + User files issue with rationale and use cases + + Dynamic connector.9 + + + + Process.8 + Maintainers label issue with Area Label + + + + + + + + + + + + + + + + + + + Maintainers label issue with Area Label + + Dynamic connector.13 + No + + + + + No + + Process.12 + Users discuss idea “exhaustively” (TBD by WGs) + + + + + + + + + + + + + + + + + + + Users discuss idea exhaustively” (TBD by WGs) + + Dynamic connector.15 + + + + Decision + Do the WGs think the idea has potential / is worth pursuing? + + + + + + + + + + + + + + + + + + + Do the WGs think the idea has potential / is worth pursuing? + + Dynamic connector.23 + No + + + + + No + + Subprocess + Committee appeals process (TBD) + + + + + + + + + + + + + + + + + + + + + + + Committee appeals process (TBD) + + Dynamic connector.27 + Appeal unsuccessful + + + + + Appeal unsuccessful + + Start/End.26 + Issue is closed, idea not pursued within PS repo + + + + + + + + + + + + + + + + + + + + Issue is closed, idea not pursued within PS repo + + Dynamic connector.30 + Successful appeal + + + + + Successful appeal + + Process.32 + Implement and release the implementation outside of PS + + + + + + + + + + + + + + + + + + + Implement and release the implementation outside of PS + + Process.43 + WGs label “RFC required”; Both paths are (eventually) require... + + + + + + + + + + + + + + + + + + + + WGs label “RFC required”; Both paths are (eventually) required; author(s) can choose to do in any order + + Dynamic connector.46 + + + + Process.45 + Contributor writes and publishes RFC as draft PR in PowerShel... + + + + + + + + + + + + + + + + + + + + Contributor writes and publishes RFC as draft PR in PowerShell-RFC(add reference in original issue) + + Dynamic connector.48 + + + + Process.47 + Contributor publishes PR with WIP and/or prototype code as dr... + + + + + + + + + + + + + + + + + + + + Contributor publishes PR with WIP and/or prototype code as draft PR in PowerShell repo(add reference in original issue, Maintainers add `Proposal` label) + + Dynamic connector.50 + + + + Process.49 + WGs, contributors, and any others have discussion about RFC f... + + + + + + + + + + + + + + + + + + + WGs, contributors, and any others have discussion about RFC for >= 2 months + + Dynamic connector.52 + + + + Process.51 + Author marks RFC PR as non-draft + + + + + + + + + + + + + + + + + + + Author marks RFC PR as non-draft + + Dynamic connector.56 + + + + Decision.55 + Does the code PR meet WG and Maintainer standards? + + + + + + + + + + + + + + + + + + + + Does the code PR meet WG and Maintainer standards? + + Decision.65 + Is the RFC accepted and matching the implemen-tation? + + + + + + + + + + + + + + + + + + + + Is the RFC accepted and matching the implemen-tation? + + Dynamic connector.67 + No + + + + + No + + Dynamic connector.69 + + + + Start/End.68 + Merge as non-experimental code + + + + + + + + + + + + + + + + + + + Merge as non-experimental code + + Dynamic connector.71 + + + + Process.70 + Committee review + + + + + + + + + + + + + + + + + + + Committee review + + Decision.75 + Does the RFC contain all necessary info to merge as experimen... + + + + + + + + + + + + + + + + + + + + Does the RFC contain all necessary info to merge as experimental? + + Decision.77 + Committee votes to approve RFC + + + + + + + + + + + + + + + + + + + Committee votes to approve RFC + + Dynamic connector.79 + Reject + + + + + Reject + + Process.80 + RFC author adds additional info + + + + + + + + + + + + + + + + + + + RFC author adds additional info + + Dynamic connector.87 + Approve + + + + + Approve + + Decision.86 + Is the code ready to go? + + + + + + + + + + + + + + + + + + + Is the code ready to go? + + Dynamic connector.88 + Yes + + + + + Yes + + Dynamic connector.90 + No + + + + + No + + Decision.36 + Do you still think it needs to be in the PS package? + + + + + + + + + + + + + + + + + + + + Do you still think it needs to be in the PS package? + + Dynamic connector.1012 + Yes + + + + + Yes + + Decision.1006 + Can the idea be implemented outside of the PS code repo? + + + + + + + + + + + + + + + + + + + + Can the idea be implemented outside of the PS code repo? + + Dynamic connector.1007 + + + + Dynamic connector.1008 + Yes + + + + + Yes + + Dynamic connector.1001 + + + + Dynamic connector.1014 + + + + Decision.1015 + Do the WGs think an RFC is required? + + + + + + + + + + + + + + + + + + + Do the WGs think an RFC is required? + + Dynamic connector.1016 + Yes + + + + + Yes + + Dynamic connector.1017 + Yes + + + + + Yes + + Dynamic connector.1019 + No + + + + + No + + Process.1018 + WGs label “RFC not required”; Contributor opens a PR to be re... + + + + + + + + + + + + + + + + + + + + WGs label “RFC not required”; Contributor opens a PR to be reviewed by WGs and merged by maintainers + + Dynamic connector.1020 + Yes + + + + + Yes + + Process.1021 + Committee labels RFC PR with “Experimental - Approved” + + + + + + + + + + + + + + + + + + + Committee labels RFC PR with Experimental - Approved + + Dynamic connector.1023 + No + + + + + No + + Dynamic connector.1024 + + + + Dynamic connector.1026 + Yes + + + + + Yes + + Decision.1025 + Has the RFC been marked with “Experimental - Approved”? + + + + + + + + + + + + + + + + + + + + Has the RFC been marked with Experimental - Approved”? + + Dynamic connector.1028 + Yes + + + + + Yes + + Process.1027 + Merge code PR as Experimental Feature + + + + + + + + + + + + + + + + + + + Merge code PR as Experimental Feature + + Dynamic connector.1029 + No + + + + + No + + Dynamic connector.1031 + No + + + + + No + + Process.1030 + Contributor updates code PR based on feedback + + + + + + + + + + + + + + + + + + + Contributor updates code PR based on feedback + + Dynamic connector.1033 + + + + Decision.1032 + Has the code PR been merged as experimental? + + + + + + + + + + + + + + + + + + + + Has the code PR been merged as experimental? + + Dynamic connector.1034 + No + + + + + No + + Dynamic connector.1036 + Yes + + + + + Yes + + Decision.1035 + Does the Committee believe the experimental feature has had e... + + + + + + + + + + + + + + + + + + + + Does the Committee believe the experimental feature has had enough time to bake? + + Dynamic connector.1037 + Yes + + + + + Yes + + Dynamic connector.1038 + + + + Dynamic connector.1040 + + + + Decision.1039 + Does the Committee think the intent of the RFC is reasonable ... + + + + + + + + + + + + + + + + + + + + Does the Committee think the intent of the RFC is reasonable to pursue + + Dynamic connector.1041 + Yes + + + + + Yes + + Dynamic connector.1043 + No + + + + + No + + Start/End.1042 + Process stops + + + + + + + + + + + + + + + + + + + Process stops + + diff --git a/docs/community/process_diagram.vsdx b/docs/community/process_diagram.vsdx new file mode 100644 index 00000000000..014c28fa43d Binary files /dev/null and b/docs/community/process_diagram.vsdx differ diff --git a/docs/community/working-group-definitions.md b/docs/community/working-group-definitions.md new file mode 100644 index 00000000000..277fc37f789 --- /dev/null +++ b/docs/community/working-group-definitions.md @@ -0,0 +1,199 @@ +# Working Group Definitions + +This document maintains a list of the current PowerShell [Working Groups (WG)](working-group.md), +as well as their definitions, membership, and a non-exhaustive set of examples of topics that fall +within the purview of that WG. + +For an up-to-date list of the issue/PR labels associated with these WGs, +see [Issue Management](../maintainers/issue-management.md) + +## Desired State Configuration (DSC) + +The Desired State Configuration (DSC) WG manages all facets of DSC in PowerShell 7, +including language features (like the `Configuration` keyword) +and the `PSDesiredStateConfiguration` module. + +Today, DSC is integrated into the PowerShell language, and we need to manage it as such. + +### Members + +* @TravisEz13 +* @theJasonHelmick +* @anmenaga +* @gaelcolas +* @michaeltlombardi +* @SteveL-MSFT + +## Developer Experience + +The PowerShell developer experience includes the **development of modules** (in C#, PowerShell script, etc.), +as well as the experience of **hosting PowerShell and its APIs** in other applications and language runtimes. +Special consideration should be given to topics like **backwards compatibility** with Windows PowerShell +(e.g. with **PowerShell Standard**) and **integration with related developer tools** +(e.g. .NET CLI or the PowerShell extension for Visual Studio Code). + +### Members + +* @JamesWTruher (PS Standard, module authoring) +* @adityapatwardhan (SDK) +* @michaeltlombardi +* @SeeminglyScience +* @bergmeister + +## Engine + +The PowerShell engine is one of the largest and most complex aspects of the codebase. +The Engine WG should be focused on the +**implementation and maintenance of core PowerShell engine code**. +This includes (but is not limited to): + +* The language parser +* The command and parameter binders +* The module and provider systems + * `*-Item` cmdlets + * Providers +* Performance +* Componentization +* AssemblyLoadContext + +It's worth noting that the Engine WG is not responsible for the definition of the PowerShell language. +This should be handled by the Language WG instead. +However, it's expected that many issues will require input from both WGs. + +### Members + +* @daxian-dbw +* @JamesWTruher +* @rkeithhill +* @vexx32 +* @SeeminglyScience +* @IISResetMe +* @powercode +* @kilasuit + +## Interactive UX + +While much of PowerShell can be used through both interactive and non-interactive means, +some of the PowerShell user experience is exclusively interactive. +These topics include (but are not limited to): + +* Console +* Help System +* Tab completion / IntelliSense +* Markdown rendering +* PSReadLine +* Debugging + +### Members + +* @theJasonHelmick +* @daxian-dbw (PSReadline / IntelliSense) +* @adityapatwardhan (Markdown / help system) +* @JamesWTruher (cmdlet design) +* @SeeminglyScience +* @sdwheeler +* @kilasuit +* @FriedrichWeinmann +* @StevenBucher98 + +## Language + +The Language WG is distinct from the Engine WG in that they deal with the abstract definition +of the PowerShell language itself. +While all WGs will be working closely with the PowerShell Committee (and may share members), +it's likely that the Language WG will work especially close with them, +particularly given the long-lasting effects of language decisions. + +### Members + +* @JamesWTruher +* @daxian-dbw +* @SeeminglyScience + +## Remoting + +The Remoting WG should focus on topics like the **PowerShell Remoting Protocol (PSRP)**, +the **protocols implemented under PSRP** (e.g. WinRM and SSH), +and **other protocols used for remoting** (e.g. "pure SSH" as opposed to SSH over PSRP). +Given the commonality of serialization boundaries, the Remoting WG should also focus on +**the PowerShell job system**. + +### Members + +* @anmenaga +* @TravisEz13 + +## Cmdlets and Modules + +The Cmdlet WG should focus on core/inbox modules whose source code lives within the +`PowerShell/PowerShell` repository, +including the proposal of new cmdlets and parameters, improvements and bugfixes to existing +cmdlets/parameters, and breaking changes. + +However, some modules that ship as part of the PowerShell package are managed in other source repositories. +These modules are owned by the maintainers of those individual repositories. +These modules include: + +* [`Microsoft.PowerShell.Archive`](https://github.com/PowerShell/Microsoft.PowerShell.Archive) +* [`PackageManagement` (formerly `OneGet`)](https://github.com/OneGet/oneget) +* [`PowerShellGet`](https://github.com/PowerShell/PowerShellGet) +* [`PSDesiredStateConfiguration`](https://github.com/PowerShell/xPSDesiredStateConfiguration) + (Note: this community repository maintains a slightly different version of this module on the Gallery, + but should be used for future development of `PSDesiredStateConfiguration`.) +* [`PSReadLine`](https://github.com/PowerShell/PSReadLine) +* [`ThreadJob`](https://github.com/PowerShell/Modules/tree/master/Modules/Microsoft.PowerShell.ThreadJob) + +### Members + +* @JamesWTruher +* @SteveL-MSFT +* @jdhitsolutions +* @TobiasPSP +* @doctordns +* @kilasuit + +## Security + +The Security WG should be brought into any issues or pull requests which may have security implications +in order to provide their expertise, concerns, and guidance. + +### Members + +* @TravisEz13 +* @SydneySmithReal +* @anamnavi +* @SteveL-MSFT + +## Explicitly not Working Groups + +Some areas of ownership in PowerShell specifically do not have Working Groups. +For the sake of completeness, these are listed below: + +### Build + +Build includes everything that is needed to build, compile, and package PowerShell. +This bucket is also not oriented a customer-facing deliverable and is already something handled by Maintainers, +so we don't need to address it as part of the WGs. + +* Build + * `build.psm1` + * `install-powershell.ps1` + * Build infrastructure and automation +* Packaging + * Scripts + * Infrastructure + +### Quality + +Similar to the topic of building PowerShell, quality +(including **test code**, **test infrastructure**, and **code coverage**) +should be managed by the PowerShell Maintainers. + +* Test code + * Pester unit tests + * xUnit unit tests +* Test infrastructure + * Nightlies + * CI +* Code coverage +* Pester diff --git a/docs/community/working-group.md b/docs/community/working-group.md new file mode 100644 index 00000000000..8b9caea2857 --- /dev/null +++ b/docs/community/working-group.md @@ -0,0 +1,197 @@ +# Working Groups + +Working Groups (WGs) are collections of contributors with knowledge of specific components or +technologies in the PowerShell domain. +They are responsible for issue triage/acceptance, code reviews, and providing their expertise to +others in issues, PRs, and RFC discussions. + +The list, description, and membership of the existing Working Groups is available +[here](working-group-definitions.md). + +## Terms + +* **Contributor** is used interchangeably within this doc as anyone participating in issues or + contributing code, RFCs, documentations, tests, bug reports, etc., + regardless of their status with the PowerShell project. +* **Repository Maintainers** are trusted stewards of the PowerShell repository responsible for + maintaining consistency and quality of PowerShell code. + One of their primary responsibilities is merging pull requests after all requirements have been fulfilled. + (Learn more about the Repository Maintainers [here](https://github.com/PowerShell/PowerShell/tree/master/docs/maintainers).) +* The **PowerShell Committee** is responsible for the design and governance of the PowerShell project, + primarily by voting to accept or reject review-for-comment (RFC) documents. + (Learn more about the PowerShell Committee [here](https://github.com/PowerShell/PowerShell/blob/master/docs/community/governance.md#powershell-committee).) +* A **Working Group** is a collection of people responsible for providing expertise on a specific + area of PowerShell in order to help establish consensus within the community and Committee. + The responsibilities of Working Groups are outlined below. + (Note: while some experts within Working Groups may have more specific expertise in a sub-topic + of the team, + the intent is that each team is holistically and collectively responsible for making decisions + within the larger topic space.) + +## Goals + +In designing the WG process, the Committee had a few goals: + +1. Increase the velocity of innovation without compromising the stability of PowerShell +1. Reduce the time spent by contributors on writing/reviewing PRs and RFCs that are not feasible +1. Increase the formal authority of subject matter experts (SMEs) inside and outside of Microsoft +1. Decrease the volume of required technical discussions held by the Committee + +## Process + +This process is represented within the [`process_diagram.vsdx` Visio diagram](process_diagram.vsdx): + +![process_diagram](process_diagram.svg) + +1. A contributor has an idea for PowerShell +1. The contributor files an issue informally describing the idea, + including some rationale as to why it should happen and a few use cases to show how it could work, + as well as to determine viability and value to the community. + This should include examples of expected input and output so that others understand how the feature would function in the real world. +1. The issue gets triaged into an [Area label](https://github.com/PowerShell/PowerShell/blob/master/docs/maintainers/issue-management.md#feature-areas) + by Maintainers. + This area label maps to one or more Working Groups. +1. If the Working Group determines that an idea can be prototyped or built outside of the PowerShell repo + (e.g. as a module), + contributors should start that idea outside of the PowerShell project. + Given that the issue is no longer directly relevant to the PowerShell project, it should be closed + (with a link to the new project, if available). + If the implementation turns out to be successful and particularly popular, + and a contributor believes that it would be overwhelmingly valuable to include in the primary PowerShell package, + they can restart the process in a new issue proposing that the functionality be + incorporated directly into the primary PowerShell package. +1. After the issue is filed, interested contributors should discuss the + feasibility and approach of the idea in the issue. + The Working Group that owns that Area is expected to contribute to this discussion. + Working groups may have their own criteria to consider in their areas. +1. After an appropriately exhaustive discussion/conversation + (i.e. the Working Group has determined that no new arguments are being made), + the Working Group makes a call on whether they believe the idea should continue through this process. + Note: this should be done via a best effort of consensus among the Working Group. + We don't currently have hard requirements for how this should be done, + but some ideas include: + + * a single member of the Working Group makes a proposal as a comment and other members should + "react" on GitHub with up/down thumbs + * Working Groups communicate privately through their own established channel to reach consensus + + It's worth noting that Working Group members who repeatedly speak on behalf of the Working Group without + consensus, they may be censured or removed from the team. + +### Working Groups reject the proposal + +If the Working Group says the idea should not pursued, the process stops. +Some reasons for rejection include (but are not limited to): + +* the idea can be implemented and validated for usefulness and popularity outside of the primary PowerShell repo/package +* the idea is difficult/impossible to implement +* the implementation would introduce undesirable (and possibly breaking) changes to PowerShell +* other reasons specific to individual Working Groups + +In the instance that the contributor feels they have compelling arguments showing that the +Working Group is incorrect in their rejection, +they can appeal to the PowerShell Committee by mentioning `@PowerShell/PowerShell-Committee`, +upon which a maintainer will add the `Review-Committee` label to the issue and reopen it. +Then, the PS Committee will discuss further with the Working Group and others to make a final call on +whether or not the issue should be pursued further. + +Be sure to enumerate your reasons for appeal, as unfounded appeals may be rejected for consideration +by the Committee until reasons are given. + +### Working groups believe the proposal has merit and/or potential + +If the idea passes the preliminary acceptance criteria for the Working Group, +the process proceeds on one of a few different paths: + +#### "RFC Not Required" + +In some cases, a proposed idea is determined by the Working Group to be small, uncontroversial, or simple, +such that an RFC is not required (to be determined by the Working Group). +In these circumstances, the Working Group should mark the issue as "RFC not required", +upon which it can move directly to the code PR phase to be reviewed by Working Groups and Maintainers. + +The Committee still holds the authority to require an RFC if they see an "RFC Not Required" issue +that they feel needs more exposition before merging. + +In cases of minor breaking changes, Maintainers or Working Groups can add the `Review - Committee` label to get +additional opinions from the Committee. + +#### RFC/Prototype Process + +If an idea has any significant design or ecosystem implications, +*cannot* be prototyped or built outside of the PowerShell repo, +and the community and Working Groups agree that the idea is worth pursuing, +a contributor (who may or may not be the original issue filer) must do two things: + +* Write an RFC as a [Draft PR](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/about-pull-requests#draft-pull-requests) + into `PowerShell/PowerShell-RFC` +* Prototype the implementation as a [Draft PR](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/about-pull-requests#draft-pull-requests) + into `PowerShell/PowerShell` + +In both cases, the intention is to provide Working Groups and other contributors who care about the +idea an opportunity to provide feedback on the design and implementation. + +Either of these two steps can be done first, but both are required in order to have code accepted +into the PowerShell repository, including experimental features. + +Note: When "Draft" is capitalized in this document, I'm referring to the +[Draft pull request](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/about-pull-requests#draft-pull-requests) +feature on GitHub. +We intend to use this feature liberally to mark the lifecycle of an idea through to implementation. + +#### RFCs + +The existing RFC process uses folders as a way to move an RFC through a multi-stage process +(Draft -> Experimental -> Accepted/Final). +However, it was difficult to reconcile this process with the benefits of PR reviews. + +With the introduction of Draft PRs on GitHub, we are reorienting the acceptance process around the PR itself. +Going forward, an RFC will have three stages: + +1. A Draft PR, denoting that the RFC is still in the review period, openly soliciting comments, + and that it may continue to be significantly iterated upon with revisions, edits, and responses to + community feedback or concerns. +1. After a minimum of two months of discussion, the RFC/PR author marks the PR as + [ready for review](https://help.github.com/en/github/collaborating-with-issues-and-pull-requests/changing-the-stage-of-a-pull-request), + upon which the RFC will enter the Committee's review queue to discuss the RFC contents and comments, + and make a decision on whether it is reasonable to pursue. + If after this review, the Committee determines that the intent of the RFC is not reasonable + (e.g. there may be irreconcilable issues with the design, + or the intent may not fit with the principles of PowerShell), + they will reject the PR and the process terminates. +1. In most cases, the Committee will choose to wait for the code PR to be merged as experimental, + and leverage user feedback or telemetry to determine if the RFC PR should be merged. +1. Finally, the Committee will choose to either merge or close the RFC PR, + marking the RFC as either accepted or rejected, respectively. + +#### Experiments + +Often times, implementing an idea can demonstrate opportunities or challenges that were not well +understood when the idea was formulated. +Similarly, a "simple" code change can have far reaching effects that are not well understood +until you're able to experiment with an idea within working code. + +To that end, it's required that *some* implementation exist before the Committee will consider an +RFC for acceptance. +That way, contributors can compile the PR branch to play with a working iteration of the idea +as a way to understand whether the feature is working as expected and valuable. + +In most cases, as long as an RFC has already been written and published as a draft, +Working Groups or the Committee will approve the feature for incorporation as an experimental feature, +so that it can be trialed with greater usage as part of a preview release. +In addition to increasing the scope of those who can provide real-world feedback on the feature, +this enables us to use telemetry to understand if users are turning off the feature in large numbers. + +Note: today, this will be done on a case-by-case basis, but over time, the Committee will establish +firmer guidelines around when PRs should be merged as experimental. + +Experiments should be complete to the extent that they serve as reasonable indicators of the user experience. +In the case that breaking changes are required of the feature, the break should be made in the prototype +so that users can experiment with whether or not the break has a significant negative effect. + +#### Reporting Working Group members abuse + +Working group members are individuals and in many cases volunteers. +There may be situations where a working group member might exhibit behavior that is objectionable, +exceed the authority defined in this document or violate the [Code of Conduct](../../CODE_OF_CONDUCT.md). +We recommend to report such issues by using the [Report Content](https://docs.github.com/communities/maintaining-your-safety-on-github/reporting-abuse-or-spam) mechanism to bring it to the attention of the maintainers to review. diff --git a/docs/debugging/README.md b/docs/debugging/README.md index fb074aa7881..6e1acaaef59 100644 --- a/docs/debugging/README.md +++ b/docs/debugging/README.md @@ -1,11 +1,9 @@ -Visual Studio Code -================== +# Visual Studio Code -[Experimental .NET Core Debugging in VS Code][core-debug] enables -cross-platform debugging with the [Visual Studio Code][vscode] editor. +The [Visual Studio Code][vscode] editor supports cross-platform debugging. This is made possible by the [OmniSharp][] extension for VS Code. -Please review their [detailed instructions][vscclrdebugger]. In +Please review their [detailed instructions][core-debug]. In addition to being able to build PowerShell, you need: - C# Extension for VS Code installed @@ -19,7 +17,7 @@ You can do this in Bash with `export PATH=$PATH:$HOME/.dotnet` or in PowerShell Once the extension is installed, you have to open a C# file to force VS Code to install the actual .NET Core debugger (the editor will tell you to do this if -you attempt to debug and haven't already open a C# file). +you attempt to debug and haven't already opened a C# file). The committed `.vscode` folder in the root of this repository contains the `launch.json` and `tasks.json` files which provide Core PowerShell @@ -39,17 +37,15 @@ launch an external console with PowerShell running interactively. If neither of these installed, the editor will tell you to do so. Alternatively, the ".NET Core Attach" configuration will start listening for a -process named `powershell`, and will attach to it. If you need more fine grained +process named `powershell`, and will attach to it. If you need more fine-grained control, replace `processName` with `processId` and provide a PID. (Please be careful not to commit such a change.) -[core-debug]: https://blogs.msdn.microsoft.com/visualstudioalm/2016/03/10/experimental-net-core-debugging-in-vs-code/ +[core-debug]: https://learn.microsoft.com/dotnet/core/tutorials/with-visual-studio-code#debug [vscode]: https://code.visualstudio.com/ [OmniSharp]: https://github.com/OmniSharp/omnisharp-vscode -[vscclrdebugger]: http://aka.ms/vscclrdebugger -PowerShell -========== +## PowerShell The `Trace-Command` cmdlet can be used to enable tracing of certain PowerShell subsystems. Use `Get-TraceSource` for a list of tracers: @@ -91,8 +87,7 @@ The `-PSHost` specifies the sink, in this case the console host, so we can see the tracing messages. The `-Name` chooses the list of tracers to enable. -LLDB with SOS plug-in -===================== +## LLDB with SOS plug-in The `./tools/debug.sh` script can be used to launch PowerShell inside of LLDB with the SOS plug-in provided by .NET Core. This provides an additional way to @@ -104,14 +99,12 @@ The script is self-documented and contains a link to the [clr-debug]: https://github.com/dotnet/coreclr/blob/master/Documentation/building/debugging-instructions.md#debugging-coreclr-on-linux -corehost -======== +## `corehost` The native executable produced by .NET CLI will produce trace output if launched with `COREHOST_TRACE=1 ./powershell`. -CoreCLR PAL -=========== +## CoreCLR PAL The native code in the CLR has debug channels to selectively output information to the console. These are controlled by the @@ -123,8 +116,7 @@ you will need to narrow your scope. [header]: https://github.com/dotnet/coreclr/blob/release/1.0.0/src/pal/src/include/pal/dbgmsg.h -Debugging .NET Core -=================== +## Debugging .NET Core The .NET Core libraries downloaded from NuGet and shipped with PowerShell are release versions. This means that `PAL_DBG_CHANNELS` will not work with them, @@ -134,16 +126,14 @@ but should prove useful. They are currently written for Linux and are meant only as a shortcut means to debug. -Build and deploy CoreCLR ------------------------- +## Build and deploy CoreCLR * Clone CoreCLR: `git clone -b release/1.0.0 https://github.com/dotnet/coreclr.git` * Follow [building instructions](https://github.com/dotnet/coreclr/blob/release/1.0.0/Documentation/building/linux-instructions.md) * Wait for `./build.sh` to finish * Overwrite PowerShell libraries: `cp bin/Product/Linux.x64.Debug/*{so,dll} /path/to/powershell/` -Build and deploy CoreFX ------------------------ +## Build and deploy CoreFX * Clone CoreFX: `git clone -b release/1.0.0 https://github.com/dotnet/corefx.git` * Follow [building instructions](https://github.com/dotnet/corefx/blob/release/1.0.0/Documentation/building/unix-instructions.md) diff --git a/docs/dev-process/breaking-change-contract.md b/docs/dev-process/breaking-change-contract.md index 44950297c4c..12121b31dfe 100644 --- a/docs/dev-process/breaking-change-contract.md +++ b/docs/dev-process/breaking-change-contract.md @@ -4,34 +4,38 @@ We have a serious commitment to backwards compatibility with all earlier version Below is a summary of our approach to handing breaking changes including what kinds of things constitute breaking changes, how we categorize them, and how we decide what we're willing to take. -Note that these rules only apply to existing stable features that have shipped in a supported release. New features marked as “in preview” that are still under development may be modified from one preview release to the next. These are not considered breaking changes. +Note that these rules only apply to existing stable features that have shipped in a supported release. New features marked as “in preview” that are still under development may be modified from one preview release to the next. +These are **not** considered breaking changes. To help triage breaking changes, we classify them in to four buckets: 1. Public Contract -2. Reasonable Grey Area -3. Unlikely Grey Area -4. Clearly Non-Public +1. Reasonable Grey Area +1. Unlikely Grey Area +1. Clearly Non-Public ## Bucket 1: Public Contract Any change that is a clear violation of the public contract. -### Unacceptable changes: +### Unacceptable changes + A code change that results in a change to the existing behavior observed for a given input with an API, protocol or the PowerShell language. -+ Renaming or removing a public type, type member, or type parameter; renaming or removing a cmdlet or cmdlet parameter (note: it is possible to rename a cmdlet parameter if a parameter alias is added. This is an acceptable solution for PowerShell scripts but may break .NET code that depends on the name of the original member on the cmdlet object type.) ++ Renaming or removing a public type, type member, or type parameter; renaming or removing a cmdlet or cmdlet parameter (note: it is possible to rename a cmdlet parameter if a parameter alias is added. + +This is an acceptable solution for PowerShell scripts but may break .NET code that depends on the name of the original member on the cmdlet object type.) + + Decreasing the range of accepted values within a given parameter. + Changing the value of a public constant or enum member; changing the type of a cmdlet parameter to a more restrictive type. + Example: A cmdlet with a parameter -p1 that was previously type as [object] cannot be changed to be or a more restrictive type such as [int]. + Making an incompatible change to any protocol without increasing the protocol version number. -### Acceptable changes: +### Acceptable changes + Any existing behavior that results in an error message generally may be changed to provide new functionality. + A new instance field is added to a type (this impacts .NET serialization but not PowerShell serialization and so is considered acceptable.) + Adding new types, new type members and new cmdlets -+ Making changes to the protocols with a protocol version increment. Older versions of the protocol would still need to be maintained to allow communication with earlier systems. This would require that protocol negotiation take place between the two systems. In addition to any protocol code changes, the Microsoft Open Specification program requires that the formal protocol specification for a protocol be updated in a timely manner. An example of a MS protocol specification document (MS-PSRP) can be found at: https://msdn.microsoft.com/en-us/library/dd357801.aspx ++ Making changes to the protocols with a protocol version increment. Older versions of the protocol would still need to be maintained to allow communication with earlier systems. This would require that protocol negotiation take place between the two systems. In addition to any protocol code changes, the Microsoft Open Specification program requires that the formal protocol specification for a protocol be updated in a timely manner. An example of a MS protocol specification document (MS-PSRP) can be found at: https://msdn.microsoft.com/library/mt242417.aspx ## Bucket 2: Reasonable Grey Area @@ -44,7 +48,8 @@ Examples: + Change in parsing of input and throwing new errors (even if parsing behavior is not specified in the docs) + Example: a script may be using a JSON parser that is forgiving to minor syntactic errors in the JSON text. Changing that parser to be more rigorous in its processing would result in errors being thrown when no error was generated in the past thus breaking scripts. -Judiciously making changes in these type of features require judgement: how predictable, obvious, consistent was the behavior? In general, a significant external preview of the change would need to be done also possibly requiring an RFC to be created to allow for community input on the proposal. +Judiciously making changes in these type of features require judgement: how predictable, obvious, consistent was the behavior? +In general, a significant external preview of the change would need to be done also possibly requiring an RFC to be created to allow for community input on the proposal. ## Bucket 3: Unlikely Grey Area @@ -66,11 +71,14 @@ Changes to surface area or behavior that is clearly internal or non-breaking in Examples: -+ Changes to internal APIs that break private reflection ++ Changes to internal APIs that break private reflection. ++ Changes to APIs in the `System.Management.Automation.Internal` namespace (even if they are public, they are still considered internal and subject to change). ++ Renaming a parameter set (see related discussion [here](https://github.com/PowerShell/PowerShell/issues/10058)). -It is impossible to evolve a code base without making such changes, so we don't require up-front approval for these, but we will sometimes have to go back and revisit such change if there's too much pain inflicted on the ecosystem through a popular app or library. +It is impossible to evolve a code base without making such changes, so we don't require up-front approval for these, but we will sometimes have to go back and +revisit such change if there's too much pain inflicted on the ecosystem through a popular app or library. -# What This Means for Contributors +## What This Means for Contributors + All bucket 1, 2, and 3 breaking changes require contacting team at @powershell/powershell. + If you're not sure into which bucket a given change falls, contact us as well. @@ -78,4 +86,3 @@ It is impossible to evolve a code base without making such changes, so we don't + If a change is deemed too breaking, we can help identify alternatives such as introducing a new API or cmdlet and obsoleting the old one. Request for clarifications or suggested alterations to this document should be done by opening issues against this document. - diff --git a/docs/dev-process/coding-guidelines.md b/docs/dev-process/coding-guidelines.md index 74e25cbf140..ef0f671c1a0 100644 --- a/docs/dev-process/coding-guidelines.md +++ b/docs/dev-process/coding-guidelines.md @@ -1,92 +1,233 @@ - -# C# Coding Style +# C# Coding Guidelines ## Coding Conventions As a general rule, our coding convention is to follow the style of the surrounding code. -Avoid reformatting any code when submitting a PR as it obscures the functional changes of your change. -We run the [.NET code formatter tool](https://github.com/dotnet/codeformatter) regularly help keep consistent formatting. +So if a file happens to differ in style from conventions defined here +(e.g. private members are named `m_member` rather than `_member`), +the existing style in that file takes precedence. + +When making changes, you may find some existing code goes against the conventions defined here. +In such cases, please avoid reformatting any existing code when submitting a PR as it obscures the functional changes of the PR. +A separate PR should be submitted for style-only changes. +We also run the [.NET code formatter tool](https://github.com/dotnet/codeformatter) regularly to keep consistent formatting. + +### Naming Conventions + +* Use meaningful, descriptive words for names. + For method names, it's encouraged to use `VerbObject` pair such as **`LoadModule`**. + +* Use `_camelCase` to name internal and private fields and use `readonly` where possible. + Prefix instance fields with `_`, static fields with `s_` and thread static fields with `t_`. + When used on static fields, `readonly` should come after `static` (i.e. `static readonly` not `readonly static`). + +* Use `camelCase` to name non-constant local variables. + +* Use `PascalCase` to name constant local variables and fields. + The only exception is for interop code where the constant should exactly match the name and value of the code you are calling via interop (e.g. `const int ERROR_SUCCESS = 0`). -A basic rule of formatting is to use "Visual Studio defaults". -Here are some general guidelines +* Use `PascalCase` to name types and all other type members. -* No tabs, indent 4 spaces. -* Braces usually go on their own line, +### Layout Conventions + +* Use four spaces of indentation (no tabs). + +* Avoid more than one blank empty line at any time. + +* Avoid trailing spaces at the end of a line. + +* Braces usually go on their own lines, with the exception of single line statements that are properly indented. -* Use `_camelCase` for instance fields, - use `readonly` where possible. + +* Namespace imports should be specified at the top of the file, + outside of `namespace` declarations. + +* Fields should be specified at the top within type declarations. + For those that serve as backing fields for properties, + they should be specified next to the corresponding properties. + +* Preprocessor directives like `#if` and `#endif` should be placed at the beginning of a line, + without any leading spaces. + +* File encoding should be `ASCII`. + All `BOM` encodings should be avoided. + Tests that need a `BOM` encoding file should generate the file on the fly. + +### Member Conventions + * Use of `this` is neither encouraged nor discouraged. -* Avoid more than one blank empty line. -* Public members should use [doc comments](https://msdn.microsoft.com/en-us/library/b2s063f7.aspx), - internal members may use doc comments but it is not encouraged. + +* Use `nameof()` instead of `""` whenever possible and relevant. + The motivation is to easily and more accurately find references. + +* Always specify the visibility, even if it's the default (i.e. `private string _foo` not `string _foo`). + Visibility should be the first modifier (i.e. `public abstract` not `abstract public`). + +* Make members private where possible. + Avoid declaring public members unless it's absolutely necessary. + * Public members in a namespace that ends with `Internal`, for example `System.Management.Automation.Internal` are not considered a supported public API. Such members are necessarily public as implementation details in code shared between C# and PowerShell script, or must be available publicly by generated code. -* File encoding should be ASCII (preferred) - or UTF8 (with BOM) if absolutely necessary. -## Preprocessor defines +### Commenting Conventions -There are 3 primary preprocessor macros we define during builds: +* Place the comment on a separate line, not at the end of a line of code. -* DEBUG - guard code that should not be included in release builds -* CORECLR - guard code that differs between Full CLR and CoreCLR -* UNIX - guard code that is specific to Unix (Linux and macOS) +* Begin comment text with an uppercase letter. + It's recommended to end comment text with a period but not required. -Any other preprocessor defines found in the source are used for one-off custom builds, -typically to help debug specific scenarios. +* Add comments where the code is not trivial or could be confusing. + +* Add comments where a reviewer needs help to understand the code. -### Runtimes +* Update/remove existing comments when you are changing the corresponding code. -The PowerShell repo is used to build PowerShell targeting CoreCLR as well as CLR 4.5. +* Make sure the added/updated comments are meaningful, accurate and easy to understand. -Code under !CORECLR must build against CLR 4.5. -We will not accept changes that require a later version of the full CLR. -In extremely rare cases, we may use reflection to use an API in a later version of the CLR, -but the feature must robustly handle running with CLR 4.5. +### Documentation comments -We may reject code under !CORECLR without explanation because -we do not support installation or testing of such code in this repo. -All new features should support CoreCLR. +* Create documentation using [XML documentation comments](https://learn.microsoft.com/dotnet/csharp/language-reference/xmldoc/) so that Visual Studio and other IDEs can use IntelliSense to show quick information about types or members. -## Performance considerations +* Publicly visible types and their members must be documented. + Internal and private members may use doc comments but it is not required. + +* Documentation text should be written using complete sentences ending with full stops. + +## Performance Considerations PowerShell has a lot of performance sensitive code as well as a lot of inefficient code. -We have some guidelines that we typically apply widely even in less important code -because code and patterns are copied we want certain inefficient code to stay out of the performance critical code. +We have some guidelines that we typically apply widely even in less important code because code and patterns are copied, +and we want certain inefficient code to stay out of the performance critical code. Some general guidelines: -* Avoid LINQ - it can create lots of avoidable garbage -* Prefer `for` and `foreach`, - with a slight preference towards `for` when you're uncertain if `foreach` allocates an iterator. +* Avoid LINQ - it can create lots of avoidable garbage. + Instead, iterate through a collection directly using `for` or `foreach` loop. + +* Between `for` and `foreach`, + `for` is slightly preferred when you're uncertain if `foreach` allocates an iterator. + * Avoid `params` arrays, prefer adding overloads with 1, 2, 3, and maybe more parameters. + * Be aware of APIs such as `String.Split(params char[])` that do not provide overloads to avoid array allocation. - When calling such APIs, reuse a static array when possible. + When calling such APIs, reuse a static array when possible (e.g. `Utils.Separators.Colon`). + +* Avoid using string interpolations and overloads with implicit parameters such as `Culture` and `StringComparison`. + Instead, use overloads with more explicit parameters such as `String.Format(IFormatProvider, String, Object[])` and `Equals(String, String, StringComparison)`. + * Avoid unnecessary memory allocation in a loop. Move the memory allocation outside the loop if possible. -## Portable code +* Avoid gratuitous exceptions as much as possible. + Exception handling can be expensive due to cache misses and page faults when accessing the handling code and data. + Finding and designing away exception-heavy code can result in a decent performance win. + For example, you should stay away from things like using exceptions for control flow. + +* Avoid `if (obj is Example) { example = (Example)obj }` when casting an object to a type. + Instead, use `var example = obj as Example` or the C# 7 syntax `if (obj is Example example) {...}` as appropriate. + In this way you can avoid converting to the type twice. + +* Use generic collections instead of the non-generic ones such as `ArrayList` and `Hashtable` to avoid type casting and unnecessary boxing whenever possible. + +* Use collection constructor overloads that take an initial capacity for collection types that have them. + Internally, `List`, `Dictionary`, + and the other generic collections use one or more arrays to hold valid data. + Whenever resizing is needed, + one or more new arrays double the size of existing arrays are created and items from the existing arrays are copied. + Setting an approximate initial capacity will reduce the number of resizing operations. + +* Use `dict.TryGetValue` instead of `dict.Contains` and `dict[..]` when retrieving value from a `Dictionary`. + In this way you can avoid hashing the key twice. + +* It's OK to use the `+` operator to concatenate one-off short strings. + But when dealing with strings in loops or large amounts of text, + use a `StringBuilder` object. + +## Security Considerations + +Security is an important aspect of PowerShell and we need to be very careful about changes that may introduce security risks, +such as code injection caused by the lack of input validation, +privilege escalation due to the misuse of impersonation, +or data privacy breach with a plain text password. + +Reviewers of a PR should be sensitive to changes that may affect security. +Some security related keywords may serve as good indicators, +such as `password`, `crypto`, `encryption`, `decryption`, `certificate`, `authenticate`, `ssl/tls` and `protected data`. + +When facing a PR with such changes, +the reviewers should request a designated security Subject Matter Expert (SME) to review the PR. +Currently, [@PaulHigin](https://github.com/PaulHigin) and [@TravisEz13](https://github.com/TravisEz13) are our security SMEs. +See [CODEOWNERS](../../.github/CODEOWNERS) for more information about the area experts. + +## Best Practices -The PowerShell code base started on Windows and depends on many Win32 APIs through P/Invoke. -Going forward, we try to depend on CoreCLR to handle platform differences, -so avoid adding new P/Invoke calls where a suitable alternative exists in .NET. +* Avoid hard-coding anything unless it's absolutely necessary. + +* Avoid a method that is too long and complex. + In such case, separate it to multiple methods or even a nested class as you see fit. + +* Use the `using` statement instead of `try/finally` if the only code in the `finally` block is to call the `Dispose` method. + +* Use of object initializers (e.g. `new Example { Name = "Name", ID = 1 }`) is encouraged for better readability, + but not required. + +* Stick to the `DRY` principle -- Don't Repeat Yourself. + * Wrap the commonly used code in methods, + or even put it in a utility class if that makes sense, + so that the same code can be reused (e.g. `StringToBase64Converter.Base64ToString(string)`). + * Check if the code for the same purpose already exists in the code base before inventing your own wheel. + * Avoid repeating literal strings in code. Instead, use `const` variable to hold the string. + * Resource strings used for errors or UI should be put in resource files (`.resx`) so that they can be localized later. + +* Use of new C# language syntax is encouraged. + But avoid refactoring any existing code using new language syntax when submitting a PR + as it obscures the functional changes of the PR. + A separate PR should be submitted for such refactoring without any functional changes. + +* Consider using the `Interlocked` class instead of the `lock` statement to atomically change simple states. The `Interlocked` class provides better performance for updates that must be atomic. + +* Here are some useful links for your reference: + * [Framework Design Guidelines](https://learn.microsoft.com/dotnet/standard/design-guidelines/index) - Naming, Design and Usage guidelines including: + * [Arrays](https://learn.microsoft.com/dotnet/standard/design-guidelines/arrays) + * [Collections](https://learn.microsoft.com/dotnet/standard/design-guidelines/guidelines-for-collections) + * [Exceptions](https://learn.microsoft.com/dotnet/standard/design-guidelines/exceptions) + * [Best Practices for Developing World-Ready Applications](https://learn.microsoft.com/dotnet/core/extensions/best-practices-for-developing-world-ready-apps) - Unicode, Culture, Encoding and Localization. + * [Best Practices for Exceptions](https://learn.microsoft.com/dotnet/standard/exceptions/best-practices-for-exceptions) + * [Best Practices for Using Strings in .NET](https://learn.microsoft.com/dotnet/standard/base-types/best-practices-strings) + * [Best Practices for Regular Expressions in .NET](https://learn.microsoft.com/dotnet/standard/base-types/best-practices) + * [Serialization Guidelines](https://learn.microsoft.com/dotnet/standard/serialization/serialization-guidelines) + * [Managed Threading Best Practices](https://learn.microsoft.com/dotnet/standard/threading/managed-threading-best-practices) + +## Portable Code + +There are 3 primary preprocessor macros we use during builds: + +* `DEBUG` - guard code that should not be included in release builds +* `CORECLR` - guard code that differs between Full CLR and CoreCLR +* `UNIX` - guard code that is specific to Unix (Linux and macOS) + +Any other preprocessor defines found in the source are used for one-off custom builds, +typically to help debug specific scenarios. -Try to minimize the use of `#if UNIX` and `#if CORECLR`. -When absolutely necessary, avoid duplicating more code than necessary, -and instead prefer introducing helper functions to minimize the platform differences. +Here are some general guidelines for writing portable code: -When adding platform dependent code, prefer preprocessor directives -over runtime checks. +* We are in the process of cleaning up Full CLR specific code (code enclosed in `!CORECLR`), + so do not use `CORECLR` or `!CORECLR` in new code. + PowerShell Core targets .NET Core only and all new changes should support .NET Core only. -We produce a single binary for all UNIX variants, -so runtime checks are currently necessary for some platform differences, e.g. macOS and Linux. +* The PowerShell code base started on Windows and depends on many Win32 APIs through P/Invoke. + Going forward, we try to depend on .NET Core to handle platform differences, + so avoid adding new P/Invoke calls where a suitable alternative exists in .NET Core. -## Code comments +* Try to minimize the use of `#if UNIX`. + When absolutely necessary, avoid duplicating more code than necessary, + and instead prefer introducing helper functions to minimize the platform differences. -It's strongly encouraged to add comments when you are making changes to the code and tests, -especially when the changes are not trivial or may raise confusion. -Make sure the added comments are accurate and easy to understand. -Good code comments would greatly improve readability of the code, and make it much more maintainable. +* When adding platform dependent code (`Windows` vs. `UNIX`), prefer preprocessor directives over runtime checks. + However, runtime checks are acceptable if it would greatly improve readability + without causing performance concerns in performance-sensitive code. +* We produce a single binary for all UNIX variants, + so runtime checks are currently necessary for some of them (e.g. macOS vs. Linux). diff --git a/docs/dev-process/resx-files.md b/docs/dev-process/resx-files.md index 61bcfa4277d..ec9eace54fe 100644 --- a/docs/dev-process/resx-files.md +++ b/docs/dev-process/resx-files.md @@ -9,33 +9,32 @@ We are using our own `Start-ResGen` to generate them. Usually it's called as part of the regular build with -``` -PS C:\> Start-PSBuild -ResGen +```powershell +Start-PSBuild -ResGen ``` If you see compilation errors related to resources, try to call `Start-ResGen` explicitly. -``` -PS C:\> Start-ResGen +```powershell +Start-ResGen ``` ## Editing `.resx` files -**Don't edit** `.resx` files from Visual Studio. +**Don't edit** `.resx` files from Visual Studio. It will try to create `.cs` files for you and you will get whole bunch of hard-to-understand errors. -To edit a resource file, use any **plain text editor**. +To edit a resource file, use any **plain text editor**. A resource file is a simple XML file, and it's easy to edit. - ## Convert `.txt` resource files into `.resx` files `dotnet cli` doesn't support embedding old-fashioned `.txt` resource. You can do a one-time conversion of `.txt` resources into `.resx` files with a helper function: -``` +```powershell # example, converting all .txt resources under src\Microsoft.WSMan.Management\resources -PS C:\> Convert-TxtResourceToXml -Path src\Microsoft.WSMan.Management\resources +Convert-TxtResourceToXml -Path src\Microsoft.WSMan.Management\resources ``` `.resx` files would be placed next to `.txt` files. diff --git a/docs/git/README.md b/docs/git/README.md index b896490aae3..46b5eee4c62 100644 --- a/docs/git/README.md +++ b/docs/git/README.md @@ -1,50 +1,34 @@ -Working with PowerShell repository -================================== +# Working with PowerShell repository -#### Get the code for the first time +## Get the code for the first time ```sh -git clone --recursive https://github.com/PowerShell/PowerShell +git clone https://github.com/PowerShell/PowerShell.git --branch=master ``` -The PowerShell repository has **submodules**. -They are required to build and test PowerShell. -That's why you need `--recursive`, when you `git clone`. - -If you already cloned the repo without `--recursive`, update submodules manually - -```sh -git submodule init -git submodule update -``` - -See [FAQ](../FAQ.md#why-is-my-submodule-empty) for details. - - -Branches ---------- +## Branches * Don't commit your changes directly to master. It will make the collaborative workflow messy. * Checkout a new local branch from `master` for every change you want to make (bugfix, feature). * Use lowercase-with-dashes for naming. * Follow [Linus' recommendations][Linus] about history. - - "People can (and probably should) rebase their _private_ trees (their own work). That's a _cleanup_. But never other peoples code. That's a 'destroy history'... - You must never EVER destroy other peoples history. You must not rebase commits other people did. - Basically, if it doesn't have your sign-off on it, it's off limits: you can't rebase it, because it's not yours." + * "People can (and probably should) rebase their _private_ trees (their own work). That's a _cleanup_. But never other peoples code. That's a 'destroy history'... + You must never EVER destroy other peoples history. You must not rebase commits other people did. + Basically, if it doesn't have your sign-off on it, it's off limits: you can't rebase it, because it's not yours." -#### Understand branches +### Understand branches * **master** is the branch with the latest and greatest changes. It could be unstable. * Send your pull requests to **master**. -#### Sync your local repo +### Sync your local repository Use **git rebase** instead of **git merge** and **git pull**, when you're updating your feature-branch. ```sh -# fetch updates all remote branch references in the repo and all submodules +# fetch updates all remote branch references in the repository # --all : tells it to do it for all remotes (handy, when you use your fork) # -p : tells it to remove obsolete remote branch references (when they are removed from remote) git fetch --all -p @@ -53,65 +37,63 @@ git fetch --all -p git rebase origin/master ``` -#### More complex scenarios +### More complex scenarios Covering all possible git scenarios is behind the scope of the current document. Git has excellent documentation and lots of materials available online. -We are leaving few links here: +We are leaving a few links here: -[Git pretty flowchart](http://justinhileman.info/article/git-pretty/): what to do, when your local repo became a mess. +[Linus]:https://web.archive.org/web/20230522041845/https://wincent.com/wiki/git_rebase%3A_you're_doing_it_wrong -[Linus]:http://thread.gmane.org/gmane.comp.video.dri.devel/34739/focus=34744 - - -Tags ------- +## Tags If you are looking for the source code for a particular release, you will find it via **tags**. -* `git tag` will show you list of all tags. +* `git tag` will show you list of all tags. * Find the tag that corresponds to the release. * Use `git checkout ` to get this version. -**Note:** [checking out a tag][tag] will move the repo to a [DETACHED HEAD][HEAD] state. +**Note:** [checking out a tag][tag] will move the repository to a [DETACHED HEAD][HEAD] state. [tag]:https://git-scm.com/book/en/v2/Git-Basics-Tagging#Checking-out-Tags [HEAD]:https://www.git-tower.com/learn/git/faq/detached-head-when-checkout-commit -If you want to make changes, based on tag's version (i.e. a hotfix), +If you want to make changes, based on tag's version (i.e. a hotfix), checkout a new branch from this DETACHED HEAD state. ```sh git checkout -b vors/hotfix ``` - -Recommended Git configurations -============================== +## Recommended Git configurations We highly recommend these configurations to help deal with whitespace, rebasing, and general use of Git. > Auto-corrects your command when it's sure (`stats` to `status`) + ```sh git config --global help.autoCorrect -1 ``` > Refuses to merge when pulling, and only pushes to branch with same name. + ```sh git config --global pull.ff only git config --global push.default current ``` > Shows shorter commit hashes and always shows reference names in the log. + ```sh git config --global log.abbrevCommit true git config --global log.decorate short ``` > Ignores whitespace changes and uses more information when merging. + ```sh git config --global apply.ignoreWhitespace change git config --global rerere.enabled true diff --git a/docs/git/basics.md b/docs/git/basics.md index f1fcb6939c7..aa7629cf746 100644 --- a/docs/git/basics.md +++ b/docs/git/basics.md @@ -1,14 +1,12 @@ -Getting started with Git -======================== +# Getting started with Git We are using Git version 2.9.0, but any recent version should be good. It's recommended to learn the `git` command-line tool for full cross-platform experience and a deeper understanding of Git itself. -Install ---------- +## Install -#### Windows +### Windows Install [Git for Windows][]. @@ -22,16 +20,12 @@ During the installation process, choose these recommended settings: [Git for Windows]: https://git-scm.com/download/win -#### Linux +### Linux -Install by using the package manager: +Install by using the package manager on your system. +A list of all the package managers and commands can be found [here][linux-git-dl]. -```sh -sudo apt-get install git -``` - -Interactive tutorials ----------------------- +### Interactive tutorials There are (too) many Git tutorials on the internet. Here we post references to our favorites. @@ -46,11 +40,6 @@ changes, and issue a pull request. [Hello World]: https://guides.github.com/activities/hello-world/ -#### Katacoda - -Learn basic Git scenarios in the browser with interactive labs. -[Git lessons on katacoda](https://www.katacoda.com/courses/git/). - #### Githug [Githug](https://github.com/Gazler/githug) is a great gamified way to @@ -58,11 +47,10 @@ learn Git in couple hours. After finishing 50+ real-world scenarios you will have a pretty good idea about what and when you can do with Git. +## Authentication -Authentication --------------- +### Windows -#### Windows On Windows, the best way to use Git securely is [Git Credential Manager for Windows][manager]. It's included in the official Git installer for Windows. @@ -78,13 +66,12 @@ git config --global credential.helper store Alternatively, you can use [SSH key][]. In this case, you may want to use git-ssh even for HTTPS Git URLs. -It will help you to use submodules transparently. -``` +```none git config --global url.git@github.com:.insteadOf https://github.com/ ``` - [SSH key]: https://help.github.com/articles/generating-a-new-ssh-key-and-adding-it-to-the-ssh-agent/#generating-a-new-ssh-key [token]: https://help.github.com/articles/creating-an-access-token-for-command-line-use/ [manager]: https://github.com/Microsoft/Git-Credential-Manager-for-Windows +[linux-git-dl]: https://git-scm.com/download/linux diff --git a/docs/git/submodules.md b/docs/git/submodules.md deleted file mode 100644 index 7464a1ef86c..00000000000 --- a/docs/git/submodules.md +++ /dev/null @@ -1,28 +0,0 @@ -Submodules -========== - -While most developers will not have to deal with submodules on a regular basis, those who do should read this information. -The submodules currently in this project are: - -- `src/Modules/Pester`: The Pester testing framework for PowerShell - -- `src/libpsl-native/test/googletest`: The GoogleTest framework for - Linux native code - -[submodules]: https://www.git-scm.com/book/en/v2/Git-Tools-Submodules - -Rebase and Fast-Forward Merge Pull Requests in Submodules -========================================================= - -*This is not necessary in the superproject, only submodules!* - -**DO NOT** commit updates unless absolutely necessary. -When submodules must be updated, a separate Pull Request must be submitted, reviewed, and merged before updating the superproject. - -Because GitHub's "Merge Pull Request" button merges with `--no-ff`, an extra merge commit will always be created. -This is especially annoying when trying to commit updates to submodules. -Therefore our policy is to merge using the Git CLI after approval, with a rebase onto master to enable a fast-forward merge. - -When committing submodule updates, ensure no other changes are in the same commit. -Submodule bumps may be included in feature branches for ease of work, -but the update must be independently approved before merging into master. diff --git a/docs/host-powershell/README.md b/docs/host-powershell/README.md index f1b212d6164..174f986dfa4 100644 --- a/docs/host-powershell/README.md +++ b/docs/host-powershell/README.md @@ -1,153 +1,26 @@ # Host PowerShell Core in .NET Core Applications -## PowerShell Core v6.0.0-beta.3 and Later - -PowerShell Core is refactored in v6.0.0-beta.3 to remove the dependency on a customized `AssemblyLoadContext`. -With this change, hosting PowerShell Core in .NET Core will be the same as hosting Windows PowerShell in .NET. - -Please see the [.NET Core Sample Application](#.net-core-sample-application) section for an example that uses PowerShell Core `beta.3` NuGet packages. - -## PowerShell Core v6.0.0-beta.2 and Prior - -### Overview - -Due to the lack of necessary APIs for manipulating assemblies in .NET Core 1.1 and prior, -PowerShell Core needs to control assembly loading via our customized `AssemblyLoadContext` ([CorePsAssemblyLoadContext.cs][]) in order to do tasks like type resolution. -So applications that want to host PowerShell Core (using PowerShell APIs) need to be bootstrapped from `PowerShellAssemblyLoadContextInitializer`. - -`PowerShellAssemblyLoadContextInitializer` exposes 2 APIs for this purpose: -`SetPowerShellAssemblyLoadContext` and `InitializeAndCallEntryMethod`. -They are for different scenarios: - -- `SetPowerShellAssemblyLoadContext` - It's designed to be used by a native host - whose Trusted Platform Assemblies (TPA) do not include PowerShell assemblies, - such as the in-box `powershell.exe` and other native CoreCLR host in Nano Server. - When using this API, instead of setting up a new load context, - `PowerShellAssemblyLoadContextInitializer` will register a handler to the [Resolving][] event of the default load context. - Then PowerShell Core will depend on the default load context to handle TPA and the `Resolving` event to handle other assemblies. - -- `InitializeAndCallEntryMethod` - It's designed to be used with `dotnet.exe` - where the TPA list includes PowerShell assemblies. - When using this API, `PowerShellAssemblyLoadContextInitializer` will set up a new load context to handle all assemblies. - PowerShell Core itself also uses this API for [bootstrapping][]. - -This documentation only covers the `InitializeAndCallEntryMethod` API, -as it's what you need when building a .NET Core application with .NET CLI. - -### Comparison - Hosting Windows PowerShell vs. Hosting PowerShell Core - -The following code demonstrates how to host Windows PowerShell in an application. -As shown below, you can insert your business logic code directly in the `Main` method. - -```CSharp -// MyApp.exe -using System; -using System.Management.Automation; - -public class Program -{ - static void Main(string[] args) - { - // My business logic code - using (PowerShell ps = PowerShell.Create()) - { - var results = ps.AddScript("Get-Command Write-Output").Invoke(); - Console.WriteLine(results[0].ToString()); - } - } -} -``` +## PowerShell Core v6.0.1 and Later -However, when it comes to hosting PowerShell Core, there will be a layer of redirection for the PowerShell load context to take effect. -In a .NET Core application, the entry point assembly that contains the `Main` method is loaded in the default load context, -and thus all assemblies referenced by the entry point assembly, implicitly or explicitly, will also be loaded into the default load context. - -In order to have the PowerShell load context to control assembly loading for the execution of an application, -the business logic code needs to be extracted out of the entry point assembly and put into a different assembly, say `Logic.dll`. -The entry point `Main` method shall do one thing only -- let the PowerShell load context load `Logic.dll` and start the execution of the business logic. -Once the execution starts this way, all further assembly loading requests will be handled by the PowerShell load context. - -So the above example needs to be altered as follows in a .NET Core application: - -```CSharp -// MyApp.exe -using System.Management.Automation; -using System.Reflection; - -namespace Application.Test -{ - public class Program - { - /// - /// Managed entry point shim, which starts the actual program - /// - public static int Main(string[] args) - { - // Application needs to use PowerShell AssemblyLoadContext if it needs to create PowerShell runspace - // PowerShell engine depends on PS ALC to provide the necessary assembly loading/searching support that is missing from .NET Core - string appBase = System.IO.Path.GetDirectoryName(typeof(Program).GetTypeInfo().Assembly.Location); - System.Console.WriteLine("\nappBase: {0}", appBase); - - // Initialize the PS ALC and let it load 'Logic.dll' and start the execution - return (int)PowerShellAssemblyLoadContextInitializer. - InitializeAndCallEntryMethod( - appBase, - new AssemblyName("Logic, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"), - "Application.Test.Logic", - "Start", - new object[] { args }); - } - } -} - -// Logic.dll -using System; -using System.Management.Automation; -using System.Management.Automation.Runspaces; - -namespace Application.Test -{ - public sealed class Logic - { - /// - /// Start the actual logic - /// - public static int Start(string[] args) - { - // My business logic code - using (PowerShell ps = PowerShell.Create()) - { - var results = ps.AddScript("Get-Command Write-Output").Invoke(); - Console.WriteLine(results[0].ToString()); - } - return 0; - } - } -} -``` +The runtime assemblies for Windows, Linux and OSX are now published in NuGet package version 6.*. -[CorePsAssemblyLoadContext.cs]: https://github.com/PowerShell/PowerShell/blob/v6.0.0-alpha.17/src/Microsoft.PowerShell.CoreCLR.AssemblyLoadContext/CoreCLR/CorePsAssemblyLoadContext.cs -[Resolving]: https://github.com/dotnet/corefx/blob/ec2a6190efa743ab600317f44d757433e44e859b/src/System.Runtime.Loader/ref/System.Runtime.Loader.cs#L35 -[bootstrapping]: https://github.com/PowerShell/PowerShell/blob/master/src/powershell/Program.cs#L27 +Please see the [.NET Core Sample Application](#net-core-sample-application) section for an example that uses PowerShell Core NuGet packages. ## .NET Core Sample Application -- [sample-dotnet1.1](./sample-dotnet1.1) - .NET Core `1.1` + PowerShell Core `alpha.17` NuGet packages. - [.NET Core SDK 1.0.1](https://github.com/dotnet/cli/releases/tag/v1.0.1) is required. -- [sample-dotnet2.0-powershell.beta.1](./sample-dotnet2.0-powershell.beta.1) - .NET Core `2.0.0` + PowerShell Core `beta.1` NuGet packages. - .NET Core SDK `2.0.0-preview1-005952` or higher is required. -- [sample-dotnet2.0-powershell.beta.3](./sample-dotnet2.0-powershell.beta.3) - .NET Core `2.0.0` + PowerShell Core `beta.3` NuGet packages. - .NET Core SDK `2.0.0-preview1-005952` or higher is required. +Note: The .NET Core `2.1` runtime and .NET Core SDK `2.1` or higher is required for the examples below: -You can find the sample application project `"MyApp"` in each of the above 3 sample folders. -To build the sample project, run the following commands (make sure the required .NET Core SDK is in use): +- [sample](./sample) + +You can find the sample application project `MyApp` in each of the above 2 sample folders. You can quickly test-run it using `dotnet run`. +To build the sample project properly for distribution, run the following command (make sure the required .NET Core SDK is in use): ```powershell -dotnet restore .\MyApp\MyApp.csproj -dotnet publish .\MyApp -c release -r win10-x64 +dotnet publish .\MyApp --configuration release ``` -Then you can run `MyApp.exe` from the publish folder and see the results: +This builds it for the runtimes specified by the `RuntimeIdentifiers` property in the `.csproj` file. +Then you can run the `MyApp` binary from the publish folder and see the results: ```none PS:> .\MyApp.exe @@ -162,8 +35,13 @@ System.Management.Automation.ActionPreference System.Management.Automation.AliasAttribute ``` -## Remaining Issue +## Special Hosting Scenario For Native Host + +There is a special hosting scenario for native hosts, +where Trusted Platform Assemblies (TPA) do not include PowerShell assemblies, +such as the in-box `powershell.exe` in Nano Server and the Azure DSC host. -PowerShell Core builds separately for Windows and Unix, so the assemblies are different between Windows and Unix platforms. -Unfortunately, all PowerShell NuGet packages that have been published so far only contain PowerShell assemblies built specifically for Windows. -The issue [#3417](https://github.com/PowerShell/PowerShell/issues/3417) was opened to track publishing PowerShell NuGet packages for Unix platforms. +For such hosting scenarios, the native host needs to bootstrap by calling [`PowerShellAssemblyLoadContextInitializer.SetPowerShellAssemblyLoadContext`](https://learn.microsoft.com/dotnet/api/system.management.automation.powershellassemblyloadcontextinitializer.setpowershellassemblyloadcontext). +When using this API, the native host can pass in the path to the directory that contains PowerShell assemblies. +A handler will then be registered to the [`Resolving`](https://github.com/dotnet/corefx/blob/d6678e9653defe3cdfff26b2ff62135b6b22c77f/src/System.Runtime.Loader/ref/System.Runtime.Loader.cs#L38) +event of the default load context to deal with the loading of assemblies from that directory. diff --git a/docs/host-powershell/sample-dotnet1.1/Logic/Logic.csproj b/docs/host-powershell/sample-dotnet1.1/Logic/Logic.csproj deleted file mode 100644 index dc8816bca50..00000000000 --- a/docs/host-powershell/sample-dotnet1.1/Logic/Logic.csproj +++ /dev/null @@ -1,16 +0,0 @@ - - - - netstandard1.6 - Logic - win10-x64 - $(PackageTargetFallback);dnxcore50;portable-net45+win8 - - - - - - - - - diff --git a/docs/host-powershell/sample-dotnet1.1/Logic/UseRunspace.cs b/docs/host-powershell/sample-dotnet1.1/Logic/UseRunspace.cs deleted file mode 100644 index b13a08c244a..00000000000 --- a/docs/host-powershell/sample-dotnet1.1/Logic/UseRunspace.cs +++ /dev/null @@ -1,35 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ -using System; -using System.Management.Automation; -using System.Management.Automation.Runspaces; - -namespace Application.Test -{ - public sealed class Logic - { - /// - /// Start the actual logic - /// - public static int Start(string[] args) - { - using (PowerShell ps = PowerShell.Create()) - { - Console.WriteLine("\nEvaluating 'Get-Command Write-Output' in PS Core Runspace\n"); - var results = ps.AddScript("Get-Command Write-Output").Invoke(); - Console.WriteLine(results[0].ToString()); - - ps.Commands.Clear(); - - Console.WriteLine("\nEvaluating '([S.M.A.ActionPreference], [S.M.A.AliasAttribute]).FullName' in PS Core Runspace\n"); - results = ps.AddScript("([System.Management.Automation.ActionPreference], [System.Management.Automation.AliasAttribute]).FullName").Invoke(); - foreach (dynamic result in results) - { - Console.WriteLine(result.ToString()); - } - } - return 0; - } - } -} diff --git a/docs/host-powershell/sample-dotnet1.1/MyApp/MyApp.csproj b/docs/host-powershell/sample-dotnet1.1/MyApp/MyApp.csproj deleted file mode 100644 index 012db33cc60..00000000000 --- a/docs/host-powershell/sample-dotnet1.1/MyApp/MyApp.csproj +++ /dev/null @@ -1,17 +0,0 @@ - - - - netcoreapp1.1 - MyApp - Exe - win10-x64 - $(PackageTargetFallback);dnxcore50;portable-net45+win8 - 1.1.1 - - - - - - - - diff --git a/docs/host-powershell/sample-dotnet1.1/MyApp/Program.cs b/docs/host-powershell/sample-dotnet1.1/MyApp/Program.cs deleted file mode 100644 index f0fa6b5fedf..00000000000 --- a/docs/host-powershell/sample-dotnet1.1/MyApp/Program.cs +++ /dev/null @@ -1,32 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -using System.Management.Automation; -using System.Reflection; - -namespace Application.Test -{ - public class Program - { - /// - /// Managed entry point shim, which starts the actual program - /// - public static int Main(string[] args) - { - // Application needs to use PowerShell AssemblyLoadContext if it needs to create powershell runspace - // PowerShell engine depends on PS ALC to provide the necessary assembly loading/searching support that is missing from .NET Core - string appBase = System.IO.Path.GetDirectoryName(typeof(Program).GetTypeInfo().Assembly.Location); - System.Console.WriteLine("\nappBase: {0}", appBase); - - // Initialize the PS ALC and let it load 'Logic.dll' and start the execution - return (int)PowerShellAssemblyLoadContextInitializer. - InitializeAndCallEntryMethod( - appBase, - new AssemblyName("Logic, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"), - "Application.Test.Logic", - "Start", - new object[] { args }); - } - } -} diff --git a/docs/host-powershell/sample-dotnet1.1/NuGet.config b/docs/host-powershell/sample-dotnet1.1/NuGet.config deleted file mode 100644 index 58f8d2c9b6d..00000000000 --- a/docs/host-powershell/sample-dotnet1.1/NuGet.config +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/docs/host-powershell/sample-dotnet2.0-powershell.beta.1/Logic/Logic.csproj b/docs/host-powershell/sample-dotnet2.0-powershell.beta.1/Logic/Logic.csproj deleted file mode 100644 index 0d32ce0bbfc..00000000000 --- a/docs/host-powershell/sample-dotnet2.0-powershell.beta.1/Logic/Logic.csproj +++ /dev/null @@ -1,15 +0,0 @@ - - - - netcoreapp2.0 - Logic - win10-x64 - - - - - - - - - diff --git a/docs/host-powershell/sample-dotnet2.0-powershell.beta.1/Logic/UseRunspace.cs b/docs/host-powershell/sample-dotnet2.0-powershell.beta.1/Logic/UseRunspace.cs deleted file mode 100644 index b13a08c244a..00000000000 --- a/docs/host-powershell/sample-dotnet2.0-powershell.beta.1/Logic/UseRunspace.cs +++ /dev/null @@ -1,35 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ -using System; -using System.Management.Automation; -using System.Management.Automation.Runspaces; - -namespace Application.Test -{ - public sealed class Logic - { - /// - /// Start the actual logic - /// - public static int Start(string[] args) - { - using (PowerShell ps = PowerShell.Create()) - { - Console.WriteLine("\nEvaluating 'Get-Command Write-Output' in PS Core Runspace\n"); - var results = ps.AddScript("Get-Command Write-Output").Invoke(); - Console.WriteLine(results[0].ToString()); - - ps.Commands.Clear(); - - Console.WriteLine("\nEvaluating '([S.M.A.ActionPreference], [S.M.A.AliasAttribute]).FullName' in PS Core Runspace\n"); - results = ps.AddScript("([System.Management.Automation.ActionPreference], [System.Management.Automation.AliasAttribute]).FullName").Invoke(); - foreach (dynamic result in results) - { - Console.WriteLine(result.ToString()); - } - } - return 0; - } - } -} diff --git a/docs/host-powershell/sample-dotnet2.0-powershell.beta.1/MyApp/MyApp.csproj b/docs/host-powershell/sample-dotnet2.0-powershell.beta.1/MyApp/MyApp.csproj deleted file mode 100644 index 6bdba82519b..00000000000 --- a/docs/host-powershell/sample-dotnet2.0-powershell.beta.1/MyApp/MyApp.csproj +++ /dev/null @@ -1,15 +0,0 @@ - - - - netcoreapp2.0 - MyApp - Exe - win10-x64 - - - - - - - - diff --git a/docs/host-powershell/sample-dotnet2.0-powershell.beta.1/MyApp/Program.cs b/docs/host-powershell/sample-dotnet2.0-powershell.beta.1/MyApp/Program.cs deleted file mode 100644 index f0fa6b5fedf..00000000000 --- a/docs/host-powershell/sample-dotnet2.0-powershell.beta.1/MyApp/Program.cs +++ /dev/null @@ -1,32 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -using System.Management.Automation; -using System.Reflection; - -namespace Application.Test -{ - public class Program - { - /// - /// Managed entry point shim, which starts the actual program - /// - public static int Main(string[] args) - { - // Application needs to use PowerShell AssemblyLoadContext if it needs to create powershell runspace - // PowerShell engine depends on PS ALC to provide the necessary assembly loading/searching support that is missing from .NET Core - string appBase = System.IO.Path.GetDirectoryName(typeof(Program).GetTypeInfo().Assembly.Location); - System.Console.WriteLine("\nappBase: {0}", appBase); - - // Initialize the PS ALC and let it load 'Logic.dll' and start the execution - return (int)PowerShellAssemblyLoadContextInitializer. - InitializeAndCallEntryMethod( - appBase, - new AssemblyName("Logic, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"), - "Application.Test.Logic", - "Start", - new object[] { args }); - } - } -} diff --git a/docs/host-powershell/sample-dotnet2.0-powershell.beta.1/NuGet.config b/docs/host-powershell/sample-dotnet2.0-powershell.beta.1/NuGet.config deleted file mode 100644 index 58f8d2c9b6d..00000000000 --- a/docs/host-powershell/sample-dotnet2.0-powershell.beta.1/NuGet.config +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/docs/host-powershell/sample-dotnet2.0-powershell.beta.3/MyApp/MyApp.csproj b/docs/host-powershell/sample-dotnet2.0-powershell.beta.3/MyApp/MyApp.csproj deleted file mode 100644 index 6901e37dda8..00000000000 --- a/docs/host-powershell/sample-dotnet2.0-powershell.beta.3/MyApp/MyApp.csproj +++ /dev/null @@ -1,16 +0,0 @@ - - - - netcoreapp2.0 - MyApp - Exe - win10-x64 - - - - - - - - - diff --git a/docs/host-powershell/sample-dotnet2.0-powershell.beta.3/MyApp/Program.cs b/docs/host-powershell/sample-dotnet2.0-powershell.beta.3/MyApp/Program.cs deleted file mode 100644 index 207028a2d1a..00000000000 --- a/docs/host-powershell/sample-dotnet2.0-powershell.beta.3/MyApp/Program.cs +++ /dev/null @@ -1,35 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -using System; -using System.Management.Automation; - -namespace Application.Test -{ - public class Program - { - /// - /// Managed entry point shim, which starts the actual program - /// - public static int Main(string[] args) - { - using (PowerShell ps = PowerShell.Create()) - { - Console.WriteLine("\nEvaluating 'Get-Command Write-Output' in PS Core Runspace\n"); - var results = ps.AddScript("Get-Command Write-Output").Invoke(); - Console.WriteLine(results[0].ToString()); - - ps.Commands.Clear(); - - Console.WriteLine("\nEvaluating '([S.M.A.ActionPreference], [S.M.A.AliasAttribute]).FullName' in PS Core Runspace\n"); - results = ps.AddScript("([System.Management.Automation.ActionPreference], [System.Management.Automation.AliasAttribute]).FullName").Invoke(); - foreach (dynamic result in results) - { - Console.WriteLine(result.ToString()); - } - } - return 0; - } - } -} diff --git a/docs/host-powershell/sample-dotnet2.0-powershell.beta.3/NuGet.config b/docs/host-powershell/sample-dotnet2.0-powershell.beta.3/NuGet.config deleted file mode 100644 index 58f8d2c9b6d..00000000000 --- a/docs/host-powershell/sample-dotnet2.0-powershell.beta.3/NuGet.config +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/docs/host-powershell/sample/MyApp/MyApp.csproj b/docs/host-powershell/sample/MyApp/MyApp.csproj new file mode 100644 index 00000000000..ab507b5acfc --- /dev/null +++ b/docs/host-powershell/sample/MyApp/MyApp.csproj @@ -0,0 +1,16 @@ + + + + netcoreapp2.1 + MyApp + Exe + win10-x64;linux-x64;osx-x64 + + + + + + + + + diff --git a/docs/host-powershell/sample/MyApp/Program.cs b/docs/host-powershell/sample/MyApp/Program.cs new file mode 100644 index 00000000000..fc54fa2d709 --- /dev/null +++ b/docs/host-powershell/sample/MyApp/Program.cs @@ -0,0 +1,35 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Management.Automation; + +namespace Application.Test +{ + public class Program + { + /// + /// Managed entry point shim, which starts the actual program. + /// + public static int Main(string[] args) + { + using (PowerShell ps = PowerShell.Create()) + { + Console.WriteLine("\nEvaluating 'Get-Command Write-Output' in PS Core Runspace\n"); + var results = ps.AddScript("Get-Command Write-Output").Invoke(); + Console.WriteLine(results[0].ToString()); + + ps.Commands.Clear(); + + Console.WriteLine("\nEvaluating '([S.M.A.ActionPreference], [S.M.A.AliasAttribute]).FullName' in PS Core Runspace\n"); + results = ps.AddScript("([System.Management.Automation.ActionPreference], [System.Management.Automation.AliasAttribute]).FullName").Invoke(); + foreach (dynamic result in results) + { + Console.WriteLine(result.ToString()); + } + } + + return 0; + } + } +} diff --git a/docs/host-powershell/sample/NuGet.config.md b/docs/host-powershell/sample/NuGet.config.md new file mode 100644 index 00000000000..bf2b4c3f688 --- /dev/null +++ b/docs/host-powershell/sample/NuGet.config.md @@ -0,0 +1,16 @@ +# Nuget.config creation + +Create a filed called `nuget.config` at this location with this content: + +```xml + + + + + + + + + + +``` diff --git a/docs/installation/linux.md b/docs/installation/linux.md deleted file mode 100644 index fe6590074d7..00000000000 --- a/docs/installation/linux.md +++ /dev/null @@ -1,510 +0,0 @@ -# Package installation instructions - -Supports [Ubuntu 14.04][u14], [Ubuntu 16.04][u16], [Debian 8][deb8], -[CentOS 7][cos], [Red Hat Enterprise Linux (RHEL) 7][rhel7], [Arch Linux][arch], [many Linux distributions (AppImage)][lai], and [macOS 10.12][mac]. -All packages are available on our GitHub [releases][] page. - -All of these steps can be done automatically by the [`download.sh`][download] script. -You should *never* run a script without reading it first! - -Please **read the [download][] script first**, and then if you want to run it, use: - -```sh -bash <(curl -fsSL https://raw.githubusercontent.com/PowerShell/PowerShell/master/tools/download.sh) -``` - -Once the package is installed, run `powershell` from a terminal. - -[u14]: #ubuntu-1404 -[u16]: #ubuntu-1604 -[deb8]: #debian-8 -[cos]: #centos-7 -[rhel7]: #red-hat-enterprise-linux-rhel-7 -[arch]: #arch-linux -[lai]: #linux-appimage -[mac]: #macos-1012 -[download]: https://github.com/PowerShell/PowerShell/blob/master/tools/download.sh - -## Ubuntu 14.04 - -### Installation via Package Repository - Ubuntu 14.04 - -PowerShell Core, for Linux, is published to package repositories for easy installation (and updates). -This is the preferred method. - -```sh -# Import the public repository GPG keys -curl https://packages.microsoft.com/keys/microsoft.asc | sudo apt-key add - - -# Register the Microsoft Ubuntu repository -curl https://packages.microsoft.com/config/ubuntu/14.04/prod.list | sudo tee /etc/apt/sources.list.d/microsoft.list - -# Update apt-get -sudo apt-get update - -# Install PowerShell -sudo apt-get install -y powershell - -# Start PowerShell -powershell -``` - -After registering the Microsoft repository once as superuser, -from then on, you just need to use `sudo apt-get upgrade powershell` to update it. - -### Installation via Direct Download - -Using [Ubuntu 14.04][], download the Debian package -`powershell_6.0.0-beta.6-1ubuntu1.14.04.1_amd64.deb` -from the [releases][] page onto the Ubuntu machine. - -Then execute the following in the terminal: - -```sh -sudo dpkg -i powershell_6.0.0-beta.6-1ubuntu1.14.04.1_amd64.deb -sudo apt-get install -f -``` - -> Please note that `dpkg -i` will fail with unmet dependencies; -> the next command, `apt-get install -f` resolves these -> and then finishes configuring the PowerShell package. - -### Uninstallation - Ubuntu 14.04 - -```sh -sudo apt-get remove powershell -``` - -[Ubuntu 14.04]: http://releases.ubuntu.com/14.04/ - -## Ubuntu 16.04 - -### Installation via Package Repository - Ubuntu 16.04 - -PowerShell Core, for Linux, is published to package repositories for easy installation (and updates). -This is the preferred method. - -```sh -# Import the public repository GPG keys -curl https://packages.microsoft.com/keys/microsoft.asc | sudo apt-key add - - -# Register the Microsoft Ubuntu repository -curl https://packages.microsoft.com/config/ubuntu/16.04/prod.list | sudo tee /etc/apt/sources.list.d/microsoft.list - -# Update apt-get -sudo apt-get update - -# Install PowerShell -sudo apt-get install -y powershell - -# Start PowerShell -powershell -``` - -After registering the Microsoft repository once as superuser, -from then on, you just need to use `sudo apt-get upgrade powershell` to update it. - -### Installation via Direct Download - Ubuntu 16.04 - -Using [Ubuntu 16.04][], download the Debian package -`powershell_6.0.0-beta.6-1ubuntu1.16.04.1_amd64.deb` -from the [releases][] page onto the Ubuntu machine. - -Then execute the following in the terminal: - -```sh -sudo dpkg -i powershell_6.0.0-beta.6-1ubuntu1.16.04.1_amd64.deb -sudo apt-get install -f -``` - -> Please note that `dpkg -i` will fail with unmet dependencies; -> the next command, `apt-get install -f` resolves these -> and then finishes configuring the PowerShell package. - -### Uninstallation - Ubuntu 16.04 - -```sh -sudo apt-get remove powershell -``` - -[Ubuntu 16.04]: http://releases.ubuntu.com/16.04/ - -## Debian 8 - -### Installation via Package Repository - Debian 8 - -PowerShell Core, for Linux, is published to package repositories for easy installation (and updates). -This is the preferred method. -Note that these instructions are the same as for Ubuntu 14.04 as we have validated the same package works on Debian 8. - -```sh -# Import the public repository GPG keys -curl https://packages.microsoft.com/keys/microsoft.asc | sudo apt-key add - - -# Register the Microsoft repository -curl https://packages.microsoft.com/config/ubuntu/14.04/prod.list | sudo tee /etc/apt/sources.list.d/microsoft.list - -# Update apt-get -sudo apt-get update - -# Install PowerShell -sudo apt-get install -y powershell - -# Start PowerShell -powershell -``` - -After registering the Microsoft repository once as superuser, -from then on, you just need to use `sudo apt-get upgrade powershell` to update it. - -### Installation via Direct Download - Debian 8 - -Download the Debian package -`powershell_6.0.0-beta.6-1ubuntu1.14.04.1_amd64.deb` -from the [releases][] page onto the Debian machine. - -Then execute the following in the terminal: - -```sh -sudo dpkg -i powershell_6.0.0-beta.6-1ubuntu1.14.04.1_amd64.deb -sudo apt-get install -f -``` - -> Please note that `dpkg -i` will fail with unmet dependencies; -> the next command, `apt-get install -f` resolves these -> and then finishes configuring the PowerShell package. - -### Uninstallation - Debian 8 - -```sh -sudo apt-get remove powershell -``` - -## CentOS 7 - -> This package also works on Oracle Linux 7. - -### Installation via Package Repository (preferred) - CentOS 7 - -PowerShell Core for Linux is published to official Microsoft repositories for easy installation (and updates). - -```sh -# Register the Microsoft RedHat repository -curl https://packages.microsoft.com/config/rhel/7/prod.repo | sudo tee /etc/yum.repos.d/microsoft.repo - -# Install PowerShell -sudo yum install -y powershell - -# Start PowerShell -powershell -``` - -After registering the Microsoft repository once as superuser, -you just need to use `sudo yum update powershell` to update PowerShell. - -### Installation via Direct Download - CentOS 7 - -Using [CentOS 7][], download the RPM package -`powershell-6.0.0_beta.6-1.el7.x86_64.rpm` -from the [releases][] page onto the CentOS machine. - -Then execute the following in the terminal: - -```sh -sudo yum install ./powershell-6.0.0_beta.6-1.el7.x86_64.rpm -``` - -You can also install the RPM without the intermediate step of downloading it: - -```sh -sudo yum install https://github.com/PowerShell/PowerShell/releases/download/v6.0.0-beta.6/powershell-6.0.0_beta.6-1.el7.x86_64.rpm -``` - -### Uninstallation - CentOS 7 - -```sh -sudo yum remove powershell -``` - -[CentOS 7]: https://www.centos.org/download/ - -## Red Hat Enterprise Linux (RHEL) 7 - -### Installation via Package Repository (preferred) - Red Hat Enterprise Linux (RHEL) 7 - -PowerShell Core for Linux is published to official Microsoft repositories for easy installation (and updates). - -```sh -# Register the Microsoft RedHat repository -curl https://packages.microsoft.com/config/rhel/7/prod.repo | sudo tee /etc/yum.repos.d/microsoft.repo - -# Install PowerShell -sudo yum install -y powershell - -# Start PowerShell -powershell -``` - -After registering the Microsoft repository once as superuser, -you just need to use `sudo yum update powershell` to update PowerShell. - -### Installation via Direct Download - Red Hat Enterprise Linux (RHEL) 7 - -Download the RPM package -`powershell-6.0.0_beta.6-1.el7.x86_64.rpm` -from the [releases][] page onto the Red Hat Enterprise Linux machine. - -Then execute the following in the terminal: - -```sh -sudo yum install ./powershell-6.0.0_beta.6-1.el7.x86_64.rpm -``` - -You can also install the RPM without the intermediate step of downloading it: - -```sh -sudo yum install https://github.com/PowerShell/PowerShell/releases/download/v6.0.0-beta.6/powershell-6.0.0_beta.6-1.el7.x86_64.rpm -``` - -### Uninstallation - Red Hat Enterprise Linux (RHEL) 7 - -```sh -sudo yum remove powershell -``` - -## OpenSUSE 42.1 - -Installation instruction for OpenSUSE 42.1. - -### Installation via Direct Download - OpenSUSE 42.1 - -Using [OpenSUSE 42.1][], download the RPM package -`powershell-6.0.0_beta.6-1.suse.42.1.x86_64.rpm` -from the [releases][] page onto the OpenSUSE machine. - -Then execute the following in the terminal: - -```sh -sudo rpm --import https://packages.microsoft.com/keys/microsoft.asc -sudo zypper install ./powershell-6.0.0_beta.6-1.suse.42.1.x86_64.rpm -``` - -You can also install the RPM without the intermediate step of downloading it: - -```sh -sudo rpm --import https://packages.microsoft.com/keys/microsoft.asc -sudo zypper install https://github.com/PowerShell/PowerShell/releases/download/v6.0.0-beta.6/powershell-6.0.0_beta.6-1.suse.42.1.x86_64.rpm -``` - -### Uninstallation - OpenSUSE 42.1 - -```sh -sudo zypper remove powershell -``` - -[OpenSUSE 42.1]: https://software.opensuse.org/421/en - -## Arch Linux - -PowerShell is available from the [Arch Linux][] User Repository (AUR) as a [release][arch-release] or the [latest development build][arch-git]. - -Packages in the AUR are community maintained - there is no official support. - -For more information on installing packages from the AUR, see the [Arch Linux wiki](https://wiki.archlinux.org/index.php/Arch_User_Repository#Installing_packages). - -[Arch Linux]: https://www.archlinux.org/download/ -[arch-release]: https://aur.archlinux.org/packages/powershell/ -[arch-git]: https://aur.archlinux.org/packages/powershell-git/ - -## Linux AppImage - -Using a recent Linux distribution, -download the AppImage `PowerShell-6.0.0-beta.6-x86_64.AppImage` -from the [releases][] page onto the Linux machine. - -Then execute the following in the terminal: - -```bash -chmod a+x PowerShell-6.0.0-beta.6-x86_64.AppImage -./PowerShell-6.0.0-beta.6-x86_64.AppImage -``` - -The [AppImage][] lets you run PowerShell without installing it. -It is a portable application that bundles PowerShell and its dependencies -(including .NET Core's system dependencies) into one cohesive package. -This package works independently of the user's Linux distribution, -and is a single binary. - -[appimage]: http://appimage.org/ - -## macOS 10.12 - -### Installation via Homebrew (preferred) - macOS 10.12 - -[Homebrew][brew] is the missing package manager for macOS. -If the `brew` command is not found, -you need to install Homebrew following [their instructions][brew]. - -Once you've installed Homebrew, installing PowerShell is easy. -First, install [Homebrew-Cask][cask], so you can install more packages: - -```sh -brew tap caskroom/cask -``` - -Now, you can install PowerShell: - -```sh -brew cask install powershell -``` - -When new versions of PowerShell are released, -simply update Homebrew's formulae and upgrade PowerShell: - -```sh -brew update -brew cask reinstall powershell -``` - -> Note: because of [this issue in Cask](https://github.com/caskroom/homebrew-cask/issues/29301), you currently have to do a reinstall to upgrade. - -[brew]: http://brew.sh/ -[cask]: https://caskroom.github.io/ - -### Installation via Direct Download - macOS 10.12 - -Using macOS 10.12, download the PKG package -`powershell-6.0.0-beta.6-osx.10.12-x64.pkg` -from the [releases][] page onto the macOS machine. - -Either double-click the file and follow the prompts, -or install it from the terminal: - -```sh -sudo installer -pkg powershell-6.0.0-beta.6-osx.10.12-x64.pkg -target / -``` - -### Uninstallation - macOS 10.12 - -If you installed PowerShell with Homebrew, uninstallation is easy: - -```sh -brew cask uninstall powershell -``` - -If you installed PowerShell via direct download, -PowerShell must be removed manually: - -```sh -sudo rm -rf /usr/local/bin/powershell /usr/local/microsoft/powershell -``` - -To uninstall the additional PowerShell paths (such as the user profile path) -please see the [paths][paths] section below in this document -and remove the desired the paths with `sudo rm`. -(Note: this is not necessary if you installed with Homebrew.) - -[paths]:#paths - -### OpenSSL on macOS - -On macOS, .NET Core requires Homebrew's OpenSSL -because the "OpenSSL" system libraries on macOS are not OpenSSL, -as Apple deprecated OpenSSL in favor of their own libraries. -This requirement is not a hard requirement for all of PowerShell. -However, most networking functions (such as `Invoke-WebRequest`) -do require OpenSSL to work properly. - -The PowerShell formula for Homebrew includes this OpenSSL as a dependency, -so you if you installed via Homebrew, you shouldn't run into these problems. - -If you installed via direct download (or through some means other than Homebrew), -the easiest fix for these issues is to install [Homebrew's OpenSSL][openssl]: - -```bash -brew install openssl -brew install curl --with-openssl -``` - -**Please ignore** .NET Core's installation instructions to manually link the OpenSSL libraries. -This is **not** required for PowerShell as we patch .NET Core's cryptography libraries to find Homebrew's OpenSSL in its installed location. -Again, **do not** run `brew link --force` nor `ln -s` for OpenSSL, regardless of other instructions. - -Homebrew previously allowed OpenSSL libraries to be linked to the system library location; -however, this created major security holes and is [no longer allowed][homebrew-patch]. -Because .NET Core's 1.0.0 release libraries still look in the prior system location for OpenSSL, -they will fail to work unless the libraries are manually placed there (security risk), -or their libraries are patched (which we do). -To patch .NET Core's cryptography libraries, we use `install_name_tool`: - -```bash -find ~/.nuget -name System.Security.Cryptography.Native.dylib | xargs sudo install_name_tool -add_rpath /usr/local/opt/openssl/lib -find ~/.nuget -name System.Net.Http.Native.dylib | xargs sudo install_name_tool -change /usr/lib/libcurl.4.dylib /usr/local/opt/curl/lib/libcurl.4.dylib -``` - -This updates .NET Core's library to look in Homebrew's OpenSSL installation location instead of the system library location. -The PowerShell macOS package come with the necessary libraries patched, -and the build script patches the libraries on-the-fly when building from source. -You *can* run this command manually if you're having trouble with .NET Core's cryptography libraries. - -[openssl]: https://github.com/Homebrew/homebrew-core/blob/master/Formula/openssl.rb -[homebrew-patch]: https://github.com/Homebrew/brew/pull/597 - -## Kali - -### Installation - -```sh -# Install prerequisites -apt-get install libunwind8 libicu55 -wget http://security.debian.org/debian-security/pool/updates/main/o/openssl/libssl1.0.0_1.0.1t-1+deb8u6_amd64.deb -dpkg -i libssl1.0.0_1.0.1t-1+deb8u6_amd64.deb - -# Install PowerShell -dpkg -i powershell_6.0.0-beta.6-1ubuntu1.16.04.1_amd64.deb - -# Start PowerShell -powershell -``` - -### Run PowerShell in latest Kali (Kali GNU/Linux Rolling) without installing it - -```sh -# Grab the latest App Image -wget https://github.com/PowerShell/PowerShell/releases/download/v6.0.0-beta.6/PowerShell-6.0.0-beta.6-x86_64.AppImage - -# Make executable -chmod a+x PowerShell-6.0.0-beta.6-x86_64.AppImage - -# Start PowerShell -./PowerShell-6.0.0-beta.6-x86_64.AppImage -``` - -### Uninstallation - Kali - -```sh -dpkg -r powershell_6.0.0-beta.6-1ubuntu1.16.04.1_amd64 -``` - -## Paths - -* `$PSHOME` is `/opt/microsoft/powershell/6.0.0-beta.6/` -* User profiles will be read from `~/.config/powershell/profile.ps1` -* Default profiles will be read from `$PSHOME/profile.ps1` -* User modules will be read from `~/.local/share/powershell/Modules` -* Shared modules will be read from `/usr/local/share/powershell/Modules` -* Default modules will be read from `$PSHOME/Modules` -* PSReadline history will be recorded to `~/.local/share/powershell/PSReadLine/ConsoleHost_history.txt` - -The profiles respect PowerShell's per-host configuration, -so the default host-specific profiles exists at `Microsoft.PowerShell_profile.ps1` in the same locations. - -On Linux and macOS, the [XDG Base Directory Specification][xdg-bds] is respected. - -Note that because macOS is a derivation of BSD, -instead of `/opt`, the prefix used is `/usr/local`. -Thus, `$PSHOME` is `/usr/local/microsoft/powershell/6.0.0-beta.6/`, -and the symlink is placed at `/usr/local/bin/powershell`. - -[releases]: https://github.com/PowerShell/PowerShell/releases/latest -[xdg-bds]: https://specifications.freedesktop.org/basedir-spec/basedir-spec-latest.html diff --git a/docs/installation/windows.md b/docs/installation/windows.md deleted file mode 100644 index 50c5484d9c9..00000000000 --- a/docs/installation/windows.md +++ /dev/null @@ -1,141 +0,0 @@ -# Package Installation Instructions - -## MSI - -To install PowerShell on Windows Full SKU (works on Windows 7 SP1 and later), download either the MSI from [AppVeyor][] for a nightly build, -or a released package from our GitHub [releases][] page. The MSI file looks like this - `PowerShell-6.0.0...msi` - -Once downloaded, double-click the installer and follow the prompts. - -There is a shortcut placed in the Start Menu upon installation. - -* By default the package is installed to `$env:ProgramFiles\PowerShell\` -* You can launch PowerShell via the Start Menu or `$env:ProgramFiles\PowerShell\powershell.exe` - -### Prerequisites - -* Install the [Universal C Runtime](https://www.microsoft.com/download/details.aspx?id=50410) on Windows versions prior to Windows 10. - It is available via direct download or Windows Update. - Fully patched (including optional packages), supported systems will already have this installed. -* Install the [Visual C++ Redistributable](https://www.microsoft.com/download/details.aspx?id=48145) for VS2015. - -## Deploying on Nano Server - -These instructions assume that Windows PowerShell is running on the Nano Server image and that it has been generated by the [Nano Server Image Builder](https://technet.microsoft.com/windows-server-docs/get-started/deploy-nano-server). -Nano Server is a "headless" OS and deployment of PowerShell Core binaries can happen in two different ways: - -1. Offline - Mount the Nano Server VHD and unzip the contents of the zip file to your chosen location within the mounted image. -1. Online - Transfer the zip file over a PowerShell Session and unzip it in your chosen location. - -In both cases, you will need the Windows 10 x64 Zip release package and will need to run the commands within an "Administrator" PowerShell instance. - -### Offline Deployment of PowerShell Core - -1. Use your favorite zip utility to unzip the package to a directory within the mounted Nano Server image. -1. Unmount the image and boot it. -1. Connect to the inbox instance of Windows PowerShell. -1. Follow the instructions to create a remoting endpoint using the [another instance technique](#executed-by-another-instance-of-powershell-on-behalf-of-the-instance-that-it-will-register). - -### Online Deployment of PowerShell Core - -The following steps will guide you through the deployment of PowerShell Core to a running instance of Nano Server and the configuration of its remote endpoint. - -* Connect to the inbox instance of Windows PowerShell - -```powershell -$session = New-PSSession -ComputerName -Credential -``` - -* Copy the file to the Nano Server instance - -```powershell -Copy-Item \powershell--win10-win2016-x64.zip c:\ -ToSession $session -``` - -* Enter the session - -```powershell -Enter-PSSession $session -``` - -* Extract the Zip file - -```powershell -# Insert the appropriate version. -Expand-Archive -Path C:\powershell--win10-win2016-x64.zip -DestinationPath "C:\PowerShellCore_" -``` - -* Follow the instructions to create a remoting endpoint using the [another instance technique](#executed-by-another-instance-of-powershell-on-behalf-of-the-instance-that-it-will-register). - -## Instructions to Create a Remoting Endpoint - -Beginning with 6.0.0-alpha.9, the PowerShell package for Windows includes a WinRM plug-in (pwrshplugin.dll) and an installation script (Install-PowerShellRemoting.ps1). -These files enable PowerShell to accept incoming PowerShell remote connections when its endpoint is specified. - -### Motivation - -An installation of PowerShell can establish PowerShell sessions to remote computers using `New-PSSession` and `Enter-PSSession`. -To enable it to accept incoming PowerShell remote connections, the user must create a WinRM remoting endpoint. -This is an explicit opt-in scenario where the user runs Install-PowerShellRemoting.ps1 to create the WinRM endpoint. -The installation script is a short-term solution until we add additional functionality to `Enable-PSRemoting` to perform the same action. -For more details, please see issue [#1193](https://github.com/PowerShell/PowerShell/issues/1193). - -### Script Actions - -The script - -1. Creates a directory for the plug-in within %windir%\System32\PowerShell -1. Copies pwrshplugin.dll to that location -1. Generates a configuration file -1. Registers that plug-in with WinRM - -### Registration - -The script must be executed within an Administrator-level PowerShell session and runs in two modes. - -#### Executed by the instance of PowerShell that it will register - -``` powershell -Install-PowerShellRemoting.ps1 -``` - -#### Executed by another instance of PowerShell on behalf of the instance that it will register - -``` powershell -\Install-PowerShellRemoting.ps1 -PowerShellHome "" -PowerShellVersion "" -``` - -For Example: - -``` powershell -C:\Program Files\PowerShell\6.0.0.9\Install-PowerShellRemoting.ps1 -PowerShellHome "C:\Program Files\PowerShell\6.0.0.9\" -PowerShellVersion "6.0.0-alpha.9" -``` - -**NOTE:** The remoting registration script will restart WinRM, so all existing PSRP sessions will terminate immediately after the script is run. If run during a remote session, this will terminate the connection. - -## How to Connect to the New Endpoint - -Create a PowerShell session to the new PowerShell endpoint by specifying `-ConfigurationName "some endpoint name"`. To connect to the PowerShell instance from the example above, use either: - -``` powershell -New-PSSession ... -ConfigurationName "powershell.6.0.0-alpha.9" -Enter-PSSession ... -ConfigurationName "powershell.6.0.0-alpha.9" -``` - -Note that `New-PSSession` and `Enter-PSSession` invocations that do not specify `-ConfigurationName` will target the default PowerShell endpoint, `microsoft.powershell`. - -## Artifact Installation Instructions - -We publish an archive with CoreCLR bits on every CI build with [AppVeyor][]. - -[releases]: https://github.com/PowerShell/PowerShell/releases -[signing]: ../../tools/Sign-Package.ps1 -[AppVeyor]: https://ci.appveyor.com/project/PowerShell/powershell - -## CoreCLR Artifacts - -* Download zip package from **artifacts** tab of the particular build. -* Unblock zip file: right-click in File Explorer -> Properties -> - check 'Unblock' box -> apply -* Extract zip file to `bin` directory -* `./bin/powershell.exe` diff --git a/docs/learning-powershell/README.md b/docs/learning-powershell/README.md deleted file mode 100644 index 526dffbd833..00000000000 --- a/docs/learning-powershell/README.md +++ /dev/null @@ -1,136 +0,0 @@ -Learning PowerShell -==== - -Whether you're a Developer, a DevOps or an IT Professional, this doc will help you getting started with PowerShell. -In this document we'll cover the following: -installing PowerShell, samples walkthrough, PowerShell editor, debugger, testing tools and a map book for experienced bash users to get started with PowerShell faster. - -The exercises in this document are intended to give you a solid foundation in how to use PowerShell. -You won't be a PowerShell guru at the end of reading this material but you will be well on your way with the right set of knowledge to start using PowerShell. - -If you have 30 minutes now, let’s try it. - - -Installing PowerShell ----- - -First you need to set up your computer working environment if you have not done so. -Choose the platform below and follow the instructions. -At the end of this exercise, you should be able to launch the PowerShell session. - -- Get PowerShell by installing package - * [PowerShell on Linux][inst-linux] - * [PowerShell on macOS][inst-macos] - * [PowerShell on Windows][inst-win] - - For this tutorial, you do not need to install PowerShell if you are running on Windows. - You can launch PowerShell console by pressing Windows key, typing PowerShell, and clicking on Windows PowerShell. - However if you want to try out the latest PowerShell, follow the [PowerShell on Windows][inst-win]. - -- Alternatively you can get the PowerShell by [building it](../../README.md#building-powershell) - -[inst-linux]: ../installation/linux.md -[inst-win]: ../installation/windows.md -[inst-macos]: ../installation/linux.md#os-x-1011 - -Getting Started with PowerShell ----- -PowerShell commands follow a Verb-Noun semantic with a set of parameters. -It's easy to learn and use PowerShell. -For example, `Get-Process` will display all the running processes on your system. -Let's walk through with a few examples from the [PowerShell Beginner's Guide](powershell-beginners-guide.md). - -Now you have learned the basics of PowerShell. -Please continue reading if you want to do some development work in PowerShell. - -PowerShell Editor ----- - -In this section, you will create a PowerShell script using a text editor. -You can use your favorite editor to write scripts. -We use Visual Studio Code (VS Code) which works on Windows, Linux, and macOS. -Click on the following link to create your first PowerShell script. - -- [Using Visual Studio Code (VS Code)][use-vscode-editor] - -PowerShell Debugger ----- - -Debugging can help you find bugs and fix problems in your PowerShell scripts. -Click on the link below to learn more about debugging: - -- [Using Visual Studio Code (VS Code)][use-vscode-debugger] -- [PowerShell Command-line Debugging][cli-debugging] - -[use-vscode-editor]:./using-vscode.md#editing-with-vs-code -[use-vscode-debugger]:./using-vscode.md#debugging-with-vs-code -[cli-debugging]:./debugging-from-commandline.md -[get-powershell]:../../README.md#get-powershell -[build-powershell]:../../README.md#building-the-repository - - -PowerShell Testing ----- - -We recommend using Pester testing tool which is initiated by the PowerShell Community for writing test cases. -To use the tool please read [ Pester Guides](https://github.com/pester/Pester) and [Writing Pester Tests Guidelines](https://github.com/PowerShell/PowerShell/blob/master/docs/testing-guidelines/WritingPesterTests.md). - - -Map Book for Experienced Bash users ----- - -The table below lists the usage of some basic commands to help you get started on PowerShell faster. -Note that all bash commands should continue working on PowerShell session. - - -| Bash | PowerShell | Description -|:--------------------|:----------------------------|:--------------------- -| ls |dir, Get-ChildItem |List files and folders -| tree |dir -Recurse |List all files and folders -| cd |cd, Set-Location |Change directory -| pwd |pwd, $pwd, Get-Location |Show working directory -| clear, Ctrl+L, reset| cls, clear |Clear screen -| mkdir |New-Item -ItemType Directory |Create a new folder -| touch test.txt |New-Item -Path test.txt |Create a new empty file -| cat test1.txt test2.txt |Get-Content test1.txt, test2.txt |Display files contents -| cp ./source.txt ./dest/dest.txt |Copy-Item source.txt dest/dest.txt |Copy a file -| cp -r ./source ./dest |Copy-Item ./source ./dest -Recurse |Recursively copy from one folder to another -| mv ./source.txt ./dest/dest.txt |Move-Item ./source.txt ./dest/dest.txt |Move a file to other folder -| rm test.txt |Remove-Item test.txt |Delete a file -| rm -r <folderName> |Remove-Item <folderName> -Recurse |Delete a folder -| find -name build* |Get-ChildItem build* -Recurse |Find a file or folder starting with 'build' -| grep -Rin "sometext" --include="*.cs" |Get-ChildItem -Recurse -Filter *.cs
\| Select-String -Pattern "sometext" | Recursively case-insensitive search for text in files - - -Recommended Training and Reading ----- -- Microsoft Virtual Academy: [Getting Started with PowerShell][getstarted-with-powershell] -- [Why Learn PowerShell][why-learn-powershell] by Ed Wilson -- PowerShell Web Docs: [Basic cookbooks][basic-cookbooks] -- [PowerShell eBook][ebook-from-powershell.com] from PowerShell.com -- [PowerShell-related Videos][channel9-learn-powershell] on Channel 9 -- [Learn PowerShell Video Library][powershell.com-learn-powershell] from PowerShell.com -- [PowerShell Quick Reference Guides][quick-reference] by PowerShellMagazine.com -- [PowerShell 5 How-To Videos][script-guy-how-to] by Ed Wilson -- [PowerShell TechNet Resources](https://technet.microsoft.com/en-us/scriptcenter/dd742419.aspx) from ScriptCenter - - -Commercial Resources ----- -- [Windows PowerShell in Action][in-action] by Bruce Payette -- [Introduction to PowerShell][powershell-intro] from Pluralsight -- [PowerShell Training and Tutorials][lynda-training] from Lynda.com - - -[in-action]: https://www.amazon.com/Windows-PowerShell-Action-Second-Payette/dp/1935182137 -[powershell-intro]: https://www.pluralsight.com/courses/powershell-intro -[lynda-training]: https://www.lynda.com/PowerShell-training-tutorials/5779-0.html - -[getstarted-with-powershell]: https://channel9.msdn.com/Series/GetStartedPowerShell3 -[why-learn-powershell]: https://blogs.technet.microsoft.com/heyscriptingguy/2014/10/18/weekend-scripter-why-learn-powershell/ -[basic-cookbooks]: https://msdn.microsoft.com/en-us/powershell/scripting/getting-started/basic-cookbooks -[ebook-from-powershell.com]: http://powershell.com/cs/blogs/ebookv2/default.aspx -[channel9-learn-powershell]: https://channel9.msdn.com/Search?term=powershell#ch9Search -[powershell.com-learn-powershell]: http://powershell.com/cs/media/14/default.aspx -[quick-reference]: http://www.powershellmagazine.com/2014/04/24/windows-powershell-4-0-and-other-quick-reference-guides/ -[script-guy-how-to]:https://blogs.technet.microsoft.com/tommypatterson/2015/09/04/ed-wilsons-powershell5-videos-now-on-channel9-2/ diff --git a/docs/learning-powershell/create-powershell-scripts.md b/docs/learning-powershell/create-powershell-scripts.md deleted file mode 100644 index f707d61125b..00000000000 --- a/docs/learning-powershell/create-powershell-scripts.md +++ /dev/null @@ -1,61 +0,0 @@ -How to Create and Run PowerShell Scripts -==== - -You can combine a series of commands in a text file and save it with the file extension '.ps1', and the file will become a PowerShell script. -This would begin by opening your favorite text editor and pasting in the following example. - -``` PowerShell -# Script to return current IPv4 addresses on a Linux or MacOS host -$ipInfo = ifconfig | Select-String 'inet' -$ipInfo = [regex]::matches($ipInfo,"addr:\b(?:\d{1,3}\.){3}\d{1,3}\b") | ForEach-Object value -foreach ($ip in $ipInfo) { - $ip.Replace('addr:','') -} -``` - -Then save the file to something memorable, such as .\NetIP.ps1. -In the future when you need to get the IP addresses for the node, you can simplify this task by executing the script. - -``` PowerShell -PS> .\NetIP.ps1 -10.0.0.1 -127.0.0.1 -``` -You can accomplish this same task on Windows. - -```PowerShell -# One line script to return current IPv4 addresses on a Windows host -Get-NetIPAddress | Where-Object {$_.AddressFamily -eq 'IPv4'} | ForEach-Object IPAddress -``` -As before, save the file as .\NetIP.ps1 and execute within a PowerShell environment. -Note: If you are using Windows, make sure you set the PowerShell's execution policy to "RemoteSigned" in this case. -See [Running PowerShell Scripts Is as Easy as 1-2-3][run-ps] for more details. - -```PowerShell -PS C:\> NetIP.ps1 -127.0.0.1 -10.0.0.1 -``` - -Creating a script that can accomplish the same task on multiple operating systems ----- - -If you would like to author one script that will return the IP address across Linux, MacOS, or Windows, you could accomplish this using an IF statement. - -```PowerShell -# Script to return current IPv4 addresses for Linux, MacOS, or Windows -$IP = if ($IsLinux -or $IsOSX) { - $ipInfo = ifconfig | Select-String 'inet' - $ipInfo = [regex]::matches($ipInfo,"addr:\b(?:\d{1,3}\.){3}\d{1,3}\b") | ForEach-Object value - foreach ($ip in $ipInfo) { - $ip.Replace('addr:','') - } -} -else { - Get-NetIPAddress | Where-Object {$_.AddressFamily -eq 'IPv4'} | ForEach-Object IPAddress -} - -# Remove loopback address from output regardless of platform -$IP | Where-Object {$_ -ne '127.0.0.1'} -``` -[run-ps]:http://windowsitpro.com/powershell/running-powershell-scripts-easy-1-2-3 \ No newline at end of file diff --git a/docs/learning-powershell/debugging-from-commandline.md b/docs/learning-powershell/debugging-from-commandline.md deleted file mode 100644 index 2880774a0c0..00000000000 --- a/docs/learning-powershell/debugging-from-commandline.md +++ /dev/null @@ -1,174 +0,0 @@ -Debugging in PowerShell Command-line -===== - -As we know, we can debug PowerShell code via GUI tools like [Visual Studio Code](./using-vscode.md#debugging-with-vs-code). In addition, we can directly perform debugging within the PowerShell command-line session by using the PowerShell debugger cmdlets. This document demonstrates how to use the cmdlets for the PowerShell command-line debugging. We will cover the following topics: setting a debug breakpoint on a line of code and on a variable. - -Let's use the following code snippet as our sample script. - -```PowerShell -# Convert Fahrenheit to Celsius -function ConvertFahrenheitToCelsius([double] $fahrenheit) -{ -$celsius = $fahrenheit - 32 -$celsius = $celsius / 1.8 -$celsius -} - -$fahrenheit = Read-Host 'Input a temperature in Fahrenheit' -$result =[int](ConvertFahrenheitToCelsius($fahrenheit)) -Write-Host "$result Celsius" - -``` - - - **1. Setting a Breakpoint on a Line** - -- Open a [PowerShell editor](README.md#powershell-editor) -- Save the above code snippet to a file. For example, "test.ps1" -- Go to your command-line PowerShell -- Clear existing breakpoints if any - -```PowerShell - PS /home/jen/debug>Get-PSBreakpoint | Remove-PSBreakpoint - ``` -- Use **Set-PSBreakpoint** cmdlet to set a debug breakpoint. In this case, we will set it to line 5 - -```PowerShell -PS /home/jen/debug>Set-PSBreakpoint -Line 5 -Script ./test.ps1 - -ID Script Line Command Variable Action --- ------ ---- ------- -------- ------ - 0 test.ps1 5 -``` -- Run the script "test.ps1". As we have set a breakpoint, it is expected the program will break into the debugger at the line 5. - -```PowerShell - -PS /home/jen/debug> ./test.ps1 -Input a temperature in Fahrenheit: 80 -Hit Line breakpoint on '/home/jen/debug/test.ps1:5' - -At /home/jen/debug/test.ps1:5 char:1 -+ $celsius = $celsius / 1.8 -+ ~~~~~~~~~~~~~~~~~~~~~~~~~ -[DBG]: PS /home/jen/debug>> -``` - -- The PowerShell prompt now has the prefix **[DBG]:** as you may have noticed. This means - we have entered into the debug mode. To watch the variables like $celsius, simply type **$celsius** as below. -- To exit from the debugging, type **q** -- To get help for the debugging commands, simply type **?**. The following is an example of debugging output. - -```PowerShell -[DBG]: PS /home/jen/debug>> $celsius -48 -[DBG]: PS /home/jen/debug>> $fahrenheit -80 -[DBG]: PS /home/jen/debug>> ? - - s, stepInto Single step (step into functions, scripts, etc.) - v, stepOver Step to next statement (step over functions, scripts, etc.) - o, stepOut Step out of the current function, script, etc. - - c, continue Continue operation - q, quit Stop operation and exit the debugger - d, detach Continue operation and detach the debugger. - - k, Get-PSCallStack Display call stack - - l, list List source code for the current script. - Use "list" to start from the current line, "list " - to start from line , and "list " to list - lines starting from line - - Repeat last command if it was stepInto, stepOver or list - - ?, h displays this help message. - - -For instructions about how to customize your debugger prompt, type "help about_prompt". - -[DBG]: PS /home/jen/debug>> s -At PS /home/jen/debug/test.ps1:6 char:1 -+ $celsius -+ ~~~~~~~~ -[DBG]: PS /home/jen/debug>> $celsius -26.6666666666667 -[DBG]: PS /home/jen/debug>> $fahrenheit -80 - -[DBG]: PS /home/jen/debug>> q -PS /home/jen/debug> - -``` - - -**2. Setting a Breakpoint on a Variable** -- Clear existing breakpoints if there are any - -```PowerShell - PS /home/jen/debug>Get-PSBreakpoint | Remove-PSBreakpoint - ``` -- Use **Set-PSBreakpoint** cmdlet to set a debug breakpoint. In this case, we set it to line 5 - -```PowerShell - - PS /home/jen/debug>Set-PSBreakpoint -Variable "celsius" -Mode write -Script ./test.ps1 - -``` - -- Run the script "test.ps1" - - Once hit the debug breakpoint, we can type **l** to list the source code that debugger is currently executing. As we can see line 3 has an asterisk at the front, meaning that's the line the program is currently executing and broke into the debugger as illustrated below. -- Type **q** to exit from the debugging mode. The following is an example of debugging output. - -```PowerShell - -PS /home/jen/debug> ./test.ps1 -Input a temperature in Fahrenheit: 80 -Hit Variable breakpoint on '/home/jen/debug/test.ps1:$celsius' (Write access) - -At /home/jen/debug/test.ps1:3 char:1 -+ $celsius = $fahrenheit - 32 -+ ~~~~~~~~~~~~~~~~~~~~~~~~~~~ -[DBG]: PS /home/jen/debug>> l - - - 1: function ConvertFahrenheitToCelsius([double] $fahrenheit) - 2: { - 3:* $celsius = $fahrenheit - 32 - 4: $celsius = $celsius / 1.8 - 5: $celsius - 6: } - 7: - 8: $fahrenheit = Read-Host 'Input a temperature in Fahrenheit' - 9: $result =[int](ConvertFahrenheitToCelsius($fahrenheit)) - 10: Write-Host "$result Celsius" - - -[DBG]: PS /home/jen/debug>> $celsius -48 -[DBG]: PS /home/jen/debug>> v -At /home/jen/debug/test.ps1:4 char:1 -+ $celsius = $celsius / 1.8 -+ ~~~~~~~~~~~~~~~~~~~~~~~~~ -[DBG]: PS /home/jen/debug>> v -Hit Variable breakpoint on '/home/jen/debug/test.ps1:$celsius' (Write access) - -At /home/jen/debug/test.ps1:4 char:1 -+ $celsius = $celsius / 1.8 -+ ~~~~~~~~~~~~~~~~~~~~~~~~~ -[DBG]: PS /home/jen/debug>> $celsius -26.6666666666667 -[DBG]: PS /home/jen/debug>> q -PS /home/jen/debug> - -``` - -Now you know the basics of the PowerShell debugging from PowerShell command-line. For further learning, read the following articles. - - -More Reading -===== -- [about_Debuggers](https://technet.microsoft.com/en-us/library/hh847790.aspx) -- [PowerShell Debugging](https://blogs.technet.microsoft.com/heyscriptingguy/tag/debugging/) diff --git a/docs/learning-powershell/powershell-beginners-guide.md b/docs/learning-powershell/powershell-beginners-guide.md deleted file mode 100644 index 3f0c1cfaafb..00000000000 --- a/docs/learning-powershell/powershell-beginners-guide.md +++ /dev/null @@ -1,309 +0,0 @@ -PowerShell Beginner’s Guide -==== - -If you are new to PowerShell, this document will walk you through a few examples to give you some basic ideas of PowerShell. -We recommend that you open a PowerShell console/session and type along with the instructions in this document to get most out of this exercise. - - -Launch PowerShell Console/Session ---- -First you need to launch a PowerShell session by following the [Installing PowerShell Guide](./README.md#installing-powershell). - - -Getting Familiar with PowerShell Commands ---- -In this section, you will learn how to -- create a file, delete a file and change file directory -- discover what version of PowerShell you are currently using -- exit a PowerShell session -- get help if you needed -- find syntax of PowerShell cmdlets -- and more - -As mentioned above, PowerShell commands are designed to have Verb-Noun structure, for instance Get-Process, Set-Location, Clear-Host, etc. -Let’s exercise some of the basic PowerShell commands, also known as **cmdlets**. - -Please note that we will use the PowerShell prompt sign **PS />** as it appears on Linux in the following examples. -It is shown as **PS C:\\>** on Windows. - -**1. Get-Process**: Gets the processes that are running on the local computer or a remote computer. - -By default, you will get data back similar to the following: -``` PowerShell -PS /> Get-Process - -Handles NPM(K) PM(K) WS(K) CPU(s) Id ProcessName -------- ------ ----- ----- ------ -- ----------- - - - - 1 0.012 12 bash - - - - 21 20.220 449 powershell - - - - 11 61.630 8620 code - - - - 74 403.150 1209 firefox - -… -``` -Only interested in the instance of firefox process that are running on your computer? -Try this: -```PowerShell -PS /> Get-Process -Name firefox - -Handles NPM(K) PM(K) WS(K) CPU(s) Id ProcessName -------- ------ ----- ----- ------ -- ----------- - - - - 74 403.150 1209 firefox - -``` -Want to get back more than one process? -Then just specify process names and separate them with commas. -```PowerShell -PS /> Get-Process -Name firefox, powershell -Handles NPM(K) PM(K) WS(K) CPU(s) Id ProcessName -------- ------ ----- ----- ------ -- ----------- - - - - 74 403.150 1209 firefox - - - - 21 20.220 449 powershell - -``` - -**2. Clear-Host**: Clears the display in the host program. -```PowerShell -PS /> Get-Process -PS /> Clear-Host -``` -Type too much just for clearing the screen? -Here is how the alias can help. - -**3. Get-Alias**: Gets the aliases for the current session. -```PowerShell -PS /> Get-Alias - -CommandType Name ------------ ---- -… - -Alias cd -> Set-Location -Alias cls -> Clear-Host -Alias copy -> Copy-Item -Alias dir -> Get-ChildItem -Alias gc -> Get-Content -Alias gmo -> Get-Module -Alias ri -> Remove-Item -Alias type -> Get-Content -… - -As you can see "cls" is an alias of Clear-Host. -Now try it: - -PS /> Get-Process -PS /> cls -``` -**4. cd - Set-Location**: Sets the current working location to a specified location. -```PowerShell -PS /> Set-Location /home -PS /home> -``` -**5. dir - Get-ChildItem**: Gets the items and child items in one or more specified locations. - -```PowerShell -Get all files under the current directory: - -PS /> Get-ChildItem - -Get all files under the current directory as well as its subdirectories: -PS /> cd $home -PS /home/jen> dir -Recurse - -List all files with "txt" file extension. - -PS /> cd $home -PS /home/jen> dir –Path *.txt -Recurse -``` - -**6. New-Item**: Creates a new item. - -```PowerShell -An empty file is created if you type the following: -PS /home/jen> New-Item -Path ./test.txt - - - Directory: /home/jen - - -Mode LastWriteTime Length Name ----- ------------- ------ ---- --a---- 7/7/2016 7:17 PM 0 test.txt -``` -You can use the **-Value** parameter to add some data to your file. -For example, the following command adds the phrase "Hello world!" as a file content to the test.txt. -Because the test.txt file exists already, we use **-Force** parameter to replace the existing content. - -```PowerShell -PS /home/jen> New-Item -Path ./test.txt -Value "Hello world!" -Force - - Directory: /home/jen - - -Mode LastWriteTime Length Name ----- ------------- ------ ---- --a---- 7/7/2016 7:19 PM 24 test.txt - -``` -There are other ways to add some data to a file. -For example, you can use Set-Content to set the file contents: - -```PowerShell -PS /home/jen>Set-Content -Path ./test.txt -Value "Hello world again!" -``` -Or simply use ">" as below: -``` -# create an empty file -"" > test.txt - -# set "Hello world!" as content of test.txt file -"Hello world!!!" > test.txt - -``` -The pound sign (#) above is used for comments in PowerShell. - -**7. type - Get-Content**: Gets the content of the item at the specified location. - -```PowerShell -PS /home/jen> Get-Content -Path ./test.txt -PS /home/jen> type -Path ./test.txt - -Hello world again! -``` -**8. del - Remove-Item**: Deletes the specified items. - -This cmdlet will delete the file /home/jen/test.txt: -```PowerShell -PS /home/jen> Remove-Item ./test.txt -``` - -**9. $PSVersionTable**: Displays the version of PowerShell you are currently using. - -Type **$PSVersionTable** in your PowerShell session, you will see something like below. -"PSVersion" indicates the PowerShell version that you are using. - -```PowerShell -Name Value ----- ----- -PSVersion 6.0.0-alpha -PSEdition Core -PSCompatibleVersions {1.0, 2.0, 3.0, 4.0...} -BuildVersion 3.0.0.0 -GitCommitId v6.0.0-alpha.12 -CLRVersion -WSManStackVersion 3.0 -PSRemotingProtocolVersion 2.3 -SerializationVersion 1.1.0.1 - -``` - -**10. Exit**: To exit the PowerShell session, type "exit". -```PowerShell -PS /home/jen> exit -``` - -Need Help? ----- -The most important command in PowerShell is possibly the Get-Help, which allows you to quickly learn PowerShell without having to search around the internet. -The Get-Help cmdlet also shows you how PowerShell commands work with examples. - -It shows the syntax and other technical information of the Get-Process cmdlet. -```PowerShell -PS /> Get-Help -Name Get-Process -``` - -It displays the examples how to use the Get-Process cmdlet. -```PowerShell -PS />Get-Help -Name Get-Process -Examples -``` - -If you use **-Full** parameter, for example, `Get-Help -Name Get-Process -Full`, it will display more technical information. - - -Discover Commands Available on Your System ----- - -You want to discover what PowerShell cmdlets available on your system? Just run "Get-Command" as below: -```PowerShell -PS /> Get-Command -``` -If you want to know whether a particular cmdlet exists on your system, you can do something like below: -```PowerShell -PS /> Get-Command Get-Process -``` -If you want to know the syntax of Get-Process cmdlet, type: -```PowerShell -PS /> Get-Command Get-Process -Syntax -``` -If you want to know how to use the Get-Process, type: -```PowerShell -PS /> Get-Help Get-Process -Example -``` - -PowerShell Pipeline '|' ----- -Sometimes when you run Get-ChildItem or "dir", you want to get a list of files and folders in a descending order. -To achieve that, type: -```PowerShell -PS /home/jen> dir | Sort-Object -Descending -``` -Say you want to get the largest file in a directory -```PowerShell -PS /home/jen> dir | Sort-Object -Property Length -Descending | Select-Object -First 1 - - - Directory: /home/jen - - -Mode LastWriteTime Length Name ----- ------------- ------ ---- --a---- 5/16/2016 1:15 PM 32972 test.log - -``` -How to Create and Run PowerShell scripts ----- -You can use Visual Studio Code or your favorite editor to create a PowerShell script and save it with a `.ps1` file extension. -For more details, see [Create and Run PowerShell Script Guide][create-run-script] - - -Recommended Training and Reading ----- -- Video: [Get Started with PowerShell][remoting] from Channel9 -- [eBooks from PowerShell.org](https://powershell.org/ebooks/) -- [eBooks from PowerShell.com][ebooks-powershell.com] -- [eBooks List][ebook-list] by Martin Schvartzman -- [Tutorial from MVP][tutorial] -- Script Guy blog: [The best way to Learn PowerShell][to-learn] -- [Understanding PowerShell Module][ps-module] -- [How and When to Create PowerShell Module][create-ps-module] by Adam Bertram -- Video: [PowerShell Remoting in Depth][in-depth] from Channel9 -- [PowerShell Basics: Remote Management][remote-mgmt] from ITPro -- [Running Remote Commands][remote-commands] from PowerShell Web Docs -- [Samples for PowerShell Scripts][examples] -- [Samples for Writing a PowerShell Script Module][examples-ps-module] -- [Writing a PowerShell module in C#][writing-ps-module] -- [Examples of Cmdlets Code][sample-code] - - -Commercial Resources ----- -- [Windows PowerShell in Action][in-action] by Bruce Payette -- [Windows PowerShell Cookbook][cookbook] by Lee Holmes - -[in-action]: https://www.amazon.com/Windows-PowerShell-Action-Second-Payette/dp/1935182137 -[cookbook]: http://shop.oreilly.com/product/9780596801519.do -[ebook-list]: https://blogs.technet.microsoft.com/pstips/2014/05/26/free-powershell-ebooks/ -[ebooks-powershell.com]: http://powershell.com/cs/blogs/ebookv2/default.aspx -[tutorial]: http://www.computerperformance.co.uk/powershell/index.htm -[to-learn]:https://blogs.technet.microsoft.com/heyscriptingguy/2015/01/04/weekend-scripter-the-best-ways-to-learn-powershell/ -[ps-module]:https://msdn.microsoft.com/en-us/library/dd878324%28v=vs.85%29.aspx -[create-ps-module]:http://www.tomsitpro.com/articles/powershell-modules,2-846.html -[remoting]:https://channel9.msdn.com/Series/GetStartedPowerShell3/06 -[in-depth]: https://channel9.msdn.com/events/MMS/2012/SV-B406 -[remote-mgmt]:http://windowsitpro.com/powershell/powershell-basics-remote-management -[remote-commands]:https://msdn.microsoft.com/en-us/powershell/scripting/core-powershell/running-remote-commands -[examples]:http://examples.oreilly.com/9780596528492/ -[examples-ps-module]:https://msdn.microsoft.com/en-us/library/dd878340%28v=vs.85%29.aspx -[writing-ps-module]:http://www.powershellmagazine.com/2014/03/18/writing-a-powershell-module-in-c-part-1-the-basics/ -[sample-code]:https://msdn.microsoft.com/en-us/library/ff602031%28v=vs.85%29.aspx -[create-run-script]:./create-powershell-scripts.md diff --git a/docs/learning-powershell/using-vscode.md b/docs/learning-powershell/using-vscode.md deleted file mode 100644 index d73ecedc11b..00000000000 --- a/docs/learning-powershell/using-vscode.md +++ /dev/null @@ -1,181 +0,0 @@ -Using Visual Studio Code for PowerShell Development -==== - -If you are working on Linux and macOS, you cannot use the PowerShell ISE because it is not supported on these platforms. -In this case, you can choose your favorite editor to write PowerShell scripts. -Here we choose Visual Studio Code as a PowerShell editor. - -You can use Visual Studio Code on Windows with PowerShell version 5 by using Windows 10 or by installing [Windows Management Framework 5.0 RTM](https://www.microsoft.com/en-us/download/details.aspx?id=50395) for down-level Windows OSs (e.g. Windows 8.1, etc.). - -Before starting it, please make sure PowerShell exists on your system. -By following the [Installing PowerShell](./README.md#installing-powershell) instructions you can install PowerShell and launch a PowerShell session. - -Editing with Visual Studio Code ----- -[**1. Installing Visual Studio Code**](https://code.visualstudio.com/Docs/setup/setup-overview) - -* **Linux**: follow the installation instructions on the [Running VS Code on Linux](https://code.visualstudio.com/docs/setup/linux) page - -* **macOS**: follow the installation instructions on the [Running VS Code on macOS](https://code.visualstudio.com/docs/setup/mac) page - - **NOTE:** On OS X you must install OpenSSL for the PowerShell extension to work correctly. - The easiest way to accomplish this is to install [Homebrew](http://brew.sh/) and then run `brew install openssl`. - The PowerShell extension will now be able to load successfully. - -* **Windows**: follow the installation instructions on the [Running VS Code on Windows](https://code.visualstudio.com/docs/setup/windows) page - - -**2. Installing PowerShell Extension** - -- Launch the Visual Studio Code app by: - * **Windows**: typing **code** in your PowerShell session - * **Linux**: typing **code** in your terminal - * **macOS**: typing **code** in your terminal - -- Launch **Quick Open** by pressing **Ctrl+P** (**Cmd+P** on Mac). -- In Quick Open, type **ext install powershell** and hit **Enter**. -- The **Extensions** view will open on the Side Bar. Select the PowerShell extension from Microsoft. - You will see something like below: - - ![VSCode](vscode.png) - -- Click the **Install** button on the PowerShell extension from Microsoft. -- After the install, you will see the **Install** button turns to **Reload**. - Click on **Reload**. -- After Visual Studio Code has reload, you are ready for editing. - -For example, to create a new file, click **File->New**. -To save it, click **File->Save** and then provide a file name, let's say "HelloWorld.ps1". -To close the file, click on "x" next to the file name. -To exit Visual Studio Code, **File->Exit**. - -#### Using a specific installed version of PowerShell - -If you wish to use a specific installation of PowerShell with Visual Studio Code, you will need to add a new variable to your user settings file. - -1. Click **File -> Preferences -> Settings** -2. Two editor panes will appear. - In the right-most pane (`settings.json`), insert the setting below appropriate for your OS somewhere between the two curly brackets (`{` and `}`) and replace ** with the installed PowerShell version: - - ```json - // On Windows: - "powershell.developer.powerShellExePath": "c:/Program Files/PowerShell//powershell.exe" - - // On Linux: - "powershell.developer.powerShellExePath": "/opt/microsoft/powershell//powershell" - - // On macOS: - "powershell.developer.powerShellExePath": "/usr/local/microsoft/powershell//powershell" - ``` - -3. Replace the setting with the path to the desired PowerShell executable -4. Save the settings file and restart Visual Studio Code - -#### Configuration settings for Visual Studio Code - -By using the steps in the previous paragraph you can add configuration settings in `settings.json`. - -We recommend the following configuration settings for Visual Studio Code: - -```json -{ - "csharp.suppressDotnetRestoreNotification": true, - "editor.renderWhitespace": "all", - "editor.renderControlCharacters": true, - "omnisharp.projectLoadTimeout": 120, - "files.trimTrailingWhitespace": true -} -``` - -Debugging with Visual Studio Code ----- -### No-workspace debugging -As of Visual Studio Code version 1.9 you can debug PowerShell scripts without having to open the folder containing the PowerShell script. -Simply open the PowerShell script file with **File->Open File...**, set a breakpoint on a line (press F9) and then press F5 to start debugging. -You will see the Debug actions pane appear which allows you to break into the debugger, step, resume and stop debugging. - -### Workspace debugging -Workspace debugging refers to debugging in the context of a folder that you have opened in Visual Studio Code using **Open Folder...** from the **File** menu. -The folder you open is typically your PowerShell project folder and/or the root of your Git repository. - -Even in this mode, you can start debugging the currently selected PowerShell script by simply pressing F5. -However, workspace debugging allows you to define multiple debug configurations other than just debugging the currently open file. -For instance, you can add a configurations to: - -* Launch Pester tests in the debugger -* Launch a specific file with arguments in the debugger -* Launch an interactive session in the debugger -* Attach the debugger to a PowerShell host process - -Follow these steps to create your debug configuration file: -1. Open the **Debug** view by pressing **Ctrl+Shift+D** (**Cmd+Shift+D** on Mac). -2. Press the **Configure** gear icon in the toolbar. -3. Visual Studio Code will prompt you to **Select Environment**. -Choose **PowerShell**. - - When you do this, Visual Studio Code creates a directory and a file ".vscode\launch.json" in the root of your workspace folder. - This is where your debug configuration is stored. If your files are in a Git repository, you will typically want to commit the launch.json file. - The contents of the launch.json file are: - -```json -{ - "version": "0.2.0", - "configurations": [ - { - "type": "PowerShell", - "request": "launch", - "name": "PowerShell Launch (current file)", - "script": "${file}", - "args": [], - "cwd": "${file}" - }, - { - "type": "PowerShell", - "request": "attach", - "name": "PowerShell Attach to Host Process", - "processId": "${command.PickPSHostProcess}", - "runspaceId": 1 - }, - { - "type": "PowerShell", - "request": "launch", - "name": "PowerShell Interactive Session", - "cwd": "${workspaceRoot}" - } - ] -} - -``` -This represents the common debug scenarios. -However, when you open this file in the editor, you will see an **Add Configuration...** button. -You can press this button to add more PowerShell debug configurations. One handy configuration to add is **PowerShell: Launch Script**. -With this configuration, you can specify a specific file with optional arguments that should be launched whenever you press F5 no matter which file is currently active in the editor. - -Once the debug configuration is established, you can select which configuration you want to use during a debug session by selecting one from the debug configuration drop-down in the **Debug** view's toolbar. - -There are a few blogs that may be helpful to get you started using PowerShell extension for Visual Studio Code - -- Visual Studio Code: [PowerShell Extension][ps-extension] -- [Write and debug PowerShell scripts in Visual Studio Code][debug] -- [Debugging Visual Studio Code Guidance][vscode-guide] -- [Debugging PowerShell in Visual Studio Code][ps-vscode] -- [Get started with PowerShell development in Visual Studio Code][getting-started] -- [Visual Studio Code editing features for PowerShell development – Part 1][editing-part1] -- [Visual Studio Code editing features for PowerShell development – Part 2][editing-part2] -- [Debugging PowerShell script in Visual Studio Code – Part 1][debugging-part1] -- [Debugging PowerShell script in Visual Studio Code – Part 2][debugging-part2] - -[ps-extension]:https://blogs.msdn.microsoft.com/cdndevs/2015/12/11/visual-studio-code-powershell-extension/ -[debug]:https://blogs.msdn.microsoft.com/powershell/2015/11/16/announcing-powershell-language-support-for-visual-studio-code-and-more/ -[vscode-guide]:https://johnpapa.net/debugging-with-visual-studio-code/ -[ps-vscode]:https://github.com/PowerShell/vscode-powershell/tree/master/examples -[getting-started]:https://blogs.technet.microsoft.com/heyscriptingguy/2016/12/05/get-started-with-powershell-development-in-visual-studio-code/ -[editing-part1]:https://blogs.technet.microsoft.com/heyscriptingguy/2017/01/11/visual-studio-code-editing-features-for-powershell-development-part-1/ -[editing-part2]:https://blogs.technet.microsoft.com/heyscriptingguy/2017/01/12/visual-studio-code-editing-features-for-powershell-development-part-2/ -[debugging-part1]:https://blogs.technet.microsoft.com/heyscriptingguy/2017/02/06/debugging-powershell-script-in-visual-studio-code-part-1/ -[debugging-part2]:https://blogs.technet.microsoft.com/heyscriptingguy/2017/02/13/debugging-powershell-script-in-visual-studio-code-part-2/ - -PowerShell Extension for Visual Studio Code ----- - -The PowerShell extension's source code can be found on [GitHub](https://github.com/PowerShell/vscode-powershell). diff --git a/docs/learning-powershell/vscode.png b/docs/learning-powershell/vscode.png deleted file mode 100644 index 4c9354bd0c9..00000000000 Binary files a/docs/learning-powershell/vscode.png and /dev/null differ diff --git a/docs/learning-powershell/working-with-powershell-objects.md b/docs/learning-powershell/working-with-powershell-objects.md deleted file mode 100644 index adbdc3eeef6..00000000000 --- a/docs/learning-powershell/working-with-powershell-objects.md +++ /dev/null @@ -1,124 +0,0 @@ -Working with PowerShell Objects -==== -When cmdlets are executed in PowerShell, the output is an Object, as opposed to only returning text. -This provides the ability to store information as properties. -As a result, handling large amounts of data and getting only specific properties is a trivial task. - -As a simple example, the following function retrieves information about storage Devices on a Linux or MacOS operating system platform. -This is accomplished by parsing the output of an existing command, *parted -l* in administrative context, and creating an object from the raw text by using the *New-Object* cmdlet. - -```PowerShell -Function Get-DiskInfo { - $disks = sudo parted -l | Select-String "Disk /dev/sd*" -Context 1,0 - $diskinfo = @() - foreach ($disk in $disks) { - $diskline1 = $disk.ToString().Split("`n")[0].ToString().Replace(' Model: ','') - $diskline2 = $disk.ToString().Split("`n")[1].ToString().Replace('> Disk ','') - $i = New-Object psobject -Property @{'Friendly Name' = $diskline1; Device=$diskline2.Split(': ')[0]; 'Total Size'=$diskline2.Split(':')[1]} - $diskinfo += $i - } - $diskinfo -} -``` - -Execute the function and store the results as a variable. -Now retrieve the value of the variable. -The results are formatted as a table with the default view. - -*Note: in this example, the disks are virtual disks in a Microsoft Azure virtual machine.* - -```PowerShell -PS /home/psuser> $d = Get-DiskInfo -[sudo] password for psuser: -PS /home/psuser> $d - -Friendly Name Total Size Device -------------- ---------- ------ -Msft Virtual Disk (scsi) 31.5GB /dev/sda -Msft Virtual Disk (scsi) 145GB /dev/sdb - -``` - -Passing the variable down the pipeline to *Get-Member* reveals available methods and properties. -This is because the value of *$d* is not just text output. -The value is actually an array of .Net objects with methods and properties. -The properties include Device, Friendly Name, and Total Size. - -```PowerShell -PS /home/psuser> $d | Get-Member - - - TypeName: System.Management.Automation.PSCustomObject - -Name MemberType Definition ----- ---------- ---------- -Equals Method bool Equals(System.Object obj) -GetHashCode Method int GetHashCode() -GetType Method type GetType() -ToString Method string ToString() -Device NoteProperty string Device=/dev/sda -Friendly Name NoteProperty string Friendly Name=Msft Virtual Disk (scsi) -Total Size NoteProperty string Total Size= 31.5GB -``` - -To confirm, we can call the GetType() method interactively from the console. - -```PowerShell -PS /home/psuser> $d.GetType() - -IsPublic IsSerial Name BaseType --------- -------- ---- -------- -True True Object[] System.Array -``` - -To index in to the array and return only specific objects, use the square brackets. - -```PowerShell -PS /home/psuser> $d[0] - -Friendly Name Total Size Device -------------- ---------- ------ -Msft Virtual Disk (scsi) 31.5GB /dev/sda - -PS /home/psuser> $d[0].GetType() - -IsPublic IsSerial Name BaseType --------- -------- ---- -------- -True False PSCustomObject System.Object -``` - -To return a specific property, the property name can be called interactively from the console. - -```PowerShell -PS /home/psuser> $d.Device -/dev/sda -/dev/sdb -``` - -To output a view of the information other than default, such as a view with only specific properties selected, pass the value to the *Select-Object* cmdlet. - -```PowerShell -PS /home/psuser> $d | Select-Object Device, 'Total Size' - -Device Total Size ------- ---------- -/dev/sda 31.5GB -/dev/sdb 145GB -``` - -Finally, the example below demonstrates use of the *ForEach-Object* cmdlet to iterate through the array and manipulate the value of a specific property of each object. -In this case the Total Size property, which was given in Gigabytes, is changed to Megabytes. -Alternatively, index in to a position in the array as shown below in the third example. - -```PowerShell -PS /home/psuser> $d | ForEach-Object 'Total Size' - 31.5GB - 145GB - -PS /home/psuser> $d | ForEach-Object {$_.'Total Size' / 1MB} -32256 -148480 - -PS /home/psuser> $d[1].'Total Size' / 1MB -148480 -``` \ No newline at end of file diff --git a/docs/maintainers/Images/merge-commit-confirm.png b/docs/maintainers/Images/merge-commit-confirm.png new file mode 100644 index 00000000000..a26e1aa0b4f Binary files /dev/null and b/docs/maintainers/Images/merge-commit-confirm.png differ diff --git a/docs/maintainers/Images/merge-commit.png b/docs/maintainers/Images/merge-commit.png new file mode 100644 index 00000000000..2ae3b6a8ba5 Binary files /dev/null and b/docs/maintainers/Images/merge-commit.png differ diff --git a/docs/maintainers/Images/squash-confirm.png b/docs/maintainers/Images/squash-confirm.png new file mode 100644 index 00000000000..3a5df89be14 Binary files /dev/null and b/docs/maintainers/Images/squash-confirm.png differ diff --git a/docs/maintainers/Images/squash-merge.png b/docs/maintainers/Images/squash-merge.png new file mode 100644 index 00000000000..98147cdd6a7 Binary files /dev/null and b/docs/maintainers/Images/squash-merge.png differ diff --git a/docs/maintainers/README.md b/docs/maintainers/README.md index 5a8a24176ae..ebba4b02258 100644 --- a/docs/maintainers/README.md +++ b/docs/maintainers/README.md @@ -3,11 +3,11 @@ Repository Maintainers are trusted stewards of the PowerShell repository responsible for maintaining consistency and quality of PowerShell code. One of their primary responsibilities is merging pull requests after all requirements have been fulfilled. -They have [write access](https://help.github.com/articles/repository-permission-levels-for-an-organization/) to the PowerShell repositories which gives them the power to: +They have [write access](https://docs.github.com/en/free-pro-team@latest/github/setting-up-and-managing-organizations-and-teams/repository-permission-levels-for-an-organization) to the PowerShell repositories which gives them the power to: 1. `git push` to the official PowerShell repository -1. Merge pull requests -1. Assign labels, milestones, and people to [issues](https://guides.github.com/features/issues/) +1. Merge [pull requests](https://docs.github.com/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-pull-requests) +1. Assign labels, milestones, and people to [issues](https://guides.github.com/features/issues/) and [pull requests](https://docs.github.com/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/about-pull-requests) ## Table of Contents @@ -15,19 +15,27 @@ They have [write access](https://help.github.com/articles/repository-permission- - [Repository Maintainer Responsibilities](#repository-maintainer-responsibilities) - [Issue Management Process](#issue-management-process) - [Pull Request Workflow](#pull-request-workflow) - - [Abandoned Pull Requests](#abandoned-pull-requests) - [Becoming a Repository Maintainer](#becoming-a-repository-maintainer) ## Current Repository Maintainers -* Sergei Vorobev ([vors](https://github.com/vors)) -* Jason Shirk ([lzybkr](https://github.com/lzybkr)) -* Dongbo Wang ([daxian-dbw](https://github.com/daxian-dbw)) -* Travis Plunk ([TravisEz13](https://github.com/TravisEz13)) -* Mike Richmond ([mirichmo](https://github.com/mirichmo)) -* Andy Schwartzmeyer ([andschwa](https://github.com/andschwa)) -* Aditya Patwardhan ([adityapatwardhan](https://github.com/adityapatwardhan)) -* Ilya Sazonov ([iSazonov](https://github.com/iSazonov)) + + +- Aditya Patwardhan ([adityapatwardhan](https://github.com/adityapatwardhan)) +- Andrew Menagarishvili ([anmenaga](https://github.com/anmenaga)) +- Dongbo Wang ([daxian-dbw](https://github.com/daxian-dbw)) +- Ilya Sazonov ([iSazonov](https://github.com/iSazonov)) +- Robert Holt ([rjmholt](https://github.com/rjmholt)) +- Travis Plunk ([TravisEz13](https://github.com/TravisEz13)) + +## Former Repository Maintainers + + + +- Andy Jordan ([andyleejordan](https://github.com/andyleejordan)) +- Jason Shirk ([lzybkr](https://github.com/lzybkr)) +- Mike Richmond ([mirichmo](https://github.com/mirichmo)) +- Sergei Vorobev ([vors](https://github.com/vors)) ## Repository Maintainer Responsibilities @@ -35,7 +43,8 @@ Repository Maintainers enable rapid contributions while maintaining a high level If you are a Repository Maintainer, you: -1. **MUST** ensure that each contributor has signed a valid Contributor License Agreement (CLA) +1. **MUST** abide by the [Code of Conduct](../../CODE_OF_CONDUCT.md) and report suspected violations to the [PowerShell Committee][ps-committee] +1. **MUST** ensure that each contributor has signed a valid Microsoft Contributor License Agreement (CLA) 1. **MUST** verify compliance with any third party code license terms (e.g., requiring attribution, etc.) if the contribution contains third party code. 1. **MUST** make sure that [any change requiring approval from the PowerShell Committee](../community/governance.md#changes-that-require-an-rfc) has gone through the proper [RFC][RFC-repo] or approval process 1. **MUST** validate that code reviews have been conducted before merging a pull request when no code is written @@ -54,10 +63,10 @@ If you are a Repository Maintainer, you: However, they should be reminded to create an issue in the future to frontload any potential problems with the work and to minimize duplication of efforts. 1. **SHOULD** encourage contributors to create meaningful titles for all PRs. Edit the title if necessary to provide clarity on the problem -1. **SHOULD** encourage contributes to write meaningful, descriptive git commits +1. **SHOULD** encourage contributors to write meaningful, descriptive git commits 1. **SHOULD NOT** merge pull requests with a failed CI build (unless, for instance, the pull request is being submitted to fix broken CI) -1. **SHOULD NOT** merge pull requests without the label `cla-signed` or `cla-not-required` from the Microsoft CLA bot +1. **SHOULD NOT** merge pull requests without the status check passing from the Microsoft CLA bot (unless the CLA bot is broken, and CLA signing can be confirmed through other means) 1. **SHOULD NOT** merge pull requests too quickly after they're submitted. Even if the pull request meets all the requirements, people should have time to give their input @@ -71,32 +80,11 @@ Please see [Issue Management][issue-management] ## Pull Request Workflow -1. A contributor opens a pull request. -1. The contributor ensures that their pull request passes the [CI system][ci-system] build. - - If the build fails, a maintainer adds the ```waiting for author``` label to the pull request. - The contributor can then continue to update the pull request until the build passes. -1. Once the build passes, the maintainer either reviews the pull request immediately or adds the ```need review``` label. -1. A maintainer or trusted contributor reviews the pull request code. - - If the contributor does not meet the reviewer's standards, the reviewer makes comments. - A maintainer then removes the ```need review``` label and adds the ```waiting for author``` label. - The contributor must address the comments and repeat from step 2. - - If the contributor meets the reviewer's standards, the reviewer comments that they are satisfied. - A maintainer then removes the ```need review``` label. -1. Once the code review is completed, a maintainer merges the pull request. - -### Abandoned Pull Requests - -A pull request with the label ```waiting for the author``` for **more than two weeks** without a word from the author is considered abandoned. - -In these cases: - -1. Ping the author of PR to remind him of pending changes. - - If the contributor responds, it's no longer an abandoned pull request, proceed as normal. -1. If the contributor does not respond **within a week**: - - Create a new branch with the changes and open an issue to merge the code into the dev branch. - Mention the original pull request ID in the description of the new issue and close the abandoned pull request. - - If the changes in an abandoned pull request are no longer needed (e.g. due to refactoring of - the code base or a design change), simply close the pull request. +Please see [Contributing][CONTRIBUTING] + +## Maintainer Best Practices + +Please see [Best Practices][best-practice] ## Becoming a Repository Maintainer @@ -107,10 +95,12 @@ Eligibility is heavily dependent on the level of contribution and expertise: ind At any point in time, the existing Repository Maintainers can unanimously nominate a strong community member to become a Repository Maintainer. Nominations are brought to the PowerShell Committee to understand the reasons and justification. A simple majority of the PowerShell Committee is required to veto the nomination. -Once a nominee has been approved, a PR will be submitted by a current Maintainer to update this document to add the nominee's name to -the [Current Repository Maintainers](#Current-Repository-Maintainers) with justification as the description of the PR to serve as the public announcement. +When a nominee has been approved, a PR will be submitted by a current Maintainer to update this document to add the nominee's name to +the [Current Repository Maintainers](#current-repository-maintainers) with justification as the description of the PR to serve as the public announcement. [RFC-repo]: https://github.com/PowerShell/PowerShell-RFC [ci-system]: ../testing-guidelines/testing-guidelines.md#ci-system [issue-management]: issue-management.md [CONTRIBUTING]: ../../.github/CONTRIBUTING.md +[best-practice]: best-practice.md +[ps-committee]: ../community/governance.md#powershell-committee diff --git a/docs/maintainers/best-practice.md b/docs/maintainers/best-practice.md new file mode 100644 index 00000000000..2496e1ae42b --- /dev/null +++ b/docs/maintainers/best-practice.md @@ -0,0 +1,50 @@ +# Maintainer Best Practices + +## PR Types + +- Feature-work PR: A PR that implements an RFC, which usually involves relatively large set of changes. +- Regular PR: A bug fix or an enhancement change that are not backed by an RFC. + +## Review PRs + +- Ask the author to reword the PR title based on guidelines in [Contributing](../../.github/CONTRIBUTING.md). +- Ask the author to apply `[feature]` tag to trigger full test builds if it's necessary. +- Label the PR with `Breaking-Change`, `Documentation Needed` and `Area-XXX` as appropriate. +- When labelling a PR with `Review-Committee`, leave a detailed comment to summarize the issue you want the committee to look into. + It's recommended to include examples to explain/demonstrate behaviors. + +## Merge PRs + +- Use `Squash and merge` by default to keep clean commit history in Master branch. + + ![Squash Merge Example](./Images/squash-merge.png)    ![Squash Confirm Example](./Images/squash-confirm.png) + +- Use `Create a merge commit` for feature-work PRs **only if** the commit history of the PR is reasonably clean. + After using this option, GitHub will make it your default option for merging a PR. + Do remember to change the default back to `Squash and merge` as it will be useful next time. + + ![Merge Commit Example](./Images/merge-commit.png)    ![Merge Confirm Example](./Images/merge-commit-confirm.png) + +- Avoid `Rebase and merge` unless you have a strong argument for using it. + +- Before clicking `Confirm squash and merge` or `Confirm merge`, + make sure you run through the following steps: + + 1. The commit title should be a short summary of the PR. + + - When merging with the `Squash and merge` option, + the PR title will be used as the commit title by default. + **Reword the title as needed** to make sure it makes sense (can be used without change in `CHANGELOG.md`). + + - When merging with the `Create a merge commit` option, + the default commit title would be `Merge pull request XXX from YYY`. + **Replace it with a short summary of the PR**, and add the PR number to the end, like `(#1234)`. + + 1. The optional extended description is required for feature-work PRs, or regular PRs with breaking changes. + For other PRs, it's not required but good to have based on the judgement of the maintainer. + + - If a PR introduces breaking changes from the previous stable release, + make sure you put the tag `[breaking change]` at the first line of the extended description, + and start the description text from the second line. + + 1. Use the present tense and imperative mood for both the commit title and description. diff --git a/docs/maintainers/issue-management.md b/docs/maintainers/issue-management.md index 71c547ee332..0cc8eb00e37 100644 --- a/docs/maintainers/issue-management.md +++ b/docs/maintainers/issue-management.md @@ -1,12 +1,20 @@ # Issue Management +## Security Vulnerabilities + +If you believe that there is a security vulnerability in PowerShell, +first follow the [vulnerability issue reporting policy](../../.github/SECURITY.md) before submitting an issue. + ## Long-living issue labels -======= -## Issue and PR Labels + +Issue labels for PowerShell/PowerShell can be found [here](https://github.com/powershell/powershell/labels). + +### Issue and PR Labels Issues are opened for many different reasons. We use the following labels for issue classifications: +* `Issue-Announcement`: the issue is for discussing an [Announcement](https://github.com/PowerShell/Announcements) * `Issue-Bug`: the issue is reporting a bug * `Issue-Code Cleanup`: the issue is for cleaning up the code with no impact on functionality * `Issue-Discussion`: the issue may not have a clear classification yet. @@ -14,7 +22,7 @@ We use the following labels for issue classifications: * `Issue-Enhancement`: the issue is more of a feature request than a bug. * `Issue-Meta`: an issue used to track multiple issues. * `Issue-Question`: ideally support can be provided via other mechanisms, - but sometimes folks to open an issue to get a question answered and we will use this label for such issues. + but sometimes folks do open an issue to get a question answered and we will use this label for such issues. [ln-rfc]: https://github.com/PowerShell/PowerShell-RFC @@ -31,36 +39,40 @@ When an issue is resolved, the following labels are used to describe the resolut ### Feature areas -These labels describe what feature area of PowerShell that an issue affects: +These labels describe what feature area of PowerShell that an issue affects. +Those labels denoted by `WG-*` are owned by a Working Group (WG) defined +[here](../community/working-group-definitions.md): -* `Area-Build`: build issues +* `Area-Maintainers-Build`: build issues * `Area-Cmdlets-Core`: cmdlets in the Microsoft.PowerShell.Core module * `Area-Cmdlets-Utility`: cmdlets in the Microsoft.PowerShell.Utility module * `Area-Cmdlets-Management`: cmdlets in the Microsoft.PowerShell.Management module -* `Area-Console`: the console experience -* `Area-Debugging`: debugging PowerShell script -* `Area-Demo`: a demo or sample * `Area-Documentation`: PowerShell *repo* documentation issues, general PowerShell doc issues go [here](https://github.com/PowerShell/PowerShell-Docs/issues) * `Area-DSC`: DSC related issues -* `Area-Engine`: core PowerShell engine, interpreter, runtime -* `Area-HelpSystem`: anything related to the help infrastructure and formatting of help -* `Area-Intellisense`: tab completion -* `Area-Language`: parser, language semantics -* `Area-OMI`: omi -* `Area-PackageManagement`: PackageManagement related issues -* `Area-Performance`: a performance issue -* `Area-Portability`: anything affecting script portability * `Area-PowerShellGet`: PowerShellGet related issues -* `Area-Providers`: PowerShell providers like FileSystem, Certificates, Registry, etc... -* `Area-PSReadline`: PSReadline related issues -* `Area-Remoting`: PSRP issues with any transport layer -* `Area-Security`: security related areas like [JEA](https://github.com/powershell/JEA) * `Area-SideBySide`: side by side support -* `Area-Test`: issues in a test or in test infrastructure +* `WG-DevEx-Portability`: anything related to authoring cross-platform or cross-architecture + modules, cmdlets, and scripts +* `WG-DevEx-SDK`: anything related to hosting PowerShell as a runtime, PowerShell's APIs, + PowerShell Standard, or the development of modules and cmdlets +* `WG-Engine`: core PowerShell engine, interpreter, and runtime +* `WG-Engine-Performance`: core PowerShell engine, interpreter, and runtime performance +* `WG-Engine-Providers`: built-in PowerShell providers such as FileSystem, Certificates, + Registry, etc. (or anything returned by `Get-PSProvider`) +* `WG-Interactive-Console`: the console experience +* `WG-Interactive-Debugging`: debugging PowerShell script +* `WG-Interactive-HelpSystem`: anything related to the help infrastructure and formatting of help +* `WG-Interactive-IntelliSense`: tab completion +* `WG-Interactive-PSReadline`: PSReadline related issues +* `WG-Language`: parser, language semantics +* `WG-Quality-Test`: issues in a test or in test infrastructure +* `WG-Remoting`: PSRP issues with any transport layer +* `WG-Security`: security related areas such as [JEA](https://github.com/powershell/JEA) ### Operating Systems These are for issues that are specific to certain Operating Systems: + * `OS-Linux` * `OS-macOS` * `OS-Windows` @@ -72,21 +84,27 @@ The following labels are used on PRs: * `Review - Needed` : The PR is being reviewed. Please see [Pull Request - Code Review](https://github.com/PowerShell/PowerShell/blob/master/.github/CONTRIBUTING.md#pull-request---code-review) * `Review - Waiting on Author` : The PR was reviewed by the team and requires changes or comments from the author before being accepted. -* `Review - Abandoned` : The PR was not updated for significant number of days (the exact number could vary over time). +* `Review - Abandoned` : The PR was not updated for a significant number of days (the exact number could vary over time). Maintainers should look into such PRs and re-evaluate them. * `Review - Committee` : The PR/Issue needs a review from [powershell-committee](../community/governance.md#powershell-committee) ### Miscellaneous labels +* `Blocked` : An issue cannot be addressed due to external factors, + but should not be closed because those external factors are temporary. +* `BVT/DRT` : An issue affecting or exposed by tests that have not been open sourced. +* `Changelog Needed` : The PR requires an addition to the changelog, + and should be removed when it has been added. * `Committee-Reviewed` : The PR/Issue has been reviewed by the [powershell-committee](../community/governance.md#powershell-committee) -* `Up-for-Grabs`: We've acknowledged the issue but have no immediate plans to address it. +* `Compliance` : Issues with the compliance label are required to be fixed either in the long term or short term for + Microsoft to continue to sign and release packages from the project as official Microsoft packages. + The time frame in which it needs to be fixed should be identified in the issue. +* `Documentation Needed` : The PR has changes that require a documentation change or new documentation added to [PowerShell-Docs](https://github.com/powershell/powershell-docs) +* `First-Time-Issue` : An issue that is identified as being easy and a good candidate for first time contributors +* `Hackathon` or `Hacktoberfest` : An issue that would be a good candidate for hackathons such as `Hacktoberfest` or `Hackillinois` +* `Porting` : An issue that affects a feature not yet ported to other platforms. +* `Up-for-Grabs` : We've acknowledged the issue but have no immediate plans to address it. If you're looking for a way to contribute, these issues can be a good place to start. -* `Blocked`: an issue cannot be addressed due to external factors, - but should not be closed because those external factors are temporary. -* `BVT/DRT`: an issue affecting or exposed by tests that have not been open sourced. -* `Porting`: an issue that affects a feature not yet ported to other platforms. -* `Usability`: this label is used to help us filter issues that might be higher priority +* `Usability` : This label is used to help us filter issues that might be higher priority because they more directly affect the usability of a particular feature or area. -* `Changelog Needed`: The PR requires an addition to the changelog, - and should be removed when it has been added. -* `Documentation Needed` : The PR has changes that require a documentation change or new documentation added to [PowerShell-Docs](http://github.com/powershell/powershell-docs) +* `Waiting - DotNetCore` : An issue waiting on a fix/change in DotNetCore. diff --git a/docs/maintainers/pull-request-process.md b/docs/maintainers/pull-request-process.md deleted file mode 100644 index f5a7c17d210..00000000000 --- a/docs/maintainers/pull-request-process.md +++ /dev/null @@ -1,32 +0,0 @@ -# Pull Request Process - -Requirements for a pull request to be accepted into PowerShell -* Writing tests -* Writing documentation - -## Pull Request Workflow - -1. A contributor opens a pull request. -1. The contributor ensures that their pull request passes the [CI system][ci-system] build. - - If the build fails, a [Repository Maintainer][repository-maintainer] adds the `Review - waiting on author` label to the pull request. - The contributor can then continue to update the pull request until the build passes. -1. Once the build passes, the maintainer either reviews the pull request immediately or adds the `Review - needed` label. -1. An [Area Expert][area-expert] reviews the pull request code. - - If the contributor does not meet the reviewer's standards, the reviewer makes comments. A maintainer then removes the `Review - needed` label and adds the `Review - waiting on author` label. The contributor must address the comments and repeat from step 2. - - If the contributor meets the reviewer's standards, the reviewer comments that they are satisfied. A maintainer then removes the `need review` label. -1. Once the code review is completed, a maintainer merges the pull request. - -### Abandoned Pull Requests -A pull request with the label `Review - waiting on author` for **more than two weeks** without a word from the author is considered abandoned. - -In these cases: - -1. Ping the author of PR to remind them of pending changes. - - If the contributor responds, it's no longer an abandoned pull request, proceed as normal. -2. If the contributor does not respond **within a week**: - - If the reviewer's comments are very minor, merge the change, fix the code immediately, and create a new PR with the fixes addressing the minor comments. - - If the changes required to merge the pull request are significant but needed, create a new branch with the changes and open an issue to merge the code into the dev branch. Mention the original pull request ID in the description of the new issue and close the abandoned pull request. - - If the changes in an abandoned pull request are no longer needed (e.g. due to refactoring of the code base or a design change), simply close the pull request. - -[repository-maintainer]: ../community/governance.md#repository-maintainers -[area-expert]: ../community/governance.md#area-experts#area-experts diff --git a/docs/maintainers/releasing.md b/docs/maintainers/releasing.md index 5502d3dcb6d..3562962e68f 100644 --- a/docs/maintainers/releasing.md +++ b/docs/maintainers/releasing.md @@ -14,28 +14,23 @@ This is to help track the release preparation work. > Note: Step 2, 3 and 4 can be done in parallel. -1. Create a branch named `release` in `PowerShell/PowerShell` repository. +1. Create a branch named `release-` in our private repository. All release related changes should happen in this branch. 1. Prepare packages - [Build release packages](#building-packages). - Sign the MSI packages and DEB/RPM packages. - Install and verify the packages. 1. Update documentation, scripts and Dockerfiles - - Summarize the change log for the release. It should be reviewed by PM(s) to make it more user-friendly. - - Update [CHANGELOG.md](../../CHANGELOG.md) with the finalized change log draft. + - Summarize the changelog for the release. It should be reviewed by PM(s) to make it more user-friendly. + - Update [CHANGELOG.md](../../CHANGELOG.md) with the finalized changelog draft. - Update other documents and scripts to use the new package names and links. 1. Verify the release Dockerfiles. 1. [Create NuGet packages](#nuget-packages) and publish them to [powershell-core feed][ps-core-feed]. 1. [Create the release tag](#release-tag) and push the tag to `PowerShell/PowerShell` repository. -1. Create the draft and publish the release in Github. -1. Merge the `release` branch to `master` and delete the `release` branch. +1. Create the draft and publish the release in GitHub. +1. Merge the `release-` branch to `master` in `powershell/powershell` and delete the `release-` branch. 1. Publish Linux packages to Microsoft YUM/APT repositories. -1. Trigger the release docker builds for Linux and Windows container images. - - Linux: push a branch named `docker` to `powershell/powershell` repository to trigger the build at [powershell docker hub](https://hub.docker.com/r/microsoft/powershell/builds/). - Delete the `docker` branch once the builds succeed. - - Windows: queue a new build in `PowerShell Windows Docker Build` on VSTS. -1. Verify the generated docker container images. -1. [Update the homebrew formula](#homebrew) for the OSX package. +1. [Update the homebrew formula](#homebrew) for the macOS package. This task usually will be taken care of by the community, so we can wait for one day or two and see if the homebrew formula has already been updated, and only do the update if it hasn't. @@ -43,7 +38,7 @@ This is to help track the release preparation work. ## Building Packages > Note: Linux and Windows packages are taken care of by our release build pipeline in VSTS, -while the OSX package needs to be built separately on a macOS. +while the macOS package needs to be built separately on a macOS. The release build should be started based on the `release` branch. The release Git tag won't be created until all release preparation tasks are done, @@ -77,11 +72,18 @@ The output of `Start-PSBuild` includes a `powershell.exe` executable which can s #### Linux / macOS The `Start-PSPackage` function delegates to `New-UnixPackage`. -It relies on the [Effing Package Management][fpm] project, -which makes building packages for any (non-Windows) platform a breeze. -Similarly, the PowerShell man-page is generated from the Markdown-like file -[`assets/powershell.1.ronn`][man] using [Ronn][]. -The function `Start-PSBootstrap -Package` will install both these tools. + +For **Linux** (Debian-based distributions), it relies on the [Effing Package Management][fpm] project, +which makes building packages a breeze. + +For **macOS**, it uses native packaging tools (`pkgbuild` and `productbuild`) from Xcode Command Line Tools, +eliminating the need for Ruby or fpm. + +For **Linux** (Red Hat-based distributions), it uses `rpmbuild` directly. + +The PowerShell man-page is generated from the Markdown-like file +[`assets/pwsh.1.ronn`][man] using [Ronn][]. +The function `Start-PSBootstrap -Package` will install these tools. To modify any property of the packages, edit the `New-UnixPackage` function. Please also refer to the function for details on the package properties @@ -94,7 +96,7 @@ license, category, dependencies, and file layout). To support side-by-side Unix packages, we use the following design: We will maintain a `powershell` package -which owns the `/usr/bin/powershell` symlink, +which owns the `/usr/bin/pwsh` symlink, is the latest version, and is upgradeable. This is the only package named `powershell` and similarly is the only package owning any symlinks, @@ -104,7 +106,7 @@ this package will contain actual PowerShell bits (i.e. it is not a meta-package). These bits are installed to `/opt/microsoft/powershell/6.0.0-alpha.8/`, where the version will change with each update -(and is the pre-release version). +(and is the prerelease version). On macOS, the prefix is `/usr/local`, instead of `/opt/microsoft` because it is derived from BSD. @@ -136,7 +138,7 @@ Without `-Name` specified, the primary `powershell` package will instead be created. [fpm]: https://github.com/jordansissel/fpm -[man]: ../../assets/powershell.1.ronn +[man]: ../../assets/manpage/pwsh.1.ronn [ronn]: https://github.com/rtomayko/ronn ### Build and Packaging Examples @@ -173,19 +175,9 @@ Start-PSPackage -Type zip -ReleaseTag v6.0.0-beta.1 -WindowsRuntime 'win7-x64' ## NuGet Packages -In the `release` branch, run `Publish-NuGetFeed` to generate PowerShell NuGet packages: - -```powershell -# Assume the to-be-used release tag is 'v6.0.0-beta.1' -$VersionSuffix = ("v6.0.0-beta.1" -split '-')[-1] - -# Generate NuGet packages -Publish-NuGetFeed -VersionSuffix $VersionSuffix -``` - -PowerShell NuGet packages and the corresponding symbol packages will be generated at `PowerShell/nuget-artifacts` by default. -Currently the NuGet packages published to [powershell-core feed][ps-core-feed] only contain assemblies built for Windows. -Maintainers are working on including the assemblies built for non-Windows platforms. +The NuGet packages for hosting PowerShell for Windows and non-Windows are being built-in our release build pipeline. +The assemblies from the individual Windows and Linux builds are consumed and packed into NuGet packages. +These are then released to [powershell-core feed][ps-core-feed]. [ps-core-feed]: https://powershell.myget.org/gallery/powershell-core @@ -200,7 +192,7 @@ we create an [annotated tag][tag] that names the release. An annotated tag has a message (like a commit), and is *not* the same as a lightweight tag. Create one with `git tag -a v6.0.0-alpha.7 -m `, -and use the release change logs as the message. +and use the release changelogs as the message. Our convention is to prepend the `v` to the semantic version. The summary (first line) of the annotated tag message should be the full release title, e.g. 'v6.0.0-alpha.7 release of PowerShellCore'. @@ -210,24 +202,25 @@ GitHub will see the tag and present it as an option when creating a new [release Start the release, use the annotated tag's summary as the title, and save the release as a draft while you upload the binary packages. -[semver]: http://semver.org/ +[semver]: https://semver.org/ [tag]: https://git-scm.com/book/en/v2/Git-Basics-Tagging [release]: https://help.github.com/articles/creating-releases/ ## Homebrew -After the release, you can update homebrew formula. - -On macOS: - -1. Make sure that you have [homebrew cask](https://caskroom.github.io/). -1. `brew update` -1. `cd /usr/local/Homebrew/Library/Taps/caskroom/homebrew-cask/Casks` -1. Edit `./powershell.rb`, reference [file history](https://github.com/vors/homebrew-cask/commits/master/Casks/powershell.rb) for the guidelines: - 1. Update `version` - 1. Update `sha256` to the checksum of produced `.pkg` (note lower-case string for the consistent style) - 1. Update `checkpoint` value. To do that run `brew cask _appcast_checkpoint --calculate 'https://github.com/PowerShell/PowerShell/releases.atom'` -1. `brew cask style --fix ./powershell.rb`, make sure there are no errors -1. `brew cask audit --download ./powershell.rb`, make sure there are no errors -1. `brew cask reinstall powershell`, make sure that powershell was updates successfully -1. Commit your changes, send a PR to [homebrew-cask](https://github.com/caskroom/homebrew-cask) +After the release, update homebrew formula. +You need macOS to do it. + +There are 2 homebrew formulas: main and preview. + +### Main + +Update it on stable releases. + +1. Wait for a PR to show up in https://github.com/powershell/homebrew-tap, review and merge it. + +### Preview + +Update it on preview releases. + +1. Wait for a PR to show up in https://github.com/powershell/homebrew-tap, review and merge it. diff --git a/docs/testing-guidelines/CodeCoverageAnalysis.md b/docs/testing-guidelines/CodeCoverageAnalysis.md deleted file mode 100644 index 3766827c575..00000000000 --- a/docs/testing-guidelines/CodeCoverageAnalysis.md +++ /dev/null @@ -1,139 +0,0 @@ -# Code coverage analysis for commit [2ae5d07](https://codecov.io/gh/PowerShell/PowerShell/tree/c7b959bd6e5356fbbd395f22ba0c6cba49f354f6/src) - -Code coverage runs are enabled on daily Windows builds for PowerShell Core 6.0. -The results of the latest build are available at: [CodeCov.io](https://codecov.io/gh/PowerShell/PowerShell) - -The goal of this analysis is to find the hot spots of missing coverage. -The metrics used for selection of these hot spots were: # missing lines and likelihood of code path usage. - -## Coverage Status - -The following table shows the status for the above commit, dated 06/18/2017 - -| Assembly | Hit % | -| -------- |:-----:| -| Microsoft.PowerShell.Commands.Diagnostics | 58.01% | -| Microsoft.PowerShell.Commands.Management | 32.02% | -| Microsoft.PowerShell.Commands.Utility | 67.55% | -| Microsoft.PowerShell.ConsoleHost | 41.15% | -| Microsoft.PowerShell.CoreCLR.AssemblyLoadContext | 97.65% | -| Microsoft.PowerShell.CoreCLR.Eventing | 29.91% | -| Microsoft.PowerShell.LocalAccounts | 86.35% | -| Microsoft.PowerShell.PSReadLine | 10.18% | -| Microsoft.PowerShell.Security | 44.44% | -| Microsoft.WSMan.Management | 4.91% | -| System.Management.Automation | 50.42% | -| Microsoft.WSMan.Runtime/WSManSessionOption.cs | 100% | -| powershell/Program.cs | 100% | - -## Hot Spots with missing coverage - -### Microsoft.PowerShell.Commands.Management - -- [ ] CDXML cmdlet coverage. It is 0% as CDXML is broken due to CoreCLR [issue](https://github.com/dotnet/corefx/issues/18877). -- [ ] Add tests for *-Computer cmdlets [#4146](https://github.com/PowerShell/PowerShell/issues/4146) -- [ ] Add tests for *-Service cmdlets [#4147](https://github.com/PowerShell/PowerShell/issues/4147) -- [ ] Add tests for *-Item cmdlets. Especially for literal paths and error cases. [#4148](https://github.com/PowerShell/PowerShell/issues/4148) -- [ ] Add tests for Get-Content -Tail. [#4150](https://github.com/PowerShell/PowerShell/issues/4150) -- [ ] Lots of resource strings not covered. Will probably get covered when coverage is added for error cases. [#4148](https://github.com/PowerShell/PowerShell/issues/4148) - -### Microsoft.PowerShell.Commands.Utility - -- [ ] Add tests for Trace-Command. Especially Trace-Command -Expression [#4151](https://github.com/PowerShell/PowerShell/issues/4151) -- [ ] Add tests for ConvertTo-XML serialization of PSObjects [#4152](https://github.com/PowerShell/PowerShell/issues/4152) -- [ ] Add tests for Debug-Runspace [#4153](https://github.com/PowerShell/PowerShell/issues/4153) -- [ ] Add tests for New-Object for ArgumentList, ComObject [#4154](https://github.com/PowerShell/PowerShell/issues/4154) - -### Microsoft.PowerShell.ConsoleHost - -- [ ] Various options, DebugHandler and hosting modes like server, namedpipe etc. [#4155](https://github.com/PowerShell/PowerShell/issues/4155) - -### Microsoft.PowerShell.CoreCLR.Eventing - -- [ ] Add tests for ETW events. [#4156](https://github.com/PowerShell/PowerShell/issues/4156) - -### Microsoft.PowerShell.PSReadLine - -- [ ] We need tests from PSReadline repo or ignore coverage data for this module. (This will be filtered out.) - -### Microsoft.PowerShell.Security - -- [ ] Add tests for *-Acl cmdlets. [4157] (https://github.com/PowerShell/PowerShell/issues/4157) -- [ ] Add tests for *-AuthenticodeSignature cmdlets. [4157] (https://github.com/PowerShell/PowerShell/issues/4157) -- [ ] Add coverage to various utility methods under src/Microsoft.PowerShell.Security/security/Utils.cs [4157] (https://github.com/PowerShell/PowerShell/issues/4157) - -### Microsoft.WSMan.Management - -- [ ] Add tests for WSMan provider [#4158](https://github.com/PowerShell/PowerShell/issues/4158) -- [ ] Add tests for WSMan cmdlets [#4158](https://github.com/PowerShell/PowerShell/issues/4158) -- [ ] Add tests for CredSSP [#4158](https://github.com/PowerShell/PowerShell/issues/4158) - -### System.Management.Automation - -#### CoreCLR - -- [ ] Lots of non-windows code can be ifdef'ed out. Issue #[3565](https://github.com/PowerShell/PowerShell/issues/3565) - -#### CIMSupport - -- [ ] Missing coverage possibly due to: CoreCLR [issue](https://github.com/dotnet/corefx/issues/18877). -[4159](https://github.com/PowerShell/PowerShell/issues/4159) - -#### Engine - -- [ ] Add tests for COM. [#4154](https://github.com/PowerShell/PowerShell/issues/4154) -- [ ] Add tests for Tab Completion of various types of input. [#4160](https://github.com/PowerShell/PowerShell/issues/4160) -- [ ] Add tests for Import-Module / Get-Module over PSRP and CIMSession. [#4161](https://github.com/PowerShell/PowerShell/issues/4161) -- [ ] Add tests for debugging PS Jobs.[#4153](https://github.com/PowerShell/PowerShell/issues/4153) -- [ ] Add test for -is, -isnot, -contains, -notcontains and -like operators.[#4162](https://github.com/PowerShell/PowerShell/issues/4162) -- [ ] Remove Snapin code from CommandDiscovery. Issue #[4118](https://github.com/PowerShell/PowerShell/issues/4118) -- [ ] Add tests SessionStateItem, SessionStateContainer error cases, dynamic parameters. Coverage possibly added by *-Item, *-ChildItem error case tests. [#4148](https://github.com/PowerShell/PowerShell/issues/4148) -- [ ] Add tests for Get-Command -ShowCommandInfo [#4163](https://github.com/PowerShell/PowerShell/issues/4163) -- [ ] Add tests for Proxy Commands [#4164](https://github.com/PowerShell/PowerShell/issues/4164) -- [ ] Add more tests using PSCredential [#4165](https://github.com/PowerShell/PowerShell/issues/4165) - -#### Remoting - -- [ ] Can PSProxyJobs be removed as it is for Workflows? -- [ ] Add more tests for PS Jobs. [#4166](https://github.com/PowerShell/PowerShell/issues/4166) -- [ ] Add more tests using -ThrottleLimit [#4166](https://github.com/PowerShell/PowerShell/issues/4166) -- [ ] Add tests for Register-PSSessionConfiguration [#4166](https://github.com/PowerShell/PowerShell/issues/4166) -- [ ] Add tests for Connect/Disconnect session [#4166](https://github.com/PowerShell/PowerShell/issues/4166) -- [ ] Add more tests for Start-Job's various options [#4166](https://github.com/PowerShell/PowerShell/issues/4166) - -#### HelpSystem - -- [ ] Add tests for Alias help [#4167](https://github.com/PowerShell/PowerShell/issues/4167) -- [ ] Add tests for Class help [#4167](https://github.com/PowerShell/PowerShell/issues/4167) -- [ ] Add tests for SaveHelp [#4167](https://github.com/PowerShell/PowerShell/issues/4167) -- [ ] Add tests for HelpProviderWithCache [#4167](https://github.com/PowerShell/PowerShell/issues/4167) -- [ ] HelpProviderWithFullCache, potential dead code. [#4167](https://github.com/PowerShell/PowerShell/issues/4167) - -#### Security - -- [ ] Add more tests under various ExecutionPolicy modes. [4168](https://github.com/PowerShell/PowerShell/issues/4168) - -#### Utils - -- [ ] Add more error case test to improve coverage of src/System.Management.Automation/utils [#4169](https://github.com/PowerShell/PowerShell/issues/4169) - -#### Providers - -##### FileSystemProvider - -- [ ] Add tests for Mapped Network Drive [#4148](https://github.com/PowerShell/PowerShell/issues/4148) -- [ ] Add tests for *-Item alternate stream [#4148](https://github.com/PowerShell/PowerShell/issues/4148) -- [ ] Add tests for Get-ChildItem -path "file" [#4148](https://github.com/PowerShell/PowerShell/issues/4148) -- [ ] Add tests for Rename-Item for a directory [#4148](https://github.com/PowerShell/PowerShell/issues/4148) -- [ ] Add tests for Copy-Item over remote session [#4148](https://github.com/PowerShell/PowerShell/issues/4148) -- [ ] Add tests for various error conditions [#4148](https://github.com/PowerShell/PowerShell/issues/4148) - -##### RegistryProvider - -- [ ] Add tests for *-Item [#4148](https://github.com/PowerShell/PowerShell/issues/4148) -- [ ] Add tests for *-Acl [#4157](https://github.com/PowerShell/PowerShell/issues/4157) -- [ ] Add tests for error conditions [#4148](https://github.com/PowerShell/PowerShell/issues/4148) - -##### FunctionProvider - -- [ ] Add *-Item tests [#4148](https://github.com/PowerShell/PowerShell/issues/4148) diff --git a/docs/testing-guidelines/Images/AppVeyor-Badge-Green.png b/docs/testing-guidelines/Images/AppVeyor-Badge-Green.png deleted file mode 100644 index 777ca2c4b44..00000000000 Binary files a/docs/testing-guidelines/Images/AppVeyor-Badge-Green.png and /dev/null differ diff --git a/docs/testing-guidelines/Images/AppVeyor-Github.png b/docs/testing-guidelines/Images/AppVeyor-Github.png index e467d28accf..4afb37d9a4f 100644 Binary files a/docs/testing-guidelines/Images/AppVeyor-Github.png and b/docs/testing-guidelines/Images/AppVeyor-Github.png differ diff --git a/docs/testing-guidelines/Images/AzDevOps-Success.png b/docs/testing-guidelines/Images/AzDevOps-Success.png new file mode 100644 index 00000000000..be0439f7c83 Binary files /dev/null and b/docs/testing-guidelines/Images/AzDevOps-Success.png differ diff --git a/docs/testing-guidelines/Images/CoverageReportFilter.PNG b/docs/testing-guidelines/Images/CoverageReportFilter.PNG new file mode 100644 index 00000000000..69e2e28ace5 Binary files /dev/null and b/docs/testing-guidelines/Images/CoverageReportFilter.PNG differ diff --git a/docs/testing-guidelines/Images/CoverageReportTop.PNG b/docs/testing-guidelines/Images/CoverageReportTop.PNG new file mode 100644 index 00000000000..f1737e9da5d Binary files /dev/null and b/docs/testing-guidelines/Images/CoverageReportTop.PNG differ diff --git a/docs/testing-guidelines/Images/Travis-CI-Badge-Green.png b/docs/testing-guidelines/Images/Travis-CI-Badge-Green.png deleted file mode 100644 index 9207e71cc13..00000000000 Binary files a/docs/testing-guidelines/Images/Travis-CI-Badge-Green.png and /dev/null differ diff --git a/docs/testing-guidelines/PowerShellCoreTestStatus.md b/docs/testing-guidelines/PowerShellCoreTestStatus.md index 6cb2853080f..ff8d225b7aa 100644 --- a/docs/testing-guidelines/PowerShellCoreTestStatus.md +++ b/docs/testing-guidelines/PowerShellCoreTestStatus.md @@ -12,7 +12,7 @@ Here are some statistics about our current test coverage: - More than 1200 tests have been created to validate the PowerShell Core cmdlets ## PowerShell Cmdlets -The follow table represents the test coverage of the PowerShell Core Cmdlets in relation to the delivery platform as of 8/17/2016: +The follow table represents the test coverage of the PowerShell Core Cmdlets in relation to the delivery platform as of 2016-08-17: | Name | Linux | Windows | Test Coverage | |---|---|---|:---:| diff --git a/docs/testing-guidelines/TestRoadmap.md b/docs/testing-guidelines/TestRoadmap.md index bc6d1cef944..aea71be5aec 100644 --- a/docs/testing-guidelines/TestRoadmap.md +++ b/docs/testing-guidelines/TestRoadmap.md @@ -17,8 +17,8 @@ This will provide us much needed visibility in how PowerShell Core is being used We already have infrastructure in place to allow us see how PowerShell Core is being used, by collecting telemetry from PowerShell Core, we can improve our confidence as we drive to production quality. ### Logging -The code which on Windows create ETW logging has been completely stubbed out on Linux/OSX. -We should take advantage of the native logging mechanisms on Linux/OSX and implement a logger similar to the ETW logger on Windows using Syslog (or equivalent). +The code which on Windows create ETW logging has been completely stubbed out on Linux/macOS. +We should take advantage of the native logging mechanisms on Linux/macOS and implement a logger similar to the ETW logger on Windows using Syslog (or equivalent). We could use this data during test runs to identify test gaps. Simply by capturing the cmdlets and their parameters which are invoked during test would illuminate the gaps we have in our current tests, and allow us to easily fill them. It is not sufficient to support only one platform because we have many tests which determine at runtime whether or not it should run based on OS, so data from Windows will not be the same as that from Linux or MacOS. @@ -44,7 +44,6 @@ Running code coverage more often on full PowerShell is something that we should We currently run only those tests which are tagged `CI` excluding the tag `SLOW` as part of our continuous integration systems. This means roughly 1/3rd of our github tests are not being run on any regular schedule. In order to provide us with higher confidence in our code, we should be running *ALL* of our tests on a regular basis. -We have recently added to `AppVeyor` running all of our tests on a daily basis, but are not yet running these tests on Linux/Mac via `Travis`, which should be done. However, running the tests is only the first step, we need an easy way to be notified of test failures, and to track progress of those runs over time. Tracking this over time affords us the ability to see how our test count increases, implying an improvement in coverage. It also provides us mechanism whereby we can see trends in instability. @@ -69,7 +68,7 @@ In addition to loopback tests using both WSMan and SSH protocols, we should have * Windows Client->Nano Server * Windows Client->Linux Server * Linux Client -> Windows Server -* OSX Client -> Nano Client +* macOS Client -> Nano Client * PowerShell Core Client -> Full PowerShell Server * Full PowerShell Client -> PowerShell Core Server * Downlevel Full PowerShell Client -> PowerShell Core Server @@ -80,7 +79,7 @@ We need to be sure that we can easily enable remoting for the non-Windows platfo * Our current multi-machine tests do not test the connection code, they simply execute test code remotely and retrieve results and assume a good connection. The infrastructure used for these tests is STEX which is not an open environment. We will need to create automation to create and configure the test systems in the test matrix and then invoke tests on them. -It is not clear that our current CI systems can accommodate our needs here as neither AppVeyor or Travis can supply us with all of the OS images needed. +It is not clear that our current CI systems can accommodate our needs here as Azure DevOps can supply us with all of the OS images needed. We may need to create our own heterogeneous environment in Azure, or look to other teams (MS Build Lab/Jenkins) for assistance. We need to investigate whether there are solutions available, and if not, design/implement an environment to meet our needs. @@ -89,9 +88,6 @@ We need to investigate whether there are solutions available, and if not, design Currently, we report against the simplest of KPI: * is the CI build error free (which is part of the PR/Merge process - and reported on our landing page) -We are also collecting data for a daily build, but not yet report on the following KPI -* is the daily build error free (we are running this on AppVeyor, we still need to do this for Travis-CI) - There are a number of KPIs which we could report on: * Code KPIs * What is the coverage (% blocks covered) of our `CI` tests @@ -194,4 +190,4 @@ Below is my suggestion for prioritization to reduce risk and improve confidence 6. Replace in-lab tests with PowerShell Core tests 7. Investigate feasibility of running current in-lab tests on PowerShell Core -These are [tracked](https://github.com/PowerShell/PowerShell/issues?utf8=%E2%9C%93&q=is%3Aissue%20%23testability%20) as issues \ No newline at end of file +These are [tracked](https://github.com/PowerShell/PowerShell/issues?utf8=%E2%9C%93&q=is%3Aissue%20%23testability%20) as issues diff --git a/docs/testing-guidelines/WritingPesterTests.md b/docs/testing-guidelines/WritingPesterTests.md index df0ea928f06..5225dd94a22 100755 --- a/docs/testing-guidelines/WritingPesterTests.md +++ b/docs/testing-guidelines/WritingPesterTests.md @@ -1,129 +1,143 @@ -### Writing Pester Tests -Note that this document does not replace the documents found in the [Pester](https://github.com/pester/pester "Pester") project. This is just -some quick tips and suggestions for creating Pester tests for this project. The Pester community is vibrant and active, if you have questions -about Pester or creating tests, the [Pester Wiki](https://github.com/pester/pester/wiki) has a lot of great information. +# Writing Pester Tests + +Note that this document does not replace the documents found in the [Pester](https://github.com/pester/pester) project. +This is just some quick tips and suggestions for creating Pester tests for this project. +The Pester community is vibrant and active, if you have questions about Pester or creating tests, the [Pester Wiki](https://github.com/pester/pester/wiki) has a lot of great information. +As of January 2018, PowerShell Core is using Pester version 4 which has some changes from earlier versions. +See [Migrating from Pester 3 to Pester 4](https://github.com/pester/Pester/wiki/Migrating-from-Pester-3-to-Pester-4) for more information. When creating tests, keep the following in mind: -* Tests should not be overly complicated and test too many things - * boil down your tests to their essence, test only what you need -* Tests should be as simple as they can -* Tests should generally not rely on any other test -Examples: -Here's the simplest of tests +- Tests should not be overly complicated and test too many things. + - Boil down your tests to their essence, test only what you need. +- Tests should be as simple as they can. +- Tests should generally not rely on any other test. + +## Examples + +Here's the simplest of tests: ```powershell Describe "A variable can be assigned and retrieved" { - It "Create a variable and make sure its value is correct" { + It "Creates a variable and makes sure its value is correct" { $a = 1 - $a | Should be 1 + $a | Should -Be 1 } } ``` -If you need to do type checking, that can be done as well +If you need to do type checking, that can be done as well: ```powershell Describe "One is really one" { It "Compare 1 to 1" { $a = 1 - $a | Should be 1 + $a | Should -Be 1 } It "1 is really an int" { $i = 1 - $i.GetType() | Should Be "int" + $i | Should -BeOfType System.Int32 } } ``` -alternatively, you could do the following: +If you are checking for proper errors, use the `Should -Throw -ErrorId` Pester syntax. +It checks against `FullyQualifiedErrorId` property, which is recommended because it does not change based on culture as an error message might. ```powershell -Describe "One is really one" { - It "Compare 1 to 1" { - $a = 1 - $a | Should be 1 - } - It "1 is really an int" { - $i = 1 - $i.GetType() | Should Be ([System.Int32]) - } +... +It "Get-Item on a nonexisting file should have error PathNotFound" { + { Get-Item "ThisFileCannotPossiblyExist" -ErrorAction Stop } | Should -Throw -ErrorId "PathNotFound,Microsoft.PowerShell.Commands.GetItemCommand" } ``` -If you are checking for proper errors, use the `ShouldBeErrorId` helper defined in HelpersCommon.psm1 module which is in your path if you import `build.psm1`. -Checking against `FullyQualifiedErrorId` is recommended because it does not change based on culture as an error message might. +Note that if `Get-Item` were to succeed, the test will fail. + +However, if you need to check the `InnerException` or other members of the ErrorRecord, you should use `-PassThru` parameter: ```powershell -... -It "Get-Item on a nonexisting file should have error PathNotFound" { - { Get-Item "ThisFileCannotPossiblyExist" -ErrorAction Stop } | ShouldBeErrorId "PathNotFound,Microsoft.PowerShell.Commands.GetItemCommand" +It "InnerException sample" { + $e = { Invoke-WebRequest https://expired.badssl.com/ } | Should -Throw -ErrorId "WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeWebRequestCommand" -PassThru + $e.Exception.InnerException.NativeErrorCode | Should -Be 12175 } ``` -Note that if get-item were to succeed, a different FullyQualifiedErrorId would be thrown and the test will fail. -This is the suggested path because Pester wants to check the error message, which will likely not work here because of localized builds, but the FullyQualifiedErrorId is constant regardless of the locale. +## Describe/Context/It -However, if you need to check the `InnerException` or other members of the ErrorRecord, the recommended pattern to use is: +For creation of PowerShell tests, the `Describe` block is the level of granularity suggested and one of three tags should be used: `CI`, `Feature`, or `Scenario`. -```powershell - It "InnerException sample" { +If the tag is not provided, the build process will fail. - $e = { Invoke-WebRequest https://expired.badssl.com/ } | ShouldBeErrorId "WebCmdletWebResponseException,Microsoft.PowerShell.Commands.InvokeWebRequestCommand" - $e.Exception.InnerException.NativeErrorCode | Should Be 12175 - ... - } -``` +### Describe + +Creates a logical group of tests. +All `Mocks` and `TestDrive` contents defined within a `Describe` block are scoped to that `Describe`; +they will no longer be present when the `Describe` block exits. +A `Describe` block may contain any number of `Context` and `It` blocks. + +### Context + +Provides logical grouping of `It` blocks within a single `Describe` block. Any `Mocks` defined inside a `Context` are removed at the end of the `Context` scope, as are any files or folders added to the `TestDrive` during the `Context` block's execution. + +Any `BeforeEach` or `AfterEach` blocks defined inside a `Context` also only apply to tests within that `Context`. + +### It + +The `It` block is intended to be used inside of a `Describe` or `Context` block. If you are familiar with the AAA pattern (Arrange-Act-Assert), the body of the `It` block is the appropriate location for an assert. -### Describe/Context/It -For creation of PowerShell tests, the Describe block is the level of granularity suggested and one of three tags should be used: "CI", "Feature", or "Scenario". If the tag is not provided, tests in that describe block will be run any time tests are executed. +The convention is to assert a single expectation for each `It` block. The code inside of the `It` block should throw a terminating error if the expectation of the test is not met and thus cause the test to fail. -#### Describe -Creates a logical group of tests. All Mocks and TestDrive contents defined within a Describe block are scoped to that Describe; they will no longer be present when the Describe block exits. A `Describe block may contain any number of Context and It blocks. +The name of the `It` block should expressively state the expectation of the test. -#### Context -Provides logical grouping of It blocks within a single Describe block. Any Mocks defined inside a Context are removed at the end of the Context scope, as are any files or folders added to the TestDrive during the Context block's execution. Any BeforeEach or AfterEach blocks defined inside a Context also only apply to tests within that Context . +## Admin privileges in tests -#### It -The It block is intended to be used inside of a Describe or Context Block. If you are familiar with the AAA pattern (Arrange-Act-Assert), the body of the It block is the appropriate location for an assert. The convention is to assert a single expectation for each It block. The code inside of the It block should throw a terminating error if the expectation of the test is not met and thus cause the test to fail. The name of the It block should expressively state the expectation of the test. +Tests that require admin privileges **on Windows** must be additionally marked with `RequireAdminOnWindows` Pester tag. -### Admin privileges in tests -Tests that require admin privileges **on windows** should be additionally marked with 'RequireAdminOnWindows' Pester tag. -In the AppVeyor CI, we run two different passes: +In the Azure DevOps Windows CI, we run two different passes: -- The pass with exclusion of tests with 'RequireAdminOnWindows' tag -- The pass where we run only 'RequireAdminOnWindows' tests +- The pass with exclusion of `RequireAdminOnWindows` tagged tests. +- The pass where only `RequireAdminOnWindows` tagged tests are being executed. In each case, tests are executed with appropriate privileges. -### Selected Features +Tests that need to be run with sudo **on Unix systems** must be additionally marked with `RequireSudoOnUnix` Pester tag. -#### Test Drive -A PSDrive is available for file activity during a tests and this drive is limited to the scope of a single Describe block. The contents of the drive are cleared when a context block is exited. -A test may need to work with file operations and validate certain types of file activities. It is usually desirable not to perform file activity tests that will produce side effects outside of an individual test. Pester creates a PSDrive inside the user's temporary drive that is accessible via a names PSDrive TestDrive:. Pester will remove this drive after the test completes. You may use this drive to isolate the file operations of your test to a temporary store. +`RequireSudoOnUnix` tag takes precedence over all other tags like `CI`, `Feature`, etc. (which are ignored when `RequireSudoOnUnix` is present). +Tests tagged with `RequireSudoOnUnix` will run as a separate pass for any Unix test. + +## Selected Features + +### Test Drive + +A `PSDrive` is available for file activity during a test and this drive is limited to the scope of a single `Describe` block. The contents of the drive are cleared when a `Context` block is exited. + +A test may need to work with file operations and validate certain types of file activities. It is usually desirable not to perform file activity tests that will produce side effects outside of an individual test. + +Pester creates a `PSDrive` inside the user's temporary drive that is accessible via `TestDrive:` or `$TestDrive`. **Pester will remove** this drive after the test completes. +You may use this drive to isolate the file operations of your test to a temporary store. The following example illustrates the feature: ```powershell function Add-Footer($path, $footer) { - Add-Content $path -Value $footer + Add-Content $path -Value $footer } Describe "Add-Footer" { - $testPath="TestDrive:\test.txt" - Set-Content $testPath -value "my test text." - Add-Footer $testPath "-Footer" - $result = Get-Content $testPath + $testPath="TestDrive:\test.txt" + Set-Content $testPath -value "my test text." + Add-Footer $testPath "-Footer" + $result = Get-Content $testPath - It "adds a footer" { - (-join $result) | Should Be("my test text.-Footer") - } + It "adds a footer" { + (-join $result) | Should -BeExactly "my test text.-Footer" + } } ``` -When this test completes, the contents of the TestDrive PSDrive will be removed. +When this test completes, the contents of the `TestDrive:` will be removed. -#### Parameter Generation +### Parameter Generation ```powershell $testCases = @( @@ -134,30 +148,34 @@ $testCases = @( ) Describe "A test" { - It " -xor should be " -testcase $testcases { + It " -xor should be " -TestCases $testCases { param ($a, $b, $ExpectedResult) - $a -xor $b | Should be $ExpectedResult + $a -xor $b | Should -Be $ExpectedResult } } ``` -You can also construct loops and pass values as parameters, including the expected value, but Pester does this for you. +### Mocking + +Mocks the behavior of an existing command with an alternate implementation. This creates new behavior for any existing command within the scope of a `Describe` or `Context` block. +The function allows you to specify a script block that will become the command's new behavior. -#### Mocking -Mocks the behavior of an existing command with an alternate implementation. This creates new behavior for any existing command within the scope of a Describe or Context block. The function allows you to specify a script block that will become the command's new behavior. The following example illustrates simple use: ```powershell Context "Get-Random is not random" { - Mock Get-Random { return 3 } - It "Get-Random returns 3" { - Get-Random | Should be 3 - } + Mock Get-Random { return 3 } + + It "Get-Random returns 3" { + Get-Random | Should -Be 3 } +} ``` -More information may be found on the [wiki](https://github.com/pester/Pester/wiki/Mock) +More information may be found [here](https://github.com/pester/Pester/wiki/Mock). + ### Free Code in a Describe block + Code execution in Pester can be very subtle and can cause issues when executing test code. The execution of code which lays outside of the usual code blocks may not happen as you expect. Consider the following: ```powershell @@ -177,7 +195,7 @@ Describe it { Write-Host -for DarkRed "Before It" It "should not be a surprise" { - 1 | should be 1 + 1 | should -Be 1 } Write-Host -for DarkRed "After It" } @@ -188,7 +206,7 @@ Describe it { } ``` -Now, when run, you can see the execution schedule +Now, when run, you can see the execution schedule: ``` PS# invoke-pester c:\temp\pester.demo.tests.ps1 @@ -214,11 +232,19 @@ Tests completed in 79ms Passed: 1 Failed: 0 Skipped: 0 Pending: 0 ``` -The DESCRIBE BeforeAll block is executed before any other code even though it was at the bottom of the Describe block, so if state is set elsewhere in the describe BLOCK, that state will not be visible (as the code will not yet been run). Notice, too, that the BEFOREALL block in Context is executed before any other code in that block. -Generally, you should have code reside in one of the code block elements of `[Before|After][All|Each]`, especially if those block rely on state set by free code elsewhere in the block. +The `Describe` - `BeforeAll` block is executed before any other code even though it was at the bottom of the `Describe` block. +So if some state is set elsewhere in the `Describe` block, that state will not yet be visible (as the code will not yet been run). + +Notice, too, that the `BeforeAll` block in `Context` is executed before any other code in that block. +Generally, you should have code reside in one of the code block elements of `BeforeAll`, `BeforeEach`, `AfterEach` and/or `AfterAll`, especially if those blocks rely on some state set by free code elsewhere in the block. + +### Skipping Tests in Bulk + +Sometimes it is beneficial to skip all the tests in a particular `Describe` block. +For example, tests which are not applicable to a platform could be skipped, and they would be reported as skipped. + +The following is an example of how this may be done: -#### Skipping tests in bulk -Sometimes it is beneficial to skip all the tests in a particular `Describe` block. For example, tests which are not applicable to a platform could be skipped, and they would be reported as skipped. The following is an example of how this may be done: ```powershell Describe "Should not run these tests on non-Windows platforms" { BeforeAll { @@ -232,23 +258,25 @@ Describe "Should not run these tests on non-Windows platforms" { } Context "Block 1" { It "This block 1 test 1" { - 1 | should be 1 + 1 | should -Be 1 } It "This is block 1 test 2" { - 1 | should be 1 + 1 | should -Be 1 } } Context "Block 2" { It "This block 2 test 1" { - 2 | should be 1 + 2 | should -Be 1 } It "This is block 2 test 2" { - 2 | should be 1 + 2 | should -Be 1 } } } ``` + Here is the output when run on a Linux distribution: + ``` Describing Should not run these tests on non-Windows platforms Context Block 1 @@ -258,7 +286,9 @@ Describing Should not run these tests on non-Windows platforms [!] This block 2 test 1 73ms [!] This is block 2 test 2 6ms ``` + and here is the output when run on a Windows distribution: + ``` Describing Should not run these tests on non-Windows platforms Context Block 1 @@ -268,41 +298,40 @@ Describing Should not run these tests on non-Windows platforms [-] This block 2 test 1 52ms Expected: {1} But was: {2} - 22: 2 | should be 1 + 22: 2 | should -Be 1 at , : line 22 [-] This is block 2 test 2 77ms Expected: {1} But was: {2} - 25: 2 | should be 1 + 25: 2 | should -Be 1 at , : line 25 ``` -this technique uses the `$PSDefaultParameterValues` feature of PowerShell to temporarily set the It block parameter `-skip` to true (or in the case of Windows, it is not set at all) - +This technique uses the `$PSDefaultParameterValues` feature of PowerShell to temporarily set the `It` block parameter `-skip` to true (or in the case of Windows, it is not set at all) -#### Multi-line strings +### Multi-line strings -You may want to have a test like +You may want to have a test like: ```powershell It 'tests multi-line string' { - Get-MultiLineString | Should Be @' + Get-MultiLineString | Should -Be @' first line second line '@ } ``` -There are problems with using here-strings with verifying the output results. +There are problems with using multi-line strings with verifying the output results. The reason for it are line-ends. They cause problems for two reasons: -* They are different on different platforms (`\r\n` on windows and `\n` on unix). -* Even on the same system, they depends on the way how the repo was cloned. +- They are different on different platforms (`\r\n` on Windows and `\n` on Unix). +- Even on the same system, they depend on the way how the repo was cloned (local git configuration). -Particularly, in the default AppVeyour CI windows image, you will get `\n` line ends in all your files. -That causes problems, because at runtime `Get-MultiLineString` would likely produce `\r\n` line ends on windows. +Particularly, in the default Azure DevOps CI Windows image, you will get `\n` line ends in all your files. +That causes problems, because at runtime `Get-MultiLineString` would likely produce `\r\n` line ends on Windows. Some workaround could be added, but they are sub-optimal and make reading test code harder. @@ -313,7 +342,7 @@ function normalizeEnds([string]$text) } It 'tests multi-line string' { - normalizeEnds (Get-MultiLineString) | Should Be (normalizeEnds @' + normalizeEnds (Get-MultiLineString) | Should -Be (normalizeEnds @' first line second line '@) @@ -323,43 +352,45 @@ second line When appropriate, you can avoid creating multi-line strings at the first place. These commands create an array of strings: -* `Get-Content` -* `Out-String -Stream` - -Pester Do and Don't -=================== - -## Do -1. Name your files .tests.ps1 -2. Keep tests simple - 1. Test only what you need - 2. Reduce dependencies -3. Be sure to tag your `Describe` blocks based on their purpose - 1. Tag `CI` indicates that it will be run as part of the continuous integration process. These should be unit test like, and generally take less than a second. - 2. Tag `Feature` indicates a higher level feature test (we will run these on a regular basis), for example, tests which go to remote resources, or test broader functionality - 3. Tag `Scenario` indicates tests of integration with other features (these will be run on a less regular basis and test even broader functionality than feature tests. -4. Make sure that `Describe`/`Context`/`It` descriptions are useful - 1. The error message should not be the place where you describe the test -5. Use `Context` to group tests - 1. Multiple `Context` blocks can help you group your test suite into logical sections -6. Use `BeforeAll`/`AfterAll`/`BeforeEach`/`AfterEach` instead of custom initiators -7. Prefer Try-Catch for expected errors and check $_.fullyQualifiedErrorId (don't use `should throw`) -8. Use `-testcases` when iterating over multiple `It` blocks -9. Use code coverage functionality where appropriate -10. Use `Mock` functionality when you don't have your entire environment -11. Avoid free code in a `Describe` block - 1. Use `[Before|After][Each|All]` see [Free Code in a Describe block](WritingPesterTests.md#free-code-in-a-describe-block) -12. Avoid creating or using test files outside of TESTDRIVE: - 1. TESTDRIVE: has automatic clean-up -13. Keep in mind that we are creating cross platform tests - 1. Avoid using the registry - 2. Avoid using COM -14. Avoid being too specific about the _count_ of a resource as these can change platform to platform - 1. ex: checking for the count of loaded format files, check rather for format data for a specific type - -## Don't -1. Don't have too many evaluations in a single It block - 1. The first `Should` failure will stop that block -2. Don't use `Should` outside of an `It` Block -3. Don't use the word "Error" or "Fail" to test a positive case - 1. ex: "Get-ChildItem TESTDRIVE: shouldn't fail", rather "Get-ChildItem should be able to retrieve file listing from TESTDRIVE" +- `Get-Content` +- `Out-String -Stream` + +## Pester Do and Don't + +### Do + +1. Name your file `.tests.ps1`. +2. Keep tests simple: + - Test only what you need. + - Reduce dependencies. +3. Be sure to tag your `Describe` blocks based on their purpose: + - Tag `CI` indicates that it will be run as part of the continuous integration process. These should be unit test like, and generally take less than a second. + - Tag `Feature` indicates a higher level feature test (we will run these on a regular basis), for example, tests which go to remote resources, or test broader functionality. + - Tag `Scenario` indicates tests of integration with other features (these will be run on a less regular basis and test even broader functionality than feature tests. +4. Make sure that `Describe`/`Context`/`It` descriptions are useful. + - The error message should not be the place where you describe the test. +5. Use `Context` to group tests. + - Multiple `Context` blocks can help you group your test suite into logical sections. +6. Use `BeforeAll`/`BeforeEach`/`AfterEach`/`AfterAll` instead of custom initiators. +7. Use `Should -Throw -ErrorId` to check for expected errors. +8. Use `-TestCases` when iterating over multiple `It` blocks. +9. Use code coverage functionality where appropriate. +10. Use `Mock` functionality when you don't have your entire environment. +11. Avoid free code in a `Describe` block. + - Use `BeforeAll`/`BeforeEach`/`AfterEach`/`AfterAll`. + - See [Free Code in a Describe block](WritingPesterTests.md#free-code-in-a-describe-block) +12. Avoid creating or using test files outside of `TESTDRIVE:`. + - `TESTDRIVE:` has automatic clean-up. +13. Keep in mind that we are creating cross platform tests. + - Avoid using the registry. + - Avoid using COM. +14. Avoid being too specific about the _count_ of a resource as these can change platform to platform. + - Example: Avoid checking for the count of loaded format files, but rather check for format data for a specific type. + +### Don't + +1. Don't have too many evaluations in a single `It` block. + - The first `Should` failure will stop that block. +2. Don't use `Should` outside of an `It` Block. +3. Don't use the word "Error" or "Fail" to test a positive case. + - Example: Rephrase the negative sentence `"Get-ChildItem TESTDRIVE: shouldn't fail"` to the following positive case `"Get-ChildItem should be able to retrieve file listing from TESTDRIVE"`. diff --git a/docs/testing-guidelines/getting-code-coverage.md b/docs/testing-guidelines/getting-code-coverage.md new file mode 100644 index 00000000000..0f2bc8eb983 --- /dev/null +++ b/docs/testing-guidelines/getting-code-coverage.md @@ -0,0 +1,172 @@ +# Getting Code Coverage Analysis for PowerShell + +**Note: Code coverage is currently only supported on Windows, since we use OpenCover.** + +The PowerShell code base is configured to build with code coverage support using [OpenCover]. + +You can see the testing coverage of the current [`master`] branch build at any time at [codecov.io]. + +To run test coverage analysis of PowerShell on your own branch/machine, +you will need to take the following steps +(and be aware that running the code coverage analysis can take as long as 8 hours). + +## Running tests with code coverage analysis + +**First**: Open PowerShell in an **elevated** session. +OpenCover needs elevated privileges to work. + +Now, in PowerShell: + +```powershell +# Go to your PowerShell build directory root +PS> Set-Location "C:\Path\to\powershell\build\dir" + +# Import the PowerShell build module +PS> Import-Module .\build.psm1 + +# Build PowerShell. You may need to add other flags here like +# -ResGen or -Restore +PS> Start-PSBuild -Configuration CodeCoverage -Clean -PsModuleRestore + +# Now ensure Pester is installed +PS> Restore-PSPester + +# We also need to build the test executor +PS> Publish-PSTestTools + +# Import the OpenCover module +PS> Import-Module $PWD\test\tools\OpenCover + +# Install OpenCover to a temporary directory +PS> Install-OpenCover -TargetDirectory $env:TEMP -Force + +# Finally, run the tests with code coverage analysis. +# If you want to run only the continuous integration tests, +# add -CIOnly, which will take less time +PS> Invoke-OpenCover -OutputLog coverage.xml -OpenCoverPath $env:TEMP\OpenCover +``` + +## Examining the code coverage data + +Once the code coverage test run is done, you'll want to examine the data: + +```powershell +# Collect the coverage data using Get-CodeCoverage from the OpenCover +# module that was imported above. This operation is generally expensive +# to compute, so worth storing in a variable +PS> $coverageData = Get-CodeCoverage .\coverage.xml + +# Take a look at a summary of the results +PS> $coverageData.CoverageSummary + +NumSequencePoints : 298237 +VisitedSequencePoints : 125949 +NumBranchPoints : 101477 +VisitedBranchPoints : 39389 +SequenceCoverage : 42.23 +BranchCoverage : 38.82 +MaxCyclomaticComplexity : 393 +MinCyclomaticComplexity : 1 +VisitedClasses : 1990 +NumClasses : 3187 +VisitedMethods : 15115 +NumMethods : 32517 + +# You can also view results by assembly +PS> $coverageData.Assembly | Format-Table AssemblyName,Branch,Sequence + +AssemblyName Branch Sequence +------------ ------ -------- +pwsh 100 100 +Microsoft.PowerShell.ConsoleHost 21.58 23.32 +System.Management.Automation 41.22 45.01 +Microsoft.PowerShell.CoreCLR.Eventing 1.88 2.03 +Microsoft.PowerShell.Security 17.32 20.09 +Microsoft.PowerShell.Commands.Utility 20.14 21.39 +Microsoft.PowerShell.Commands.Management 43.05 43.39 +Microsoft.WSMan.Management 52.58 56.98 +Microsoft.WSMan.Runtime 80.95 80.33 +Microsoft.PowerShell.Commands.Diagnostics 0 0 +``` + +If you have made changes to tests or code +and run a second code coverage run, +you can also compare code coverage results: + +```powershell +PS> $cov1 = Get-CodeCoverage ./coverage1.xml +PS> $cov2 = Get-CodeCoverage ./coverage2.xml +PS> Compare-CodeCoverage -Run1 $cov1 -Run2 $cov2 + +AssemblyName Sequence SequenceDelta Branch BranchDelta +------------ -------- ------------- ------ ----------- +Microsoft.PowerShell.Security 20.09 -30.12 17.32 -31.63 +Microsoft.PowerShell.Commands.Management 43.39 9.10 43.05 11.59 +System.Management.Automation 45.04 -10.63 41.23 -11.07 +Microsoft.PowerShell.Commands.Utility 21.39 -47.22 20.14 -46.47 +Microsoft.PowerShell.Commands.Diagnostics 0 -51.91 0 -48.62 +Microsoft.PowerShell.ConsoleHost 23.32 -22.28 21.58 -22.47 +pwsh 100 0.00 100 0.00 +Microsoft.WSMan.Management 57.73 48.23 53.02 43.22 +Microsoft.WSMan.Runtime 80.33 -19.67 80.95 -19.05 +Microsoft.PowerShell.CoreCLR.Eventing 2.03 -32.74 1.88 -26.01 +``` + +To get file-specific coverage data, +you can use `Compare-FileCoverage`: + +```powershell +PS> Compare-FileCoverage -ReferenceCoverage $cov2 -DifferenceCoverage $cov1 -FileName LanguagePrimitives.cs + +FileName ReferenceCoverage DifferenceCoverage CoverageDelta +-------- ----------------- ------------------ ------------- +LanguagePrimitives.cs 53.68 69.03 15.34 +``` + +You can see more ways to use `Compare-CodeCoverage` and `Compare-FileCoverage` +by running: + +```powershell +PS> Get-Help Compare-CodeCoverage -Full +# Or +PS> Get-Help Compare-FileCoverage -Full +``` + +## Visualizing code coverage + +For a more detailed, graphical representation of the code coverage results, +you can use the ReportGenerator package. +This generates an HTML report of the coverage from the XML file +and will provide much more detail about the coverage analysis. +The package is available on [NuGet], +and you can install and run it as follows: + +```powershell +# Install ReportGenerator +PS> Find-Package ReportGenerator ` +>> -ProviderName Nuget ` +>> -Source "https://nuget.org/api/v2" ` +>> | Install-Package -Scope CurrentUser + +# Get the ReportGenerator executable path +# Make sure use the appropriate version number in the path +$ReportGenExe = "$HOME\AppData\Local\PackageManagement\NuGet\Packages\ReportGenerator.\tools\ReportGenerator.exe" + +# Run ReportGenerator +& $ReportGenExe -report:coverage.xml -targetdir:C:\temp\Coverage + +# Finally, open the report in your browser +Invoke-Item C:\temp\Coverage\index.htm +``` + +This should open a screen in the browser like this: +![Coverage report browser page](Images/CoverageReportTop.PNG) + +The main report, which is below the summary and risk hot spots, has +a filter functionality as well (when "Enable Filtering" is clicked on): +![Coverage report with filter on](Images/CoverageReportFilter.PNG) + +[OpenCover]: https://github.com/OpenCover/opencover +[codecov.io]: https://codecov.io +[`master`]: https://github.com/PowerShell/PowerShell +[NuGet]: https://nuget.org/packages/ReportGenerator diff --git a/docs/testing-guidelines/testing-guidelines.md b/docs/testing-guidelines/testing-guidelines.md index b5178a34157..e00c8352ee7 100755 --- a/docs/testing-guidelines/testing-guidelines.md +++ b/docs/testing-guidelines/testing-guidelines.md @@ -1,10 +1,10 @@ - # Testing Guidelines Testing is a critical and required part of the PowerShell project. The Microsoft PowerShell team created nearly 100,000 tests over the last 12 years which we run as part of the release process for Windows PowerShell. -Having all of those tests available for the initial release of PowerShell was not feasible, and we have targeted those tests which we believe will provide us the ability to catch regressions in the areas which have had the largest changes for PowerShell. +Having all of those tests available for the initial release of PowerShell was not feasible, +and we have targeted those tests which we believe will provide us the ability to catch regressions in the areas which have had the largest changes for PowerShell. It is our intent to continue to release more and more of our tests until we have the coverage we need. For creating new tests, please review the [documents](https://github.com/PowerShell/PowerShell/tree/master/docs/testing-guidelines) on how to create tests for PowerShell. @@ -13,35 +13,21 @@ When adding new tests, place them in the directories as [outlined below](#test-l ## CI System -We use [AppVeyor](http://www.appveyor.com/) as a continuous integration (CI) system for Windows -and [Travis CI](http://www.travis-ci.com) for non-Windows platforms. - -### AppVeyor +We use [Azure DevOps](https://azure.microsoft.com/en-us/solutions/devops) as a continuous integration (CI) system for Windows +and non-Windows platforms. -In the `README.md` at the top of the repo, you can see AppVeyor badge. +In the `README.md` at the top of the repository, you can see Azure CI badge. It indicates the last build status of `master` branch. Hopefully, it's green: -![AppVeyor-Badge-Green.png](Images/AppVeyor-Badge-Green.png) - -This badge is **clickable**; you can open corresponding build page with logs, artifacts, and tests results. -From there you can easily navigate to the build history. - -### Travis CI - -Travis CI works similarly to AppVeyor. -For Travis CI there will be multiple badges. -The badges indicate the last build status of `master` branch for different platforms. -Hopefully, it's green: - -![Travis-CI-Badge-Green.png](Images/Travis-CI-Badge-Green.png) +![AzDevOps-Success.png](Images/AzDevOps-Success.png) This badge is **clickable**; you can open corresponding build page with logs, artifacts, and tests results. From there you can easily navigate to the build history. ### Getting CI Results -CI System builds (AppVeyor and Travis CI) and runs tests on every pull request and provides quick feedback about it. +CI System builds and runs tests on every pull request and provides quick feedback about it. ![AppVeyor-Github](Images/AppVeyor-Github.png) @@ -69,8 +55,11 @@ The Pester framework allows `Describe` blocks to be tagged, and our CI system re One of the following tags must be used: * `CI` - this tag indicates that the tests in the `Describe` block will be executed as part of the CI/PR process -* `Feature` - tests with this tag will not be executed as part of the CI/PR process, but they will be executed on a daily basis as part of a `cron` driven build. They indicate that the test will be validating more behavior, or will be using remote network resources (ex: package management tests) * `Scenario` - this tag indicates a larger scale test interacting with multiple areas of functionality and/or remote resources, these tests are also run daily. +* `Feature` - tests with this tag will not be executed as part of the CI/PR process, + but they will be executed on a daily basis as part of a `cron` driven build. + They indicate that the test will be validating more behavior, + or will be using remote network resources (ex: package management tests) Additionally, the tag: @@ -78,22 +67,34 @@ Additionally, the tag: #### Requesting additional tests for a PR -In our CI systems, we normally run only run tests tagged with `CI`. If in the first line of the last (most recent) commit description you add `[Feature]`, -we will ensure that we will also run the tests tagged with `Feature`. When you would want to do this: +In our CI systems, we normally run only run tests tagged with `CI`. +If in the first line of the last (most recent) commit description you add `[Feature]`, +we will ensure that we will also run the tests tagged with `Feature`. +When you would want to do this: - You have added or changed a `Feature` test. - A maintainer asks you to run the `Feature` tests. - Based on experience, you are confident that a maintainer will ask you to run the `Feature` tests. +#### Validating packaging changes for a PR + +By default, our CI system does a build and run tests for a PR and does not exercise code to create a package. +If your PR includes changes to packaging, you can have the CI system exercise the packaging code by +using `[Package]` as the first line in the commit message. +When you would want to do this: + +- You made change to PowerShell Core packaging +- A maintainer asks you to run as `[Package]` + ### xUnit -For those tests which are not easily run via Pester, we have decided to use [xUnit](https://xunit.github.io/) as the test framework. +For those tests which are not easily run via Pester, we have decided to use [xUnit](https://xunit.net/) as the test framework. Currently, we have a minuscule number of tests which are run by using xUnit. ## Running tests outside of CI When working on new features or fixes, it is natural to want to run those tests locally before making a PR. -Two helper functions are part of the build.psm1 module to help with that: +These helper functions are part of the build.psm1 module to help with that: * `Start-PSPester` will execute all Pester tests which are run by the CI system * `Start-PSxUnit` will execute the available xUnit tests run by the CI system @@ -105,20 +106,20 @@ environment is not the default or has any customization. For example, to run all the Pester tests for CI (assuming you are at the root of the PowerShell repo): -``` +```PowerShell Import-Module ./build.psm1 Start-PSPester ``` If you wish to run specific tests, that is possible as well: -``` +```PowerShell Start-PSPester -Path test/powershell/engine/Api ``` Or a specific Pester test file: -``` +```PowerShell Start-PSPester -Path test/powershell/engine/Api/XmlAdapter.Tests.ps1 ``` @@ -129,8 +130,6 @@ in Microsoft's internal test frameworks. The tests that you created for your change and the library of historical tests will be run to determine if any regressions are present. If these tests find regressions, you'll be notified that your PR is not ready, and provided with enough information to investigate why the failure happened. - - ## Test Layout We have taken a functional approach to the layout of our Pester tests and you should place new tests in their appropriate location. diff --git a/dsc/pwsh.profile.dsc.resource.json b/dsc/pwsh.profile.dsc.resource.json new file mode 100644 index 00000000000..cd18e94eec6 --- /dev/null +++ b/dsc/pwsh.profile.dsc.resource.json @@ -0,0 +1,126 @@ +{ + "$schema": "https://aka.ms/dsc/schemas/v3/bundled/resource/manifest.json", + "description": "Manage PowerShell profiles.", + "tags": [ + "Linux", + "Windows", + "macOS", + "PowerShell" + ], + "type": "Microsoft.PowerShell/Profile", + "version": "0.1.0", + "get": { + "executable": "pwsh", + "args": [ + "-NoLogo", + "-NonInteractive", + "-NoProfile", + "-ExecutionPolicy", + "Bypass", + "-File", + "./pwsh.profile.resource.ps1", + "-operation", + "get" + ], + "input": "stdin" + }, + "set": { + "executable": "pwsh", + "args": [ + "-NoLogo", + "-NonInteractive", + "-NoProfile", + "-ExecutionPolicy", + "Bypass", + "-File", + "./pwsh.profile.resource.ps1", + "-operation", + "set" + ], + "input": "stdin" + }, + "export": { + "executable": "pwsh", + "args": [ + "-NoLogo", + "-NonInteractive", + "-NoProfile", + "-ExecutionPolicy", + "Bypass", + "-File", + "./pwsh.profile.resource.ps1", + "-operation", + "export" + ], + "input": "stdin" + }, + "exitCodes": { + "0": "Success", + "1": "Error", + "2": "Input not supported for export operation" + }, + "schema": { + "embedded": { + "$schema": "https://json-schema.org/draft/2020-12/schema", + "title": "Profile", + "description": "Manage PowerShell profiles.", + "type": "object", + "unevaluatedProperties": false, + "required": [ + "profileType" + ], + "properties": { + "profileType": { + "type": "string", + "title": "Profile Type", + "description": "Defines which profile to manage. Valid values are: 'AllUsersCurrentHost', 'AllUsersAllHosts', 'CurrentUserAllHosts', and 'CurrentUserCurrentHost'.", + "enum": [ + "AllUsersCurrentHost", + "AllUsersAllHosts", + "CurrentUserAllHosts", + "CurrentUserCurrentHost" + ] + }, + "profilePath": { + "title": "Profile Path", + "description": "The full path to the profile file.", + "type": "string", + "readOnly": true + }, + "content": { + "title": "Content", + "description": "Defines the content of the profile. If you don't specify this property, the resource doesn't manage the file contents. If you specify this property as an empty string, the resource removes all content from the file. If you specify this property as a non-empty string, the resource sets the file contents to the specified string. The resources retains newlines from this property without any modification.", + "type": "string" + }, + "_exist": { + "$ref": "https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/v3/resource/properties/exist.json" + }, + "_name": { + "$ref": "https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/v3/resource/properties/name.json" + } + }, + "$defs": { + "https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/v3/resource/properties/exist.json": { + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/v3/resource/properties/exist.json", + "title": "Instance should exist", + "description": "Indicates whether the DSC resource instance should exist.", + "type": "boolean", + "default": true, + "enum": [ + false, + true + ] + }, + "https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/v3/resource/properties/name.json": { + "$schema": "https://json-schema.org/draft/2020-12/schema", + "$id": "https://raw.githubusercontent.com/PowerShell/DSC/main/schemas/v3/resource/properties/name.json", + "title": "Exported instance name", + "description": "Returns a generated name for the resource instance from an export operation.", + "readOnly": true, + "type": "string" + } + } + } + } +} diff --git a/dsc/pwsh.profile.resource.ps1 b/dsc/pwsh.profile.resource.ps1 new file mode 100644 index 00000000000..ad9cfa4a63a --- /dev/null +++ b/dsc/pwsh.profile.resource.ps1 @@ -0,0 +1,179 @@ +## Copyright (c) Microsoft Corporation. All rights reserved. +## Licensed under the MIT License. + +[CmdletBinding()] +param( + [Parameter(Mandatory = $true)] + [ValidateSet('get', 'set', 'export')] + [string]$Operation, + [Parameter(ValueFromPipeline)] + [string[]]$UserInput +) + +Begin { + enum ProfileType { + AllUsersCurrentHost + AllUsersAllHosts + CurrentUserAllHosts + CurrentUserCurrentHost + } + + function New-PwshResource { + param( + [Parameter(Mandatory = $true)] + [ProfileType] $ProfileType, + + [Parameter(ParameterSetName = 'WithContent')] + [string] $Content, + + [Parameter(ParameterSetName = 'WithContent')] + [bool] $Exist + ) + + # Create the PSCustomObject with properties + $resource = [PSCustomObject]@{ + profileType = $ProfileType + content = $null + profilePath = GetProfilePath -profileType $ProfileType + _exist = $false + } + + # Add ToJson method + $resource | Add-Member -MemberType ScriptMethod -Name 'ToJson' -Value { + return ([ordered] @{ + profileType = $this.profileType + content = $this.content + profilePath = $this.profilePath + _exist = $this._exist + }) | ConvertTo-Json -Compress -EnumsAsStrings + } + + # Constructor logic - if Content and Exist parameters are provided (WithContent parameter set) + if ($PSCmdlet.ParameterSetName -eq 'WithContent') { + $resource.content = $Content + $resource._exist = $Exist + } else { + # Default constructor logic - read from file system + $fileExists = Test-Path $resource.profilePath + if ($fileExists) { + $resource.content = Get-Content -Path $resource.profilePath + } else { + $resource.content = $null + } + $resource._exist = $fileExists + } + + return $resource + } + + function GetProfilePath { + param ( + [ProfileType] $profileType + ) + + $path = switch ($profileType) { + 'AllUsersCurrentHost' { $PROFILE.AllUsersCurrentHost } + 'AllUsersAllHosts' { $PROFILE.AllUsersAllHosts } + 'CurrentUserAllHosts' { $PROFILE.CurrentUserAllHosts } + 'CurrentUserCurrentHost' { $PROFILE.CurrentUserCurrentHost } + } + + return $path + } + + function ExportOperation { + $allUserCurrentHost = New-PwshResource -ProfileType 'AllUsersCurrentHost' + $allUsersAllHost = New-PwshResource -ProfileType 'AllUsersAllHosts' + $currentUserAllHost = New-PwshResource -ProfileType 'CurrentUserAllHosts' + $currentUserCurrentHost = New-PwshResource -ProfileType 'CurrentUserCurrentHost' + + # Cannot use the ToJson() method here as we are adding a note property + $allUserCurrentHost | Add-Member -NotePropertyName '_name' -NotePropertyValue 'AllUsersCurrentHost' -PassThru | ConvertTo-Json -Compress -EnumsAsStrings + $allUsersAllHost | Add-Member -NotePropertyName '_name' -NotePropertyValue 'AllUsersAllHosts' -PassThru | ConvertTo-Json -Compress -EnumsAsStrings + $currentUserAllHost | Add-Member -NotePropertyName '_name' -NotePropertyValue 'CurrentUserAllHosts' -PassThru | ConvertTo-Json -Compress -EnumsAsStrings + $currentUserCurrentHost | Add-Member -NotePropertyName '_name' -NotePropertyValue 'CurrentUserCurrentHost' -PassThru | ConvertTo-Json -Compress -EnumsAsStrings + } + + function GetOperation { + param ( + [Parameter(Mandatory = $true)] + $InputResource, + [Parameter()] + [switch] $AsJson + ) + + $profilePath = GetProfilePath -profileType $InputResource.profileType.ToString() + + $actualState = New-PwshResource -ProfileType $InputResource.profileType + + $actualState.profilePath = $profilePath + + $exists = Test-Path $profilePath + + if ($InputResource._exist -and $exists) { + $content = Get-Content -Path $profilePath + $actualState.Content = $content + } elseif ($InputResource._exist -and -not $exists) { + $actualState.Content = $null + $actualState._exist = $false + } elseif (-not $InputResource._exist -and $exists) { + $actualState.Content = Get-Content -Path $profilePath + $actualState._exist = $true + } else { + $actualState.Content = $null + $actualState._exist = $false + } + + if ($AsJson) { + return $actualState.ToJson() + } else { + return $actualState + } + } + + function SetOperation { + param ( + $InputResource + ) + + $actualState = GetOperation -InputResource $InputResource + + if ($InputResource._exist) { + if (-not $actualState._exist) { + $null = New-Item -Path $actualState.profilePath -ItemType File -Force + } + + if ($null -ne $InputResource.content) { + Set-Content -Path $actualState.profilePath -Value $InputResource.content + } + } elseif ($actualState._exist) { + Remove-Item -Path $actualState.profilePath -Force + } + } +} +End { + $inputJson = $input | ConvertFrom-Json + + if ($inputJson) { + $InputResource = New-PwshResource -ProfileType $inputJson.profileType -Content $inputJson.content -Exist $inputJson._exist + } + + switch ($Operation) { + 'get' { + GetOperation -InputResource $InputResource -AsJson + } + 'set' { + SetOperation -InputResource $InputResource + } + 'export' { + if ($inputJson) { + Write-Error "Input not supported for export operation" + exit 2 + } + + ExportOperation + } + } + + exit 0 +} diff --git a/experimental-feature-linux.json b/experimental-feature-linux.json new file mode 100644 index 00000000000..31f7b965a5b --- /dev/null +++ b/experimental-feature-linux.json @@ -0,0 +1,9 @@ +[ + "PSFeedbackProvider", + "PSLoadAssemblyFromNativeCode", + "PSNativeWindowsTildeExpansion", + "PSProfileDSCResource", + "PSSerializeJSONLongEnumAsNumber", + "PSRedirectToVariable", + "PSSubsystemPluginModel" +] diff --git a/experimental-feature-windows.json b/experimental-feature-windows.json new file mode 100644 index 00000000000..31f7b965a5b --- /dev/null +++ b/experimental-feature-windows.json @@ -0,0 +1,9 @@ +[ + "PSFeedbackProvider", + "PSLoadAssemblyFromNativeCode", + "PSNativeWindowsTildeExpansion", + "PSProfileDSCResource", + "PSSerializeJSONLongEnumAsNumber", + "PSRedirectToVariable", + "PSSubsystemPluginModel" +] diff --git a/global.json b/global.json new file mode 100644 index 00000000000..936a420a573 --- /dev/null +++ b/global.json @@ -0,0 +1,5 @@ +{ + "sdk": { + "version": "10.0.101" + } +} diff --git a/license_thirdparty_proprietary.txt b/license_thirdparty_proprietary.txt deleted file mode 100644 index 86d445c8930..00000000000 --- a/license_thirdparty_proprietary.txt +++ /dev/null @@ -1,771 +0,0 @@ -PowerShell 6.0 -Copyright (c) Microsoft Corporation -All rights reserved. -MIT License -Permission is hereby granted, free of charge, to any person obtaining a copy of this -software and associated documentation files (the "Software"), to deal in the Software -without restriction, including without limitation the rights to use, copy, modify, -merge, publish, distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to the following -conditions: -The above copyright notice and this permission notice shall be included in all copies -or substantial portions of the Software. -THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, -INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF -CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE -OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - -IMPORTANT NOTICE: THE SOFTWARE ALSO CONTAINS THIRD PARTY AND OTHER -PROPRIETARY SOFTWARE THAT ARE GOVERNED BY SEPARATE LICENSE TERMS. BY ACCEPTING -THE LICENSE TERMS ABOVE, YOU ALSO ACCEPT THE LICENSE TERMS GOVERNING THE -THIRD PARTY AND OTHER SOFTWARE, WHICH ARE SET FORTH BELOW: -The following components listed are governed by the license terms that follow the -component(s) name: ------------------------------------------------------- -Libmi.so ------------------------------------------------------- -Copyright (c) Microsoft Corporation -All rights reserved. -MIT License -Permission is hereby granted, free of charge, to any person obtaining a copy of this -software and associated documentation files (the "Software"), to deal in the Software -without restriction, including without limitation the rights to use, copy, modify, -merge, publish, distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to the following -conditions: -The above copyright notice and this permission notice shall be included in all copies -or substantial portions of the Software. -THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, -INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF -CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE -OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -The following components are governed by the MIT license, a copy of which appears -below the list of components: ------------------------------------------------------- -Newtonsoft.Json ------------------------------------------------------- -Copyright (c) 2007 James Newton-King -All rights reserved. -MIT License -Permission is hereby granted, free of charge, to any person obtaining a copy of this -software and associated documentation files (the "Software"), to deal in the Software -without restriction, including without limitation the rights to use, copy, modify, -merge, publish, distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to the following -conditions: -The above copyright notice and this permission notice shall be included in all copies -or substantial portions of the Software. -THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, -INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF -CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE -OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ---------------------------------------------------------- -Libuv v.1.9.0 ---------------------------------------------------------- - -https://raw.githubusercontent.com/aspnet/libuv-package/dev/content/License.txt - -This software is licensed to you by Microsoft Corporation under the original terms of -the copyright holder provided below: - -========================================= - -libuv is part of the Node project: http://nodejs.org/ -libuv may be distributed alone under Node's license: - -==== - -Copyright Joyent, Inc. and other Node contributors. All rights reserved. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to -deal in the Software without restriction, including without limitation the -rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -sell copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -IN THE SOFTWARE. - -==== - -This license applies to all parts of libuv that are not externally -maintained libraries. - -The externally maintained libraries used by libuv are: - - - tree.h (from FreeBSD), copyright Niels Provos. Two clause BSD license. - - - inet_pton and inet_ntop implementations, contained in src/inet.c, are -copyright the Internet Systems Consortium, Inc., and licensed under the ISC -license. - - - stdint-msvc2008.h (from msinttypes), copyright Alexander Chemeris. Three - clause BSD license. - - - pthread-fixes.h, pthread-fixes.c, copyright Google Inc. and Sony Mobile - Communications AB. Three clause BSD license. - - - android-ifaddrs.h, android-ifaddrs.c, copyright Berkeley Software Design Inc, -Kenneth MacKay and Emergya (Cloud4all, FP7/2007-2013, grant agreement - n° 289016). Three clause BSD license. - -========================================= - - ----------------------------------------------------- -• dotnet-test-xunit 2.2.0-preview2-build1029 -• xunit -• xunit.abstractions -• xunit.assert -• xunit.core -• xunit.extensibility.core -• xunit.extensibility.execution -• xunit.runner.reporters -• xunit.runner.utility ----------------------------------------------------- - -https://www.nuget.org/packages - -Copyright 2015 Outercurve Foundation - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. - - --------------------------------------------------- -• Microsoft.CodeAnalysis.Analyzers -• Microsoft.CodeAnalysis.Common -• Microsoft.CodeAnalysis.CSharp -• Microsoft.CodeAnalysis.VisualBasic -• Microsoft.CSharp -• Microsoft.DiaSymReader -• Microsoft.DiaSymReader.Native -• Microsoft.DotNet.Cli.Utils -• Microsoft.DotNet.InternalAbstractions -• Microsoft.DotNet.ProjectModel -• Microsoft.Extensions.DependencyModel -• Microsoft.Extensions.Testing.Abstractions -• Microsoft.NETCore -• Microsoft.NETCore.App -• Microsoft.NETCore.DotNetHost -• Microsoft.NETCore.DotNetHostPolicy -• Microsoft.NETCore.DotNetHostResolver -• Microsoft.NETCore.Jit -• Microsoft.NETCore.Platforms -• Microsoft.NETCore.Portable.Compatibility -• Microsoft.NETCore.Runtime -• Microsoft.NETCore.Runtime.CoreCLR -• Microsoft.NETCore.Runtime.Native -• Microsoft.NETCore.Targets -• Microsoft.NETCore.Windows.ApiSets -• Microsoft.VisualBasic -• Microsoft.Win32.Primitives -• Microsoft.Win32.Registry -• Microsoft.Win32.Registry.AccessControl -• NETStandard.Library -• runtime.any.System.Collections -• runtime.any.System.Diagnostics.Tools -• runtime.any.System.Diagnostics.Tracing -• runtime.any.System.Globalization -• runtime.any.System.Globalization.Calendars -• runtime.any.System.IO -• runtime.any.System.Reflection -• runtime.any.System.Reflection.Extensions -• runtime.any.System.Reflection.Primitives -• runtime.any.System.Resources.ResourceManager -• runtime.any.System.Runtime -• runtime.any.System.Runtime.Handles -• runtime.any.System.Runtime.InteropServices -• runtime.any.System.Text.Encoding -• runtime.any.System.Text.Encoding.Extensions -• runtime.any.System.Threading.Tasks -• runtime.any.System.Threading.Timer -• runtime.debian.8-x64.Microsoft.NETCore.DotNetHost -• runtime.debian.8-x64.Microsoft.NETCore.DotNetHostPolicy -• runtime.debian.8-x64.Microsoft.NETCore.DotNetHostResolver -• runtime.debian.8-x64.Microsoft.NETCore.Jit -• runtime.debian.8-x64.Microsoft.NETCore.Runtime.CoreCLR -• runtime.debian.8-x64.runtime.native.System -• runtime.debian.8-x64.runtime.native.System.IO.Compression -• runtime.debian.8-x64.runtime.native.System.Net.Http -• runtime.debian.8-x64.runtime.native.System.Net.Security -• runtime.debian.8-x64.runtime.native.System.Security.Cryptography -• runtime.native.System -• runtime.native.System.Data.SqlClient.sni -• runtime.native.System.IO.Compression -• runtime.native.System.Net.Http -• runtime.native.System.Net.Security -• runtime.native.System.Security.Cryptography -• runtime.osx.10.10-x64.Microsoft.NETCore.DotNetHost -• runtime.osx.10.10-x64.Microsoft.NETCore.DotNetHostPolicy -• runtime.osx.10.10-x64.Microsoft.NETCore.DotNetHostResolver -• runtime.osx.10.10-x64.Microsoft.NETCore.Jit -• runtime.osx.10.10-x64.Microsoft.NETCore.Runtime.CoreCLR -• runtime.osx.10.10-x64.runtime.native.System -• runtime.osx.10.10-x64.runtime.native.System.IO.Compression -• runtime.osx.10.10-x64.runtime.native.System.Net.Http -• runtime.osx.10.10-x64.runtime.native.System.Net.Security -• runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography -• runtime.rhel.7-x64.Microsoft.NETCore.DotNetHost -• runtime.rhel.7-x64.Microsoft.NETCore.DotNetHostPolicy -• runtime.rhel.7-x64.Microsoft.NETCore.DotNetHostResolver -• runtime.rhel.7-x64.Microsoft.NETCore.Jit -• runtime.rhel.7-x64.Microsoft.NETCore.Runtime.CoreCLR -• runtime.rhel.7-x64.runtime.native.System -• runtime.rhel.7-x64.runtime.native.System.IO.Compression -• runtime.rhel.7-x64.runtime.native.System.Net.Http -• runtime.rhel.7-x64.runtime.native.System.Net.Security -• runtime.rhel.7-x64.runtime.native.System.Security.Cryptography -• runtime.ubuntu.14.04-x64.Microsoft.NETCore.DotNetHost -• runtime.ubuntu.14.04-x64.Microsoft.NETCore.DotNetHostPolicy -• runtime.ubuntu.14.04-x64.Microsoft.NETCore.DotNetHostResolver -• runtime.ubuntu.14.04-x64.Microsoft.NETCore.Jit -• runtime.ubuntu.14.04-x64.Microsoft.NETCore.Runtime.CoreCLR -• runtime.ubuntu.14.04-x64.runtime.native.System -• runtime.ubuntu.14.04-x64.runtime.native.System.IO.Compression -• runtime.ubuntu.14.04-x64.runtime.native.System.Net.Http -• runtime.ubuntu.14.04-x64.runtime.native.System.Net.Security -• runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography -• runtime.ubuntu.16.04-x64.Microsoft.NETCore.DotNetHost -• runtime.ubuntu.16.04-x64.Microsoft.NETCore.DotNetHostPolicy -• runtime.ubuntu.16.04-x64.Microsoft.NETCore.DotNetHostResolver -• runtime.ubuntu.16.04-x64.Microsoft.NETCore.Jit -• runtime.ubuntu.16.04-x64.Microsoft.NETCore.Runtime.CoreCLR -• runtime.ubuntu.16.04-x64.runtime.native.System -• runtime.ubuntu.16.04-x64.runtime.native.System.IO.Compression -• runtime.ubuntu.16.04-x64.runtime.native.System.Net.Http -• runtime.ubuntu.16.04-x64.runtime.native.System.Net.Security -• runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography -• runtime.unix.Microsoft.Win32.Primitives -• runtime.unix.System.Console -• runtime.unix.System.Diagnostics.Debug -• runtime.unix.System.IO.FileSystem -• runtime.unix.System.Net.Primitives -• runtime.unix.System.Net.Sockets -• runtime.unix.System.Private.Uri -• runtime.unix.System.Runtime.Extensions -• runtime.win.Microsoft.Win32.Primitives -• runtime.win.System.Console -• runtime.win.System.Diagnostics.Debug -• runtime.win.System.IO.FileSystem -• runtime.win.System.Net.Primitives -• runtime.win.System.Net.Sockets -• runtime.win.System.Runtime.Extensions -• runtime.win7-x64.Microsoft.NETCore.DotNetHost -• runtime.win7-x64.Microsoft.NETCore.DotNetHostPolicy -• runtime.win7-x64.Microsoft.NETCore.DotNetHostResolver -• runtime.win7-x64.Microsoft.NETCore.Jit -• runtime.win7-x64.Microsoft.NETCore.Runtime.CoreCLR -• runtime.win7-x64.Microsoft.NETCore.Windows.ApiSets -• runtime.win7-x64.runtime.native.System.Data.SqlClient.sni -• runtime.win7-x64.runtime.native.System.IO.Compression -• runtime.win7-x86.runtime.native.System.Data.SqlClient.sni -• runtime.win7.System.Private.Uri -• runtime.win81-x64.Microsoft.NETCore.Windows.ApiSets -• System.AppContext -• System.Buffers -• System.Collections -• System.Collections.Concurrent -• System.Collections.Immutable -• System.Collections.NonGeneric -• System.Collections.Specialized -• System.ComponentModel -• System.ComponentModel.Annotations -• System.ComponentModel.EventBasedAsync -• System.ComponentModel.Primitives -• System.ComponentModel.TypeConverter -• System.Console -• System.Data.Common -• System.Data.SqlClient -• System.Diagnostics.Contracts -• System.Diagnostics.Debug -• System.Diagnostics.DiagnosticSource -• System.Diagnostics.FileVersionInfo -• System.Diagnostics.Process -• System.Diagnostics.StackTrace -• System.Diagnostics.TextWriterTraceListener -• System.Diagnostics.Tools -• System.Diagnostics.TraceSource -• System.Diagnostics.Tracing -• System.Dynamic.Runtime -• System.Globalization -• System.Globalization.Calendars -• System.Globalization.Extensions -• System.IO -• System.IO.Compression -• System.IO.Compression.ZipFile -• System.IO.FileSystem -• System.IO.FileSystem.AccessControl -• System.IO.FileSystem.DriveInfo -• System.IO.FileSystem.Primitives -• System.IO.FileSystem.Watcher -• System.IO.MemoryMappedFiles -• System.IO.Packaging -• System.IO.Pipes -• System.IO.UnmanagedMemoryStream -• System.Linq -• System.Linq.Expressions -• System.Linq.Parallel -• System.Linq.Queryable -• System.Net.Http -• System.Net.Http.WinHttpHandler -• System.Net.NameResolution -• System.Net.NetworkInformation -• System.Net.Ping -• System.Net.Primitives -• System.Net.Requests -• System.Net.Security -• System.Net.Sockets -• System.Net.WebHeaderCollection -• System.Net.WebSockets -• System.Net.WebSockets.Client -• System.Numerics.Vectors -• System.ObjectModel -• System.Private.DataContractSerialization -• System.Private.ServiceModel -• System.Private.Uri -• System.Reflection -• System.Reflection.DispatchProxy -• System.Reflection.Emit -• System.Reflection.Emit.ILGeneration -• System.Reflection.Emit.Lightweight -• System.Reflection.Extensions -• System.Reflection.Metadata -• System.Reflection.Primitives -• System.Reflection.TypeExtensions -• System.Resources.Reader -• System.Resources.ResourceManager -• System.Runtime -• System.Runtime.CompilerServices.VisualC -• System.Runtime.Extensions -• System.Runtime.Handles -• System.Runtime.InteropServices -• System.Runtime.InteropServices.PInvoke -• System.Runtime.InteropServices.RuntimeInformation -• System.Runtime.Loader -• System.Runtime.Numerics -• System.Runtime.Serialization.Json -• System.Runtime.Serialization.Primitives -• System.Runtime.Serialization.Xml -• System.Security.AccessControl -• System.Security.Claims -• System.Security.Cryptography.Algorithms -• System.Security.Cryptography.Cng -• System.Security.Cryptography.Csp -• System.Security.Cryptography.Encoding -• System.Security.Cryptography.OpenSsl -• System.Security.Cryptography.Pkcs -• System.Security.Cryptography.Primitives -• System.Security.Cryptography.X509Certificates -• System.Security.Principal -• System.Security.Principal.Windows -• System.Security.SecureString -• System.ServiceModel.Duplex -• System.ServiceModel.Http -• System.ServiceModel.NetTcp -• System.ServiceModel.Primitives -• System.ServiceModel.Security -• System.ServiceProcess.ServiceController -• System.Text.Encoding -• System.Text.Encoding.CodePages -• System.Text.Encoding.Extensions -• System.Text.Encodings.Web -• System.Text.RegularExpressions -• System.Threading -• System.Threading.AccessControl -• System.Threading.Overlapped -• System.Threading.Tasks -• System.Threading.Tasks.Dataflow -• System.Threading.Tasks.Extensions -• System.Threading.Tasks.Parallel -• System.Threading.Thread -• System.Threading.ThreadPool -• System.Threading.Timer -• System.Xml.ReaderWriter -• System.Xml.XDocument -• System.Xml.XmlDocument -• System.Xml.XmlSerializer -• System.Xml.XPath -• System.Xml.XPath.XDocument -• System.Xml.XPath.XmlDocument -------------------------------------------------------- - -MICROSOFT SOFTWARE LICENSE TERMS -MICROSOFT .NET LIBRARY -These license terms are an agreement between Microsoft Corporation (or based on where you -live, one of its affiliates) and you. Please read them. They apply to the software named above, -which includes the media on which you received it, if any. The terms also apply to any Microsoft -• updates, -• supplements, -• Internet-based services, and -• support services -for this software, unless other terms accompany those items. If so, those terms apply. -BY USING THE SOFTWARE, YOU ACCEPT THESE TERMS. IF YOU DO NOT ACCEPT THEM, DO NOT -USE THE SOFTWARE. -IF YOU COMPLY WITH THESE LICENSE TERMS, YOU HAVE THE PERPETUAL RIGHTS BELOW. -1. INSTALLATION AND USE RIGHTS. -a. Installation and Use. You may install and use any number of copies of the software to design, -develop and test your programs. -b. Third Party Programs. The software may include third party programs that Microsoft, not the -third party, licenses to you under this agreement. Notices, if any, for the third party program are -included for your information only. -2. DATA. The software may collect information about you and your use of the software, and send that -to Microsoft. Microsoft may use this information to improve our products and services. You can learn -more about data collection and use in the help documentation and the privacy statement -at https://go.microsoft.com/fwlink/?LinkId=528096 . Your use of the software operates as your -consent to these practices. -3. ADDITIONAL LICENSING REQUIREMENTS AND/OR USE RIGHTS. -a. DISTRIBUTABLE CODE. The software is comprised of Distributable Code. “Distributable -Code” is code that you are permitted to distribute in programs you develop if you comply with -the terms below. -i . Right to Use and Distribute. -• You may copy and distribute the object code form of the software. -• Third Party Distribution. You may permit distributors of your programs to copy and -distribute the Distributable Code as part of those programs. -ii. Distribution Requirements. For any Distributable Code you distribute, you must -• add significant primary functionality to it in your programs; -• require distributors and external end users to agree to terms that protect it at least as -much as this agreement; -• display your valid copyright notice on your programs; and -• indemnify, defend, and hold harmless Microsoft from any claims, including attorneys’ -fees, related to the distribution or use of your programs. -iii. Distribution Restrictions. You may not -• alter any copyright, trademark or patent notice in the Distributable Code; -• use Microsoft’s trademarks in your programs’ names or in a way that suggests your -programs come from or are endorsed by Microsoft; -• include Distributable Code in malicious, deceptive or unlawful programs; or -• modify or distribute the source code of any Distributable Code so that any part of it -becomes subject to an Excluded License. An Excluded License is one that requires, as a -condition of use, modification or distribution, that -• the code be disclosed or distributed in source code form; or -• others have the right to modify it. -4. SCOPE OF LICENSE. The software is licensed, not sold. This agreement only gives you some rights to -use the software. Microsoft reserves all other rights. Unless applicable law gives you more rights despite -this limitation, you may use the software only as expressly permitted in this agreement. In doing so, you -must comply with any technical limitations in the software that only allow you to use it in certain ways. -You may not -• work around any technical limitations in the software; -• reverse engineer, decompile or disassemble the software, except and only to the extent that -applicable law expressly permits, despite this limitation; -• publish the software for others to copy; -• rent, lease or lend the software; -• transfer the software or this agreement to any third party; or -• use the software for commercial software hosting services. -5. BACKUP COPY. You may make one backup copy of the software. You may use it only to reinstall the -software. -6. DOCUMENTATION. Any person that has valid access to your computer or internal network may copy -and use the documentation for your internal, reference purposes. -7. EXPORT RESTRICTIONS. The software is subject to United States export laws and regulations. You -must comply with all domestic and international export laws and regulations that apply to the software. -These laws include restrictions on destinations, end users and end use. For additional information, -see www.microsoft.com/exporting. -8. SUPPORT SERVICES. Because this software is “as is,” we may not provide support services for it. -9. ENTIRE AGREEMENT. This agreement, and the terms for supplements, updates, Internet-based -services and support services that you use, are the entire agreement for the software and support -services. -10. APPLICABLE LAW. -a. United States. If you acquired the software in the United States, Washington state law governs the -interpretation of this agreement and applies to claims for breach of it, regardless of conflict of laws -principles. The laws of the state where you live govern all other claims, including claims under state -consumer protection laws, unfair competition laws, and in tort. -b. Outside the United States. If you acquired the software in any other country, the laws of -that country apply. -11. LEGAL EFFECT. This agreement describes certain legal rights. You may have other rights under the -laws of your country. You may also have rights with respect to the party from whom you acquired the -software. This agreement does not change your rights under the laws of your country if the laws of your -country do not permit it to do so. -12. DISCLAIMER OF WARRANTY. THE SOFTWARE IS LICENSED “AS-IS.” YOU BEAR THE RISK OF -USING IT. MICROSOFT GIVES NO EXPRESS WARRANTIES, GUARANTEES OR CONDITIONS. -YOU MAY HAVE ADDITIONAL CONSUMER RIGHTS OR STATUTORY GUARANTEES UNDER YOUR -LOCAL LAWS WHICH THIS AGREEMENT CANNOT CHANGE. TO THE EXTENT PERMITTED -UNDER YOUR LOCAL LAWS, MICROSOFT EXCLUDES THE IMPLIED WARRANTIES OF -MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT. -FOR AUSTRALIA – YOU HAVE STATUTORY GUARANTEES UNDER THE AUSTRALIAN CONSUMER -LAW AND NOTHING IN THESE TERMS IS INTENDED TO AFFECT THOSE RIGHTS. -13. LIMITATION ON AND EXCLUSION OF REMEDIES AND DAMAGES. YOU CAN RECOVER FROM -MICROSOFT AND ITS SUPPLIERS ONLY DIRECT DAMAGES UP TO U.S. $5.00. YOU CANNOT -RECOVER ANY OTHER DAMAGES, INCLUDING CONSEQUENTIAL, LOST PROFITS, SPECIAL, -INDIRECT OR INCIDENTAL DAMAGES. -This limitation applies to -• anything related to the software, services, content (including code) on third party Internet sites, or -third party programs; and -• claims for breach of contract, breach of warranty, guarantee or condition, strict liability, negligence, -or other tort to the extent permitted by applicable law. -It also applies even if Microsoft knew or should have known about the possibility of the damages. The above -limitation or exclusion may not apply to you because your country may not allow the exclusion or limitation of -incidental, consequential or other damages. -Please note: As this software is distributed in Quebec, Canada, some of the clauses in this agreement are -provided below in French. -Remarque : Ce logiciel étant distribué au Québec, Canada, certaines des clauses dans ce contrat sont fournies -ci-dessous en français. -EXONÉRATION DE GARANTIE. Le logiciel visé par une licence est offert « tel quel ». Toute utilisation de ce -logiciel est à votre seule risque et péril. Microsoft n’accorde aucune autre garantie expresse. Vous pouvez -bénéficier de droits additionnels en vertu du droit local sur la protection des consommateurs, que ce contrat ne -peut modifier. La ou elles sont permises par le droit locale, les garanties implicites de qualité marchande, -d’adéquation à un usage particulier et d’absence de contrefaçon sont exclues. -LIMITATION DES DOMMAGES-INTÉRÊTS ET EXCLUSION DE RESPONSABILITÉ POUR LES -DOMMAGES. Vous pouvez obtenir de Microsoft et de ses fournisseurs une indemnisation en cas de -dommages directs uniquement à hauteur de 5,00 $ US. Vous ne pouvez prétendre à aucune indemnisation -pour les autres dommages, y compris les dommages spéciaux, indirects ou accessoires et pertes de bénéfices. -Cette limitationconcerne: -• tout ce qui est relié au logiciel, aux services ou au contenu (y compris le code) figurant sur des -sites Internet tiers ou dans des programmes tiers ; et -• les réclamations au titre de violation de contrat ou de garantie, ou au titre de responsabilité -stricte, de négligence ou d’une autre faute dans la limite autorisée par la loi en vigueur. -Elle s’applique également, même si Microsoft connaissait ou devrait connaître l’éventualité d’un tel dommage. -Si votre pays n’autorise pas l’exclusion ou la limitation de responsabilité pour les dommages indirects, -accessoires ou de quelque nature que ce soit, il se peut que la limitation ou l’exclusion ci-dessus ne -s’appliquera pas à votre égard. -EFFET JURIDIQUE. Le présent contrat décrit certains droits juridiques. Vous pourriez avoir d’autres droits -prévus par les lois de votre pays. Le présent contrat ne modifie pas les droits que vous confèrent les lois de -votre pays si celles-ci ne le permettent pas. - - --------------------------------------------------------- -• NuGet.Common -• NuGet.Configuration -• NuGet.DependencyResolver.Core -• NuGet.Frameworks -• NuGet.LibraryModel -• NuGet.Packaging -• NuGet.Packaging.Core -• NuGet.Packaging.Core.Types -• NuGet.ProjectModel -• NuGet.Protocol.Core.Types -• NuGet.Protocol.Core.v3 -• NuGet.Repositories -• NuGet.RuntimeModel -• NuGet.Versioning ----------------------------------------------------------- - -Copyright (c) .NET Foundation. All rights reserved. - -Licensed under the Apache License, Version 2.0 (the "License"); you may not use -these files except in compliance with the License. You may obtain a copy of the -License at - -http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software distributed -under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -CONDITIONS OF ANY KIND, either express or implied. See the License for the -specific language governing permissions and limitations under the License. - - ---------------------------------------------------------- -• Microsoft.Management.Infrastructure.dll -• Microsoft.Management.Infrastructure.Native.dll -• Microsoft.Management.Infrastructure.Unmanaged.dll ----------------------------------------------------------- -MICROSOFT SOFTWARE LICENSE TERMS -MANAGEMENT.INFRASTRUCTURE.DLL -MANAGEMENT.INFRASTRUCTURE.NATIVE.DLL -MANAGEMENT.INFRASTRUCTURE.UNMANAGED.DLL - -These license terms are an agreement between you and Microsoft Corporation (or one of its affiliates). They -apply to the software named above and any Microsoft services or software updates (except to the extent such -services or updates are accompanied by new or additional terms, in which case those different terms apply -prospectively and do not alter your or Microsoft’s rights relating to pre-updated software or services). IF YOU -COMPLY WITH THESE LICENSE TERMS, YOU HAVE THE RIGHTS BELOW. BY USING THE SOFTWARE, YOU -ACCEPT THESE TERMS. -1. INSTALLATION AND USE RIGHTS -a) General. You may install and use any number of copies of the software on your devices. -b) Included Microsoft Applications. The software may include other Microsoft applications. These -license terms apply to those included applications, if any, unless other license terms are provided with -the other Microsoft applications. -c) Third Party Software. The software may include third party applications that are licensed to you -under this agreement or under their own terms. License terms, notices, and acknowledgements, if any, -for the third party applications may be accessible online at http://aka.ms/thirdpartynotices or in an -accompanying notices file. Even if such applications are governed by other agreements, the disclaimer, -limitations on, and exclusions of damages below also apply to the extent allowed by applicable law. -2. TIME-SENSITIVE SOFTWARE -a) Period. The software is time-sensitive and may stop running on a date that is defined in the software. -b) Notice. You may receive periodic reminder notices of this date through the software. -c) Access to data. You may not be able to access data used in the software when it stops running. -3. PRE-RELEASE SOFTWARE. The software is a pre-release version. It may not operate correctly. It may be -different from the commercially released version. -4. FEEDBACK. If you give feedback about the software to Microsoft, you give to Microsoft, without charge, -the right to use, share and commercialize your feedback in any way and for any purpose. You will not give -feedback that is subject to a license that requires Microsoft to license its software or documentation to -third parties because Microsoft includes your feedback in them. These rights survive this agreement. -5. DATA COLLECTION. The software may collect information about you and your use of the software and -send that to Microsoft. Microsoft may use this information to provide services and improve Microsoft’s -products and services. Your opt-out rights, if any, are described in the product documentation. Some -features in the software may enable collection of data from users of your applications that access or use the -software. If you use these features to enable data collection in your applications, you must comply with -applicable law, including getting any required user consent, and maintain a prominent privacy policy that -accurately informs users about how you use, collect, and share their data. You can learn more about -Microsoft’s data collection and use in the product documentation and the Microsoft Privacy Statement at -https://go.microsoft.com/fwlink/?LinkId=521839. You agree to comply with all applicable provisions of the -Microsoft Privacy Statement. -6. FONTS. While the software is running, you may use its fonts to display and print content. You may only (i) -embed fonts in content as permitted by the embedding restrictions in the fonts; and (ii) temporarily -download them to a printer or other output device to help print content. -7. SCOPE OF LICENSE. The software is licensed, not sold. Microsoft reserves all other rights. Unless applicable -law gives you more rights despite this limitation, you will not (and have no right to): -a) work around any technical limitations in the software that only allow you to use it in certain ways; -b) reverse engineer, decompile or disassemble the software; -c) remove, minimize, block, or modify any notices of Microsoft or its suppliers in the software; -d) use the software in any way that is against the law or to create or propagate malware; or -e) share, publish, distribute, or lend the software, provide the software as a stand-alone hosted solution -for others to use, or transfer the software or this agreement to any third party. -8. EXPORT RESTRICTIONS. You must comply with all domestic and international export laws and regulations -that apply to the software, which include restrictions on destinations, end users, and end use. For further -information on export restrictions, visit http://aka.ms/exporting. -9. SUPPORT SERVICES. Microsoft is not obligated under this agreement to provide any support services for -the software. Any support provided is “as is”, “with all faults”, and without warranty of any kind. -10. UPDATES. The software may periodically check for updates, and download and install them for you. You -may obtain updates only from Microsoft or authorized sources. Microsoft may need to update your system -to provide you with updates. You agree to receive these automatic updates without any additional notice. -Updates may not include or support all existing software features, services, or peripheral devices. -11. ENTIRE AGREEMENT. This agreement, and any other terms Microsoft may provide for supplements, -updates, or third-party applications, is the entire agreement for the software. -12. APPLICABLE LAW AND PLACE TO RESOLVE DISPUTES. If you acquired the software in the United States -or Canada, the laws of the state or province where you live (or, if a business, where your principal place of -business is located) govern the interpretation of this agreement, claims for its breach, and all other claims -(including consumer protection, unfair competition, and tort claims), regardless of conflict of laws -principles. If you acquired the software in any other country, its laws apply. If U.S. federal jurisdiction exists, -you and Microsoft consent to exclusive jurisdiction and venue in the federal court in King County, -Washington for all disputes heard in court. If not, you and Microsoft consent to exclusive jurisdiction and -venue in the Superior Court of King County, Washington for all disputes heard in court. -13. CONSUMER RIGHTS; REGIONAL VARIATIONS. This agreement describes certain legal rights. You may -have other rights, including consumer rights, under the laws of your state, province, or country. Separate -and apart from your relationship with Microsoft, you may also have rights with respect to the party from -which you acquired the software. This agreement does not change those other rights if the laws of your -state, province, or country do not permit it to do so. For example, if you acquired the software in one of the -below regions, or mandatory country law applies, then the following provisions apply to you: -a) Australia. You have statutory guarantees under the Australian Consumer Law and nothing in this -agreement is intended to affect those rights. -b) Canada. If you acquired this software in Canada, you may stop receiving updates by turning off the -automatic update feature, disconnecting your device from the Internet (if and when you re-connect to -the Internet, however, the software will resume checking for and installing updates), or uninstalling the -software. The product documentation, if any, may also specify how to turn off updates for your specific -device or software. -c) Germany and Austria. -i. Warranty. The properly licensed software will perform substantially as described in -any Microsoft materials that accompany the software. However, Microsoft gives no -contractual guarantee in relation to the licensed software. -ii. Limitation of Liability. In case of intentional conduct, gross negligence, claims based -on the Product Liability Act, as well as, in case of death or personal or physical injury, -Microsoft is liable according to the statutory law. -Subject to the foregoing clause ii., Microsoft will only be liable for slight negligence if Microsoft is in -breach of such material contractual obligations, the fulfillment of which facilitate the due performance -of this agreement, the breach of which would endanger the purpose of this agreement and the -compliance with which a party may constantly trust in (so-called "cardinal obligations"). In other cases -of slight negligence, Microsoft will not be liable for slight negligence. -14. DISCLAIMER OF WARRANTY. THE SOFTWARE IS LICENSED “AS IS.” YOU BEAR THE RISK OF USING -IT. MICROSOFT GIVES NO EXPRESS WARRANTIES, GUARANTEES, OR CONDITIONS. TO THE EXTENT -PERMITTED UNDER APPLICABLE LAWS, MICROSOFT EXCLUDES ALL IMPLIED WARRANTIES, -INCLUDING MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, AND NON-INFRINGEMENT. -15. LIMITATION ON AND EXCLUSION OF DAMAGES. IF YOU HAVE ANY BASIS FOR RECOVERING -DAMAGES DESPITE THE PRECEDING DISCLAIMER OF WARRANTY, YOU CAN RECOVER FROM -MICROSOFT AND ITS SUPPLIERS ONLY DIRECT DAMAGES UP TO U.S. $5.00. YOU CANNOT RECOVER -ANY OTHER DAMAGES, INCLUDING CONSEQUENTIAL, LOST PROFITS, SPECIAL, INDIRECT OR -INCIDENTAL DAMAGES. -This limitation applies to (a) anything related to the software, services, content (including code) on -third party Internet sites, or third party applications; and (b) claims for breach of contract, warranty, -guarantee, or condition; strict liability, negligence, or other tort; or any other claim; in each case to -the extent permitted by applicable law. -It also applies even if Microsoft knew or should have known about the possibility of the damages. -The above limitation or exclusion may not apply to you because your state, province, or country -may not allow the exclusion or limitation of incidental, consequential, or other damages. - -Please note: As this software is distributed in Canada, some of the clauses in this agreement are -provided below in French. -Remarque: Ce logiciel étant distribué au Canada, certaines des clauses dans ce contrat sont fournies ci- -dessous en français. -EXONÉRATION DE GARANTIE. Le logiciel visé par une licence est offert « tel quel ». Toute utilisation de -ce logiciel est à votre seule risque et péril. Microsoft n’accorde aucune autre garantie expresse. Vous -pouvez bénéficier de droits additionnels en vertu du droit local sur la protection des consommateurs, -que ce contrat ne peut modifier. La ou elles sont permises par le droit locale, les garanties implicites de -qualité marchande, d’adéquation à un usage particulier et d’absence de contrefaçon sont exclues. -LIMITATION DES DOMMAGES-INTÉRÊTS ET EXCLUSION DE RESPONSABILITÉ POUR LES DOMMAGES. -Vous pouvez obtenir de Microsoft et de ses fournisseurs une indemnisation en cas de dommages directs -uniquement à hauteur de 5,00 $ US. Vous ne pouvez prétendre à aucune indemnisation pour les autres -dommages, y compris les dommages spéciaux, indirects ou accessoires et pertes de bénéfices. -Cette limitation concerne: -• tout ce qui est relié au logiciel, aux services ou au contenu (y compris le code) figurant sur des sites -Internet tiers ou dans des programmes tiers; et -• les réclamations au titre de violation de contrat ou de garantie, ou au titre de responsabilité stricte, -de négligence ou d’une autre faute dans la limite autorisée par la loi en vigueur. -Elle s’applique également, même si Microsoft connaissait ou devrait connaître l’éventualité d’un tel -dommage. Si votre pays n’autorise pas l’exclusion ou la limitation de responsabilité pour les dommages -indirects, accessoires ou de quelque nature que ce soit, il se peut que la limitation ou l’exclusion ci- -dessus ne s’appliquera pas à votre égard. -EFFET JURIDIQUE. Le présent contrat décrit certains droits juridiques. Vous pourriez avoir d’autres droits -prévus par les lois de votre pays. Le présent contrat ne modifie pas les droits que vous confèrent les lois -de votre pays si celles-ci ne le permettent pas. - -The following components are governed by the MIT license, a copy of which appears -below the list of components: ------------------------------------------------------- -tools/appimage.sh ------------------------------------------------------- -Copyright (c) 2016 Simon Peter -All rights reserved. -MIT License -Permission is hereby granted, free of charge, to any person obtaining a copy of this -software and associated documentation files (the "Software"), to deal in the Software -without restriction, including without limitation the rights to use, copy, modify, -merge, publish, distribute, sublicense, and/or sell copies of the Software, and to -permit persons to whom the Software is furnished to do so, subject to the following -conditions: -The above copyright notice and this permission notice shall be included in all copies -or substantial portions of the Software. -THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, -INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A -PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT -HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF -CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE -OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ---------------------------------------------------------- -DotNet/CoreFX -https://github.com/dotnet/corefx ---------------------------------------------------------- -The MIT License (MIT) - -Copyright (c) .NET Foundation and Contributors - -All rights reserved. - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. \ No newline at end of file diff --git a/nuget.config b/nuget.config index ca674f6c218..388a65572dd 100644 --- a/nuget.config +++ b/nuget.config @@ -2,8 +2,9 @@ - - - + + + + diff --git a/src/GlobalTools/PowerShell.Windows.x64/PowerShell.Windows.x64.csproj b/src/GlobalTools/PowerShell.Windows.x64/PowerShell.Windows.x64.csproj new file mode 100644 index 00000000000..49d607ebfed --- /dev/null +++ b/src/GlobalTools/PowerShell.Windows.x64/PowerShell.Windows.x64.csproj @@ -0,0 +1,33 @@ + + + + Exe + net10.0 + enable + enable + true + win-x64 + pwsh + $(PackageVersion) + true + ../../signing/visualstudiopublic.snk + + + + + + Modules\%(RecursiveDir)\%(FileName)%(Extension) + PreserveNewest + PreserveNewest + + + + + + + + + + + + diff --git a/src/GlobalTools/PowerShell.Windows.x64/Powershell_64.png b/src/GlobalTools/PowerShell.Windows.x64/Powershell_64.png new file mode 100644 index 00000000000..2a656ffc3c8 Binary files /dev/null and b/src/GlobalTools/PowerShell.Windows.x64/Powershell_64.png differ diff --git a/src/Microsoft.Management.Infrastructure.CimCmdlets/AssemblyInfo.cs b/src/Microsoft.Management.Infrastructure.CimCmdlets/AssemblyInfo.cs index 92e660ae73c..2e914a2299a 100644 --- a/src/Microsoft.Management.Infrastructure.CimCmdlets/AssemblyInfo.cs +++ b/src/Microsoft.Management.Infrastructure.CimCmdlets/AssemblyInfo.cs @@ -1,13 +1,6 @@ -/*============================================================================ - * Copyright (C) Microsoft Corporation, All rights reserved. - *============================================================================ - */ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. -using System.Diagnostics; -using System.Reflection; using System.Runtime.CompilerServices; -[assembly:InternalsVisibleTo("Microsoft.Windows.DSC.CoreConfProviders,PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] -[assembly:InternalsVisibleTo("Microsoft.Management.Infrastructure.CimCmdlets.Test,PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] -//This is equal to Debuggable(true,true) which enables IsJITTracking and Disable Optimization. CoreCLR does not have constructor Debuggable(true,true) -[assembly: Debuggable(DebuggableAttribute.DebuggingModes.DisableOptimizations)] +[assembly: InternalsVisibleTo("Microsoft.Windows.DSC.CoreConfProviders,PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] diff --git a/src/Microsoft.Management.Infrastructure.CimCmdlets/CimAsyncOperation.cs b/src/Microsoft.Management.Infrastructure.CimCmdlets/CimAsyncOperation.cs index 4cb2c7ab200..e794fd929d1 100644 --- a/src/Microsoft.Management.Infrastructure.CimCmdlets/CimAsyncOperation.cs +++ b/src/Microsoft.Management.Infrastructure.CimCmdlets/CimAsyncOperation.cs @@ -1,7 +1,5 @@ -/*============================================================================ - * Copyright (C) Microsoft Corporation, All rights reserved. - *============================================================================ - */ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. #region Using directives @@ -10,7 +8,6 @@ using System.Collections.Generic; using System.Management.Automation; using System.Threading; -using System.Globalization; #endregion @@ -29,9 +26,9 @@ internal abstract class CimAsyncOperation : IDisposable #region Constructor /// - /// Constructor + /// Initializes a new instance of the class. /// - public CimAsyncOperation() + protected CimAsyncOperation() { this.moreActionEvent = new ManualResetEventSlim(false); this.actionQueue = new ConcurrentQueue(); @@ -52,10 +49,10 @@ public CimAsyncOperation() /// /// object raised the event /// - /// event argument + /// Event argument. protected void NewCmdletActionHandler(object cimSession, CmdletActionEventArgs actionArgs) { - DebugHelper.WriteLogEx("Disposed {0}, action type = {1}", 0, this._disposed, actionArgs.Action); + DebugHelper.WriteLogEx("Disposed {0}, action type = {1}", 0, this.Disposed, actionArgs.Action); if (this.Disposed) { @@ -64,6 +61,7 @@ protected void NewCmdletActionHandler(object cimSession, CmdletActionEventArgs a // unblock the thread waiting for response (actionArgs.Action as CimSyncAction).OnComplete(); } + return; } @@ -82,14 +80,14 @@ protected void NewCmdletActionHandler(object cimSession, CmdletActionEventArgs a /// /// /// - /// object raised the event + /// object raised the event. /// - /// event argument + /// Event argument. protected void OperationCreatedHandler(object cimSession, OperationEventArgs actionArgs) { DebugHelper.WriteLogEx(); - lock (this.myLock) + lock (this.a_lock) { this.operationCount++; } @@ -102,14 +100,14 @@ protected void OperationCreatedHandler(object cimSession, OperationEventArgs act /// /// /// - /// object raised the event + /// object raised the event. /// - /// event argument + /// Event argument. protected void OperationDeletedHandler(object cimSession, OperationEventArgs actionArgs) { DebugHelper.WriteLogEx(); - lock (this.myLock) + lock (this.a_lock) { this.operationCount--; if (this.operationCount == 0) @@ -127,7 +125,7 @@ protected void OperationDeletedHandler(object cimSession, OperationEventArgs act /// /// /// - /// wrapper of cmdlet, for details + /// Wrapper of cmdlet, for details. /// public void ProcessActions(CmdletOperationBase cmdletOperation) { @@ -147,12 +145,12 @@ public void ProcessActions(CmdletOperationBase cmdletOperation) /// /// - /// process remaining actions until all operations are completed or - /// current cmdlet is terminated by user + /// Process remaining actions until all operations are completed or + /// current cmdlet is terminated by user. /// /// /// - /// wrapper of cmdlet, for details + /// Wrapper of cmdlet, for details. /// public void ProcessRemainActions(CmdletOperationBase cmdletOperation) { @@ -166,6 +164,7 @@ public void ProcessRemainActions(CmdletOperationBase cmdletOperation) DebugHelper.WriteLogEx("Either disposed or all operations completed.", 2); break; } + try { this.moreActionEvent.Wait(); @@ -179,6 +178,7 @@ public void ProcessRemainActions(CmdletOperationBase cmdletOperation) break; } } + ProcessActions(cmdletOperation); } @@ -186,11 +186,11 @@ public void ProcessRemainActions(CmdletOperationBase cmdletOperation) /// /// - /// Get action object from action queue + /// Get action object from action queue. /// /// - /// next action to execute - /// true indicates there is an valid action, otherwise false + /// Next action to execute. + /// True indicates there is an valid action, otherwise false. protected bool GetActionAndRemove(out CimBaseAction action) { return this.actionQueue.TryDequeue(out action); @@ -201,16 +201,13 @@ protected bool GetActionAndRemove(out CimBaseAction action) /// Add temporary object to cache. /// /// - /// Computer name of the cimsession - /// cimsession wrapper object + /// Cimsession wrapper object. protected void AddCimSessionProxy(CimSessionProxy sessionproxy) { lock (cimSessionProxyCacheLock) { - if (this.cimSessionProxyCache == null) - { - this.cimSessionProxyCache = new List(); - } + this.cimSessionProxyCache ??= new List(); + if (!this.cimSessionProxyCache.Contains(sessionproxy)) { this.cimSessionProxyCache.Add(sessionproxy); @@ -223,7 +220,7 @@ protected void AddCimSessionProxy(CimSessionProxy sessionproxy) /// Are there active operations? /// /// - /// true for having active operations, otherwise false. + /// True for having active operations, otherwise false. protected bool IsActive() { DebugHelper.WriteLogEx("Disposed {0}, Operation Count {1}", 2, this.Disposed, this.operationCount); @@ -237,7 +234,7 @@ protected bool IsActive() /// protected CimSessionProxy CreateCimSessionProxy(CimSessionProxy originalProxy) { - CimSessionProxy proxy = new CimSessionProxy(originalProxy); + CimSessionProxy proxy = new(originalProxy); this.SubscribeEventAndAddProxytoCache(proxy); return proxy; } @@ -259,7 +256,7 @@ protected CimSessionProxy CreateCimSessionProxy(CimSessionProxy originalProxy, b /// protected CimSessionProxy CreateCimSessionProxy(CimSession session) { - CimSessionProxy proxy = new CimSessionProxy(session); + CimSessionProxy proxy = new(session); this.SubscribeEventAndAddProxytoCache(proxy); return proxy; } @@ -282,7 +279,7 @@ protected CimSessionProxy CreateCimSessionProxy(CimSession session, bool passThr /// protected CimSessionProxy CreateCimSessionProxy(string computerName) { - CimSessionProxy proxy = new CimSessionProxy(computerName); + CimSessionProxy proxy = new(computerName); this.SubscribeEventAndAddProxytoCache(proxy); return proxy; } @@ -294,10 +291,9 @@ protected CimSessionProxy CreateCimSessionProxy(string computerName) /// /// /// - protected CimSessionProxy CreateCimSessionProxy(string computerName, - CimInstance cimInstance) + protected CimSessionProxy CreateCimSessionProxy(string computerName, CimInstance cimInstance) { - CimSessionProxy proxy = new CimSessionProxy(computerName, cimInstance); + CimSessionProxy proxy = new(computerName, cimInstance); this.SubscribeEventAndAddProxytoCache(proxy); return proxy; } @@ -317,7 +313,7 @@ protected CimSessionProxy CreateCimSessionProxy(string computerName, CimInstance } /// - /// Subscribe event from proxy and add proxy to cache + /// Subscribe event from proxy and add proxy to cache. /// /// protected void SubscribeEventAndAddProxytoCache(CimSessionProxy proxy) @@ -342,22 +338,20 @@ protected virtual void SubscribeToCimSessionProxyEvent(CimSessionProxy proxy) } /// - /// Retrieve the base object out if wrapped in psobject + /// Retrieve the base object out if wrapped in psobject. /// /// /// protected object GetBaseObject(object value) { - PSObject psObject = value as PSObject; - if (psObject == null) + if (value is not PSObject psObject) { return value; } else { object baseObject = psObject.BaseObject; - var arrayObject = baseObject as object[]; - if (arrayObject == null) + if (baseObject is not object[] arrayObject) { return baseObject; } @@ -368,6 +362,7 @@ protected object GetBaseObject(object value) { arraybaseObject[i] = GetBaseObject(arrayObject[i]); } + return arraybaseObject; } } @@ -379,41 +374,40 @@ protected object GetBaseObject(object value) /// if not thrown exception. /// /// - /// output the cimtype of the value, either Reference or ReferenceArray - /// + /// Output the cimtype of the value, either Reference or ReferenceArray. + /// The object. protected object GetReferenceOrReferenceArrayObject(object value, ref CimType referenceType) { - PSReference cimReference = value as PSReference; - if (cimReference != null) + if (value is PSReference cimReference) { object baseObject = GetBaseObject(cimReference.Value); - CimInstance cimInstance = baseObject as CimInstance; - if (cimInstance == null) + if (baseObject is not CimInstance cimInstance) { return null; } + referenceType = CimType.Reference; return cimInstance; } else { - object[] cimReferenceArray = value as object[]; - if (cimReferenceArray == null) + if (value is not object[] cimReferenceArray) { return null; } - else if (!(cimReferenceArray[0] is PSReference)) + else if (cimReferenceArray[0] is not PSReference) { return null; } + CimInstance[] cimInstanceArray = new CimInstance[cimReferenceArray.Length]; for (int i = 0; i < cimReferenceArray.Length; i++) { - PSReference tempCimReference = cimReferenceArray[i] as PSReference; - if (tempCimReference == null) + if (cimReferenceArray[i] is not PSReference tempCimReference) { return null; } + object baseObject = GetBaseObject(tempCimReference.Value); cimInstanceArray[i] = baseObject as CimInstance; if (cimInstanceArray[i] == null) @@ -421,6 +415,7 @@ protected object GetReferenceOrReferenceArrayObject(object value, ref CimType re return null; } } + referenceType = CimType.ReferenceArray; return cimInstanceArray; } @@ -438,10 +433,11 @@ protected bool Disposed { get { - return (Interlocked.Read(ref this._disposed) == 1); + return this._disposed == 1; } } - private long _disposed; + + private int _disposed; /// /// @@ -453,6 +449,7 @@ protected bool Disposed public void Dispose() { Dispose(true); + // This object will be cleaned up by the Dispose method. // Therefore, you should call GC.SuppressFinalize to // take this object off the finalization queue @@ -472,7 +469,7 @@ public void Dispose() /// other objects. Only unmanaged resources can be disposed. /// /// - /// Whether it is directly called + /// Whether it is directly called. protected virtual void Dispose(bool disposing) { if (Interlocked.CompareExchange(ref this._disposed, 1, 0) == 0) @@ -488,7 +485,7 @@ protected virtual void Dispose(bool disposing) /// /// - /// Clean up managed resources + /// Clean up managed resources. /// /// private void Cleanup() @@ -508,6 +505,7 @@ private void Cleanup() (action as CimSyncAction).OnComplete(); } } + if (this.cimSessionProxyCache != null) { List temporaryProxy; @@ -516,6 +514,7 @@ private void Cleanup() temporaryProxy = new List(this.cimSessionProxyCache); this.cimSessionProxyCache.Clear(); } + // clean up all proxy objects foreach (CimSessionProxy proxy in temporaryProxy) { @@ -525,10 +524,8 @@ private void Cleanup() } this.moreActionEvent.Dispose(); - if (this.ackedEvent != null) - { - this.ackedEvent.Dispose(); - } + this.ackedEvent?.Dispose(); + DebugHelper.WriteLog("Cleanup complete.", 2); } @@ -537,34 +534,34 @@ private void Cleanup() #region private members /// - /// lock object + /// Lock object. /// - private readonly object myLock = new object(); + private readonly object a_lock = new(); /// - /// number of active operations + /// Number of active operations. /// - private UInt32 operationCount = 0; + private uint operationCount; /// - /// Event to notify ps thread that more action is available + /// Event to notify ps thread that more action is available. /// - private ManualResetEventSlim moreActionEvent; + private readonly ManualResetEventSlim moreActionEvent; /// /// The following is the definition of action queue. /// The queue holding all actions to be executed in the context of either /// ProcessRecord or EndProcessing. /// - private ConcurrentQueue actionQueue; + private readonly ConcurrentQueue actionQueue; /// - /// lock object + /// Lock object. /// - private readonly object cimSessionProxyCacheLock = new object(); + private readonly object cimSessionProxyCacheLock = new(); /// - /// cache all objects related to + /// Cache all objects related to /// the current operation. /// private List cimSessionProxyCache; @@ -574,7 +571,7 @@ private void Cleanup() #region protected members /// /// Event to notify ps thread that either a ACK message sent back - /// or a error happened. Currently only used by class + /// or a error happened. Currently only used by /// . /// protected ManualResetEventSlim ackedEvent; @@ -584,6 +581,5 @@ private void Cleanup() internal const string ComputerNameArgument = @"ComputerName"; internal const string CimSessionArgument = @"CimSession"; #endregion - - }//End Class -}//End namespace + } +} diff --git a/src/Microsoft.Management.Infrastructure.CimCmdlets/CimBaseAction.cs b/src/Microsoft.Management.Infrastructure.CimCmdlets/CimBaseAction.cs index 829152def7b..4702e47e2f2 100644 --- a/src/Microsoft.Management.Infrastructure.CimCmdlets/CimBaseAction.cs +++ b/src/Microsoft.Management.Infrastructure.CimCmdlets/CimBaseAction.cs @@ -1,13 +1,11 @@ -/*============================================================================ - * Copyright (C) Microsoft Corporation, All rights reserved. - *============================================================================ - */ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. #region Using directives +using System; using System.Threading; using Microsoft.Management.Infrastructure.Options; -using System; #endregion @@ -19,9 +17,9 @@ namespace Microsoft.Management.Infrastructure.CimCmdlets internal abstract class CimBaseAction { /// - /// Constructor method. + /// Initializes a new instance of the class. /// - public CimBaseAction() + protected CimBaseAction() { } @@ -46,19 +44,8 @@ public virtual void Execute(CmdletOperationBase cmdlet) /// , object. /// /// - protected XOperationContextBase Context - { - get - { - return this.context; - } - set - { - this.context = value; - } - } - private XOperationContextBase context; - }//End Class + protected XOperationContextBase Context { get; set; } + } /// /// @@ -69,7 +56,7 @@ protected XOperationContextBase Context internal class CimSyncAction : CimBaseAction, IDisposable { /// - /// Constructor + /// Initializes a new instance of the class. /// public CimSyncAction() { @@ -82,7 +69,7 @@ public CimSyncAction() /// Block current thread until action completed /// /// - /// Response from user + /// Response from user. public virtual CimResponseType GetResponse() { this.Block(); @@ -91,7 +78,7 @@ public virtual CimResponseType GetResponse() /// /// - /// Set response result + /// Set the response result. /// /// internal CimResponseType ResponseType @@ -102,7 +89,7 @@ internal CimResponseType ResponseType /// /// /// Call this method when the action is completed or - /// the operation is terminated + /// the operation is terminated. /// /// internal virtual void OnComplete() @@ -112,7 +99,7 @@ internal virtual void OnComplete() /// /// - /// block current thread. + /// Block current thread. /// /// protected virtual void Block() @@ -124,12 +111,12 @@ protected virtual void Block() #region members /// - /// action completed event + /// Action completed event. /// - private ManualResetEventSlim completeEvent; + private readonly ManualResetEventSlim completeEvent; /// - /// response result + /// Response result. /// protected CimResponseType responseType; @@ -137,7 +124,7 @@ protected virtual void Block() #region IDisposable interface /// - /// IDisposable interface + /// IDisposable interface. /// private bool _disposed; @@ -170,7 +157,7 @@ public void Dispose() /// other objects. Only unmanaged resources can be disposed. /// /// - /// Whether it is directly called + /// Whether it is directly called. protected virtual void Dispose(bool disposing) { // Check to see if Dispose has already been called. @@ -181,10 +168,7 @@ protected virtual void Dispose(bool disposing) if (disposing) { // Dispose managed resources. - if (this.completeEvent != null) - { - this.completeEvent.Dispose(); - } + this.completeEvent?.Dispose(); } // Call the appropriate methods to clean up @@ -197,5 +181,5 @@ protected virtual void Dispose(bool disposing) } } #endregion - }//End Class -}//End namespace + } +} diff --git a/src/Microsoft.Management.Infrastructure.CimCmdlets/CimCmdletModuleInitialize.cs b/src/Microsoft.Management.Infrastructure.CimCmdlets/CimCmdletModuleInitialize.cs deleted file mode 100644 index b8138e87682..00000000000 --- a/src/Microsoft.Management.Infrastructure.CimCmdlets/CimCmdletModuleInitialize.cs +++ /dev/null @@ -1,116 +0,0 @@ -/*============================================================================ - * Copyright (C) Microsoft Corporation, All rights reserved. - *============================================================================ - */ - -#region Using directives - -using System; -using System.Globalization; -using System.Management.Automation; -using System.Management.Automation.Runspaces; - -#endregion - -namespace Microsoft.Management.Infrastructure.CimCmdlets -{ - /// - /// - /// Initialize the cimcmdlets. - /// - /// - /// - /// Provide a hook to the engine for startup initialization - /// w.r.t compiled assembly loading. - /// - public sealed class CimCmdletsAssemblyInitializer : IModuleAssemblyInitializer - { - /// - /// - /// constructor - /// - /// - public CimCmdletsAssemblyInitializer() - { - } - - /// - /// PowerShell engine will call this method when the cimcmdlets module - /// is loaded. - /// - public void OnImport() - { - DebugHelper.WriteLogEx(); - using (System.Management.Automation.PowerShell invoker = System.Management.Automation.PowerShell.Create(RunspaceMode.CurrentRunspace)) - { - foreach (CimCmdletAliasEntry alias in Aliases) - { - invoker.AddScript(string.Format(CultureInfo.CurrentUICulture, "Set-Alias -Name {0} -Value {1} -Option {2} -ErrorAction SilentlyContinue", alias.Name, alias.Value, alias.Options)); - DebugHelper.WriteLog(@"Add commands {0} of {1} with option {2} to current runspace.", 1, alias.Name, alias.Value, alias.Options); - } - System.Collections.ObjectModel.Collection psObjects = invoker.Invoke(); - DebugHelper.WriteLog(@"Invoke results {0}.", 1, psObjects.Count); - } - } - - #region readonly string - - /// - /// - /// CimCmdlet alias entry - /// - /// - internal sealed class CimCmdletAliasEntry - { - /// - /// - /// Constructor - /// - /// - /// - /// - internal CimCmdletAliasEntry(string name, string value) - { - this._name = name; - this._value = value; - } - - /// - /// The string defining the name of this alias - /// - internal string Name { get { return this._name; } } - private string _name; - - /// - /// The string defining real cmdlet name - /// - internal string Value { get { return this._value; } } - private string _value = String.Empty; - - /// - /// The string defining real cmdlet name - /// - internal ScopedItemOptions Options { get { return this._options; } } - private ScopedItemOptions _options = ScopedItemOptions.AllScope | ScopedItemOptions.ReadOnly; - } - - /// - /// Returns a new array of alias entries everytime it's called. - /// - internal static CimCmdletAliasEntry[] Aliases = new CimCmdletAliasEntry[] { - new CimCmdletAliasEntry("gcim", "Get-CimInstance"), - new CimCmdletAliasEntry("scim", "Set-CimInstance"), - new CimCmdletAliasEntry("ncim", "New-CimInstance "), - new CimCmdletAliasEntry("rcim", "Remove-cimInstance"), - new CimCmdletAliasEntry("icim", "Invoke-CimMethod"), - new CimCmdletAliasEntry("gcai", "Get-CimAssociatedInstance"), - new CimCmdletAliasEntry("rcie", "Register-CimIndicationEvent"), - new CimCmdletAliasEntry("ncms", "New-CimSession"), - new CimCmdletAliasEntry("rcms", "Remove-cimSession"), - new CimCmdletAliasEntry("gcms", "Get-CimSession"), - new CimCmdletAliasEntry("ncso", "New-CimSessionOption"), - new CimCmdletAliasEntry("gcls", "Get-CimClass"), - }; - #endregion - }//End Class -}//End namespace diff --git a/src/Microsoft.Management.Infrastructure.CimCmdlets/CimCommandBase.cs b/src/Microsoft.Management.Infrastructure.CimCmdlets/CimCommandBase.cs index 2f545311bc2..89f7478b513 100644 --- a/src/Microsoft.Management.Infrastructure.CimCmdlets/CimCommandBase.cs +++ b/src/Microsoft.Management.Infrastructure.CimCmdlets/CimCommandBase.cs @@ -1,7 +1,5 @@ -/*============================================================================ - * Copyright (C) Microsoft Corporation, All rights reserved. - *============================================================================ - */ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. #region Using directives using System; @@ -27,39 +25,25 @@ namespace Microsoft.Management.Infrastructure.CimCmdlets internal class ParameterDefinitionEntry { /// - /// constructor + /// Initializes a new instance of the class. /// /// /// internal ParameterDefinitionEntry(string parameterSetName, bool mandatory) { - this.mandatory = mandatory; - this.parameterSetName = parameterSetName; + this.IsMandatory = mandatory; + this.ParameterSetName = parameterSetName; } /// - /// property ParameterSetName + /// Property ParameterSetName. /// - internal string ParameterSetName - { - get - { - return this.parameterSetName; - } - } - private readonly string parameterSetName = null; + internal string ParameterSetName { get; } /// - /// Whether the parameter is mandatory to the set + /// Whether the parameter is mandatory to the set. /// - internal bool IsMandatory - { - get - { - return this.mandatory; - } - } - private readonly bool mandatory = false; + internal bool IsMandatory { get; } } /// @@ -70,135 +54,77 @@ internal bool IsMandatory internal class ParameterSetEntry { /// - /// constructor + /// Initializes a new instance of the class. /// /// - internal ParameterSetEntry(UInt32 mandatoryParameterCount) + internal ParameterSetEntry(uint mandatoryParameterCount) { - this.mandatoryParameterCount = mandatoryParameterCount; - this.isDefaultParameterSet = false; + this.MandatoryParameterCount = mandatoryParameterCount; + this.IsDefaultParameterSet = false; reset(); } /// - /// constructor + /// Initializes a new instance of the class. /// /// internal ParameterSetEntry(ParameterSetEntry toClone) { - this.mandatoryParameterCount = toClone.MandatoryParameterCount; - this.isDefaultParameterSet = toClone.IsDefaultParameterSet; + this.MandatoryParameterCount = toClone.MandatoryParameterCount; + this.IsDefaultParameterSet = toClone.IsDefaultParameterSet; reset(); } /// - /// constructor + /// Initializes a new instance of the class. /// /// /// - internal ParameterSetEntry(UInt32 mandatoryParameterCount, bool isDefault) + internal ParameterSetEntry(uint mandatoryParameterCount, bool isDefault) { - this.mandatoryParameterCount = mandatoryParameterCount; - this.isDefaultParameterSet = isDefault; + this.MandatoryParameterCount = mandatoryParameterCount; + this.IsDefaultParameterSet = isDefault; reset(); } /// - /// reset the internal status + /// Reset the internal status. /// internal void reset() { - this.setMandatoryParameterCount = this.setMandatoryParameterCountAtBeginProcess; - this.isValueSet = this.isValueSetAtBeginProcess; + this.SetMandatoryParameterCount = this.SetMandatoryParameterCountAtBeginProcess; + this.IsValueSet = this.IsValueSetAtBeginProcess; } /// - /// property DefaultParameterSet + /// Property DefaultParameterSet /// - internal bool IsDefaultParameterSet - { - get - { - return this.isDefaultParameterSet; - } - } - private readonly bool isDefaultParameterSet = false; + internal bool IsDefaultParameterSet { get; } /// - /// property MandatoryParameterCount + /// Property MandatoryParameterCount /// - internal UInt32 MandatoryParameterCount - { - get - { - return this.mandatoryParameterCount; - } - } - private readonly UInt32 mandatoryParameterCount = 0; + internal uint MandatoryParameterCount { get; } = 0; /// - /// property IsValueSet + /// Property IsValueSet /// - internal bool IsValueSet - { - get - { - return this.isValueSet; - } - set - { - this.isValueSet = value; - } - } - private bool isValueSet = false; + internal bool IsValueSet { get; set; } /// - /// property IsValueSetAtBeginProcess + /// Property IsValueSetAtBeginProcess /// - internal bool IsValueSetAtBeginProcess - { - get - { - return this.isValueSetAtBeginProcess; - } - set - { - this.isValueSetAtBeginProcess = value; - } - } - private bool isValueSetAtBeginProcess = false; + internal bool IsValueSetAtBeginProcess { get; set; } /// - /// property SetMandatoryParameterCount + /// Property SetMandatoryParameterCount /// - internal UInt32 SetMandatoryParameterCount - { - get - { - return this.setMandatoryParameterCount; - } - set - { - this.setMandatoryParameterCount = value; - } - } - private UInt32 setMandatoryParameterCount = 0; + internal uint SetMandatoryParameterCount { get; set; } = 0; /// - /// property SetMandatoryParameterCountAtBeginProcess + /// Property SetMandatoryParameterCountAtBeginProcess /// - internal UInt32 SetMandatoryParameterCountAtBeginProcess - { - get - { - return this.setMandatoryParameterCountAtBeginProcess; - } - set - { - this.setMandatoryParameterCountAtBeginProcess = value; - } - } - private UInt32 setMandatoryParameterCountAtBeginProcess = 0; + internal uint SetMandatoryParameterCountAtBeginProcess { get; set; } = 0; } /// @@ -207,7 +133,7 @@ internal UInt32 SetMandatoryParameterCountAtBeginProcess internal class ParameterBinder { /// - /// constructor + /// Initializes a new instance of the class. /// /// /// @@ -246,12 +172,12 @@ internal ParameterBinder( /// throw exception /// /// - private List parametersetNamesList = new List(); + private List parametersetNamesList = new(); /// - /// Parameter names list + /// Parameter names list. /// - private List parameterNamesList = new List(); + private readonly List parameterNamesList = new(); /// /// @@ -260,12 +186,12 @@ internal ParameterBinder( /// throw exception /// /// - private List parametersetNamesListAtBeginProcess = new List(); + private List parametersetNamesListAtBeginProcess = new(); /// - /// Parameter names list before begin process + /// Parameter names list before begin process. /// - private List parameterNamesListAtBeginProcess = new List(); + private readonly List parameterNamesListAtBeginProcess = new(); /// /// @@ -300,7 +226,7 @@ internal void reset() /// /// /// - /// throw if conflict parameter was set + /// Throw if conflict parameter was set. internal void SetParameter(string parameterName, bool isBeginProcess) { DebugHelper.WriteLogEx("ParameterName = {0}, isBeginProcess = {1}", 0, parameterName, isBeginProcess); @@ -321,7 +247,7 @@ internal void SetParameter(string parameterName, bool isBeginProcess) if (this.parametersetNamesList.Count == 0) { - List nameset = new List(); + List nameset = new(); foreach (ParameterDefinitionEntry parameterDefinitionEntry in this.parameterDefinitionEntries[parameterName]) { DebugHelper.WriteLogEx("parameterset name = '{0}'; mandatory = '{1}'", 1, parameterDefinitionEntry.ParameterSetName, parameterDefinitionEntry.IsMandatory); @@ -336,8 +262,10 @@ internal void SetParameter(string parameterName, bool isBeginProcess) { psEntry.SetMandatoryParameterCountAtBeginProcess++; } + DebugHelper.WriteLogEx("parameterset name = '{0}'; SetMandatoryParameterCount = '{1}'", 1, parameterDefinitionEntry.ParameterSetName, psEntry.SetMandatoryParameterCount); } + if (!psEntry.IsValueSet) { psEntry.IsValueSet = true; @@ -346,8 +274,10 @@ internal void SetParameter(string parameterName, bool isBeginProcess) psEntry.IsValueSetAtBeginProcess = true; } } + nameset.Add(parameterDefinitionEntry.ParameterSetName); } + this.parametersetNamesList = nameset; if (isBeginProcess) { @@ -356,7 +286,7 @@ internal void SetParameter(string parameterName, bool isBeginProcess) } else { - List nameset = new List(); + List nameset = new(); foreach (ParameterDefinitionEntry entry in this.parameterDefinitionEntries[parameterName]) { if (this.parametersetNamesList.Contains(entry.ParameterSetName)) @@ -370,6 +300,7 @@ internal void SetParameter(string parameterName, bool isBeginProcess) { psEntry.SetMandatoryParameterCountAtBeginProcess++; } + DebugHelper.WriteLogEx("parameterset name = '{0}'; SetMandatoryParameterCount = '{1}'", 1, entry.ParameterSetName, @@ -377,9 +308,10 @@ internal void SetParameter(string parameterName, bool isBeginProcess) } } } + if (nameset.Count == 0) { - throw new PSArgumentException(Strings.UnableToResolveParameterSetName); + throw new PSArgumentException(CimCmdletStrings.UnableToResolveParameterSetName); } else { @@ -393,7 +325,7 @@ internal void SetParameter(string parameterName, bool isBeginProcess) } /// - /// Get the parameter set name based on current binding results + /// Get the parameter set name based on current binding results. /// /// internal string GetParameterSet() @@ -402,7 +334,7 @@ internal string GetParameterSet() string boundParameterSetName = null; string defaultParameterSetName = null; - List noMandatoryParameterSet = new List(); + List noMandatoryParameterSet = new(); // Looking for parameter set which have mandatory parameters foreach (string parameterSetName in this.parameterSetEntries.Keys) @@ -422,19 +354,23 @@ internal string GetParameterSet() { defaultParameterSetName = parameterSetName; } + if (entry.IsValueSet) { noMandatoryParameterSet.Add(parameterSetName); } + continue; } + if ((entry.SetMandatoryParameterCount == entry.MandatoryParameterCount) && this.parametersetNamesList.Contains(parameterSetName)) { if (boundParameterSetName != null) { - throw new PSArgumentException(Strings.UnableToResolveParameterSetName); + throw new PSArgumentException(CimCmdletStrings.UnableToResolveParameterSetName); } + boundParameterSetName = parameterSetName; } } @@ -445,7 +381,7 @@ internal string GetParameterSet() // throw if there are > 1 parameter set if (noMandatoryParameterSet.Count > 1) { - throw new PSArgumentException(Strings.UnableToResolveParameterSetName); + throw new PSArgumentException(CimCmdletStrings.UnableToResolveParameterSetName); } else if (noMandatoryParameterSet.Count == 1) { @@ -454,21 +390,19 @@ internal string GetParameterSet() } // Looking for default parameter set - if (boundParameterSetName == null) - { - boundParameterSetName = defaultParameterSetName; - } + boundParameterSetName ??= defaultParameterSetName; // throw if still can not find the parameter set name if (boundParameterSetName == null) { - throw new PSArgumentException(Strings.UnableToResolveParameterSetName); + throw new PSArgumentException(CimCmdletStrings.UnableToResolveParameterSetName); } + return boundParameterSetName; } /// - /// Deep clone the parameter entries to member variable + /// Deep clone the parameter entries to member variable. /// private void CloneParameterEntries( Dictionary> parameters, @@ -486,11 +420,10 @@ private void CloneParameterEntries( #endregion /// - /// Base command for all cim cmdlets + /// Base command for all cim cmdlets. /// public class CimBaseCommand : Cmdlet, IDisposable { - #region resolve parameter set name /// /// @@ -511,18 +444,19 @@ internal void CheckParameterSet() { try { - this.parameterSetName = this.parameterBinder.GetParameterSet(); + this.ParameterSetName = this.parameterBinder.GetParameterSet(); } finally { this.parameterBinder.reset(); } } - DebugHelper.WriteLog("current parameterset is: " + this.parameterSetName, 4); + + DebugHelper.WriteLog("current parameterset is: " + this.ParameterSetName, 4); } /// - /// Redirect to parameterBinder to set one parameter + /// Redirect to parameterBinder to set one parameter. /// /// internal void SetParameter(object value, string parameterName) @@ -535,17 +469,15 @@ internal void SetParameter(object value, string parameterName) { return; } - if (this.parameterBinder != null) - { - this.parameterBinder.SetParameter(parameterName, this.AtBeginProcess); - } + + this.parameterBinder?.SetParameter(parameterName, this.AtBeginProcess); } #endregion #region constructors /// - /// constructor + /// Initializes a new instance of the class. /// internal CimBaseCommand() { @@ -554,7 +486,7 @@ internal CimBaseCommand() } /// - /// constructor + /// Initializes a new instance of the class. /// internal CimBaseCommand(Dictionary> parameters, Dictionary sets) @@ -573,13 +505,13 @@ internal CimBaseCommand(Dictionary> pa protected override void StopProcessing() { Dispose(); - }//End StopProcessing() + } #endregion #region IDisposable interface /// - /// IDisposable interface + /// IDisposable interface. /// private bool disposed; @@ -612,7 +544,7 @@ public void Dispose() /// other objects. Only unmanaged resources can be disposed. /// /// - /// Whether it is directly called + /// Whether it is directly called. protected void Dispose(bool disposing) { // Check to see if Dispose has already been called. @@ -636,24 +568,21 @@ protected void Dispose(bool disposing) } /// - /// Clean up resources + /// Clean up resources. /// protected virtual void DisposeInternal() { // Dispose managed resources. - if (this.operation != null) - { - this.operation.Dispose(); - } + this.operation?.Dispose(); } #endregion #region private members /// - /// Parameter binder used to resolve parameter set name + /// Parameter binder used to resolve parameter set name. /// - private ParameterBinder parameterBinder; + private readonly ParameterBinder parameterBinder; /// /// @@ -663,29 +592,24 @@ protected virtual void DisposeInternal() private CimAsyncOperation operation; /// - /// lock object + /// Lock object. /// - private readonly object myLock = new object(); - - /// - /// - /// parameter set name - /// - /// - private string parameterSetName; + private readonly object myLock = new(); /// /// This flag is introduced to resolve the parameter set name /// during process record - /// Whether at begin process time, false means in processrecord + /// Whether at begin process time, false means in processrecord. /// private bool atBeginProcess = true; + internal bool AtBeginProcess { get { return this.atBeginProcess; } + set { this.atBeginProcess = value; @@ -703,6 +627,11 @@ internal bool AtBeginProcess /// internal CimAsyncOperation AsyncOperation { + get + { + return this.operation; + } + set { lock (this.myLock) @@ -711,10 +640,6 @@ internal CimAsyncOperation AsyncOperation this.operation = value; } } - get - { - return this.operation; - } } /// @@ -722,16 +647,10 @@ internal CimAsyncOperation AsyncOperation /// Get current ParameterSetName of the cmdlet /// /// - internal string ParameterSetName - { - get - { - return this.parameterSetName; - } - } + internal string ParameterSetName { get; private set; } /// - /// Gets/Sets cmdlet operation wrapper object + /// Gets/Sets cmdlet operation wrapper object. /// internal virtual CmdletOperationBase CmdletOperation { @@ -744,9 +663,10 @@ internal virtual CmdletOperationBase CmdletOperation /// Throw terminating error /// /// + [System.Diagnostics.CodeAnalysis.DoesNotReturn] internal void ThrowTerminatingError(Exception exception, string operation) { - ErrorRecord errorRecord = new ErrorRecord(exception, operation, ErrorCategory.InvalidOperation, this); + ErrorRecord errorRecord = new(exception, operation, ErrorCategory.InvalidOperation, this); this.CmdletOperation.ThrowTerminatingError(errorRecord); } @@ -755,78 +675,77 @@ internal void ThrowTerminatingError(Exception exception, string operation) #region internal const strings /// - /// alias CN - computer name + /// Alias CN - computer name. /// internal const string AliasCN = "CN"; /// - /// alias ServerName - computer name + /// Alias ServerName - computer name. /// internal const string AliasServerName = "ServerName"; /// - /// alias OT - operation timeout + /// Alias OT - operation timeout. /// internal const string AliasOT = "OT"; /// - /// session set name + /// Session set name. /// internal const string SessionSetName = "SessionSet"; /// - /// computer set name + /// Computer set name. /// internal const string ComputerSetName = "ComputerSet"; /// - /// class name computer set name + /// Class name computer set name. /// internal const string ClassNameComputerSet = "ClassNameComputerSet"; /// - /// resource Uri computer set name + /// Resource Uri computer set name. /// internal const string ResourceUriComputerSet = "ResourceUriComputerSet"; /// - /// computer set name + /// computer set name. /// internal const string CimInstanceComputerSet = "CimInstanceComputerSet"; /// - /// query computer set name + /// Query computer set name. /// internal const string QueryComputerSet = "QueryComputerSet"; /// - /// class name session set name + /// Class name session set name. /// internal const string ClassNameSessionSet = "ClassNameSessionSet"; - /// - /// resource Uri session set name + /// Resource Uri session set name. /// internal const string ResourceUriSessionSet = "ResourceUriSessionSet"; /// - /// session set name + /// session set name. /// internal const string CimInstanceSessionSet = "CimInstanceSessionSet"; /// - /// query session set name + /// Query session set name. /// internal const string QuerySessionSet = "QuerySessionSet"; /// - /// computer set name + /// computer set name. /// internal const string CimClassComputerSet = "CimClassComputerSet"; /// - /// session set name + /// session set name. /// internal const string CimClassSessionSet = "CimClassSessionSet"; @@ -848,17 +767,17 @@ internal void ThrowTerminatingError(Exception exception, string operation) #endregion /// - /// credential parameter set + /// Credential parameter set. /// internal const string CredentialParameterSet = "CredentialParameterSet"; /// - /// certificate parameter set + /// Certificate parameter set. /// internal const string CertificateParameterSet = "CertificateParameterSet"; /// - /// CimInstance parameter alias + /// CimInstance parameter alias. /// internal const string AliasCimInstance = "CimInstance"; @@ -879,19 +798,19 @@ internal void ThrowInvalidAuthenticationTypeError( string parameterName, PasswordAuthenticationMechanism authentication) { - string message = String.Format(CultureInfo.CurrentUICulture, Strings.InvalidAuthenticationTypeWithNullCredential, + string message = string.Format(CultureInfo.CurrentUICulture, CimCmdletStrings.InvalidAuthenticationTypeWithNullCredential, authentication, ImpersonatedAuthenticationMechanism.None, ImpersonatedAuthenticationMechanism.Negotiate, ImpersonatedAuthenticationMechanism.Kerberos, ImpersonatedAuthenticationMechanism.NtlmDomain); - PSArgumentOutOfRangeException exception = new PSArgumentOutOfRangeException( + PSArgumentOutOfRangeException exception = new( parameterName, authentication, message); ThrowTerminatingError(exception, operationName); } /// - /// Throw conflict parameter error + /// Throw conflict parameter error. /// /// /// @@ -901,10 +820,10 @@ internal void ThrowConflictParameterWasSet( string parameterName, string conflictParameterName) { - string message = String.Format(CultureInfo.CurrentUICulture, - Strings.ConflictParameterWasSet, + string message = string.Format(CultureInfo.CurrentUICulture, + CimCmdletStrings.ConflictParameterWasSet, parameterName, conflictParameterName); - PSArgumentException exception = new PSArgumentException(message, parameterName); + PSArgumentException exception = new(message, parameterName); ThrowTerminatingError(exception, operationName); } @@ -920,24 +839,26 @@ internal void ThrowInvalidProperty( string operationName, IDictionary actualValue) { - StringBuilder propList = new StringBuilder(); + StringBuilder propList = new(); foreach (string property in propertiesList) { if (propList.Length > 0) { - propList.Append(","); + propList.Append(','); } + propList.Append(property); } - string message = String.Format(CultureInfo.CurrentUICulture, Strings.CouldNotFindPropertyFromGivenClass, + + string message = string.Format(CultureInfo.CurrentUICulture, CimCmdletStrings.CouldNotFindPropertyFromGivenClass, className, propList); - PSArgumentOutOfRangeException exception = new PSArgumentOutOfRangeException( + PSArgumentOutOfRangeException exception = new( parameterName, actualValue, message); ThrowTerminatingError(exception, operationName); } /// - /// Create credentials based on given authentication type and PSCredential + /// Create credentials based on given authentication type and PSCredential. /// /// /// @@ -955,7 +876,6 @@ internal CimCredential CreateCimCredentials(PSCredential psCredentials, NetworkCredential networkCredential = psCredentials.GetNetworkCredential(); DebugHelper.WriteLog("Domain:{0}; UserName:{1}; Password:{2}.", 1, networkCredential.Domain, networkCredential.UserName, psCredentials.Password); credentials = new CimCredential(passwordAuthentication, networkCredential.Domain, networkCredential.UserName, psCredentials.Password); - } else { @@ -978,11 +898,13 @@ internal CimCredential CreateCimCredentials(PSCredential psCredentials, ThrowInvalidAuthenticationTypeError(operationName, parameterName, passwordAuthentication); return null; } + credentials = new CimCredential(impersonatedAuthentication); } + DebugHelper.WriteLogEx("return credential {0}", 1, credentials); return credentials; } #endregion - }//End Class -}//End namespace + } +} diff --git a/src/Microsoft.Management.Infrastructure.CimCmdlets/CimGetAssociatedInstance.cs b/src/Microsoft.Management.Infrastructure.CimCmdlets/CimGetAssociatedInstance.cs index b21e8304ace..0ab4988c57d 100644 --- a/src/Microsoft.Management.Infrastructure.CimCmdlets/CimGetAssociatedInstance.cs +++ b/src/Microsoft.Management.Infrastructure.CimCmdlets/CimGetAssociatedInstance.cs @@ -1,12 +1,8 @@ -/*============================================================================ - * Copyright (C) Microsoft Corporation, All rights reserved. - *============================================================================ - */ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. #region Using directives -using System.Collections; -using System; using System.Collections.Generic; #endregion @@ -21,9 +17,7 @@ namespace Microsoft.Management.Infrastructure.CimCmdlets internal sealed class CimGetAssociatedInstance : CimAsyncOperation { /// - /// - /// Constructor - /// + /// Initializes a new instance of the class. /// public CimGetAssociatedInstance() : base() @@ -35,7 +29,7 @@ public CimGetAssociatedInstance() /// Base on parametersetName to retrieve associated ciminstances /// /// - /// object + /// object. public void GetCimAssociatedInstance(GetCimAssociatedInstanceCommand cmdlet) { IEnumerable computerNames = ConstValue.GetComputerNames(cmdlet.ComputerName); @@ -46,15 +40,17 @@ public void GetCimAssociatedInstance(GetCimAssociatedInstanceCommand cmdlet) // try to use namespace of ciminstance, then fall back to default namespace nameSpace = ConstValue.GetNamespace(cmdlet.CimInstance.CimSystemProperties.Namespace); } - List proxys = new List(); + + List proxys = new(); switch (cmdlet.ParameterSetName) { case CimBaseCommand.ComputerSetName: foreach (string computerName in computerNames) { CimSessionProxy proxy = CreateSessionProxy(computerName, cmdlet.CimInstance, cmdlet); - proxys.Add(proxy); + proxys.Add(proxy); } + break; case CimBaseCommand.SessionSetName: foreach (CimSession session in cmdlet.CimSession) @@ -62,10 +58,12 @@ public void GetCimAssociatedInstance(GetCimAssociatedInstanceCommand cmdlet) CimSessionProxy proxy = CreateSessionProxy(session, cmdlet); proxys.Add(proxy); } + break; default: return; } + foreach (CimSessionProxy proxy in proxys) { proxy.EnumerateAssociatedInstancesAsync( @@ -87,7 +85,7 @@ public void GetCimAssociatedInstance(GetCimAssociatedInstanceCommand cmdlet) /// /// /// - private void SetSessionProxyProperties( + private static void SetSessionProxyProperties( ref CimSessionProxy proxy, GetCimAssociatedInstanceCommand cmdlet) { @@ -119,7 +117,7 @@ private CimSessionProxy CreateSessionProxy( } /// - /// Create and set properties + /// Create and set properties. /// /// /// @@ -135,5 +133,5 @@ private CimSessionProxy CreateSessionProxy( #endregion - }//End Class -}//End namespace + } +} diff --git a/src/Microsoft.Management.Infrastructure.CimCmdlets/CimGetCimClass.cs b/src/Microsoft.Management.Infrastructure.CimCmdlets/CimGetCimClass.cs index 2fec594ad3a..c775325094b 100644 --- a/src/Microsoft.Management.Infrastructure.CimCmdlets/CimGetCimClass.cs +++ b/src/Microsoft.Management.Infrastructure.CimCmdlets/CimGetCimClass.cs @@ -1,15 +1,10 @@ -/*============================================================================ - * Copyright (C) Microsoft Corporation, All rights reserved. - *============================================================================ - */ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. #region Using directives -using System.Collections; -using System; using System.Collections.Generic; using System.Management.Automation; -using System.Globalization; #endregion @@ -22,9 +17,7 @@ namespace Microsoft.Management.Infrastructure.CimCmdlets internal class CimGetCimClassContext : XOperationContextBase { /// - /// - /// Constructor - /// + /// Initializes a new instance of the class. /// /// /// @@ -35,10 +28,10 @@ internal CimGetCimClassContext( string thePropertyName, string theQualifierName) { - this.className = theClassName; - this.methodName = theMethodName; - this.propertyName = thePropertyName; - this.qualifierName = theQualifierName; + this.ClassName = theClassName; + this.MethodName = theMethodName; + this.PropertyName = thePropertyName; + this.QualifierName = theQualifierName; } /// @@ -49,12 +42,7 @@ internal CimGetCimClassContext( /// Wildcard expansion should be allowed. /// /// - public String ClassName - { - get { return className; } - set { className = value; } - } - private String className; + public string ClassName { get; set; } /// /// @@ -63,11 +51,7 @@ public String ClassName /// Then Filter the by given methodname /// /// - internal String MethodName - { - get { return methodName; } - } - private String methodName; + internal string MethodName { get; } /// /// @@ -76,11 +60,7 @@ internal String MethodName /// Filter the by given property name. /// /// - internal String PropertyName - { - get { return propertyName; } - } - private String propertyName; + internal string PropertyName { get; } /// /// @@ -89,11 +69,7 @@ internal String PropertyName /// Filter the by given methodname /// /// - internal String QualifierName - { - get { return qualifierName; } - } - private String qualifierName; + internal string QualifierName { get; } } /// @@ -104,9 +80,7 @@ internal String QualifierName internal sealed class CimGetCimClass : CimAsyncOperation { /// - /// - /// Constructor - /// + /// Initializes a new instance of the class. /// public CimGetCimClass() : base() @@ -118,13 +92,13 @@ public CimGetCimClass() /// Base on parametersetName to retrieve /// /// - /// object + /// object. public void GetCimClass(GetCimClassCommand cmdlet) { - List proxys = new List(); + List proxys = new(); string nameSpace = ConstValue.GetNamespace(cmdlet.Namespace); - string className = (cmdlet.ClassName == null) ? @"*" : cmdlet.ClassName; - CimGetCimClassContext context = new CimGetCimClassContext( + string className = cmdlet.ClassName ?? @"*"; + CimGetCimClassContext context = new( cmdlet.ClassName, cmdlet.MethodName, cmdlet.PropertyName, @@ -142,6 +116,7 @@ public void GetCimClass(GetCimClassCommand cmdlet) proxys.Add(proxy); } } + break; case CimBaseCommand.SessionSetName: { @@ -152,6 +127,7 @@ public void GetCimClass(GetCimClassCommand cmdlet) proxys.Add(proxy); } } + break; default: return; @@ -184,11 +160,12 @@ public void GetCimClass(GetCimClassCommand cmdlet) /// /// /// - private void SetSessionProxyProperties( + private static void SetSessionProxyProperties( ref CimSessionProxy proxy, GetCimClassCommand cmdlet) { proxy.OperationTimeout = cmdlet.OperationTimeoutSec; + proxy.Amended = cmdlet.Amended; } /// @@ -210,7 +187,7 @@ private CimSessionProxy CreateSessionProxy( } /// - /// Create and set properties + /// Create and set properties. /// /// /// @@ -227,5 +204,5 @@ private CimSessionProxy CreateSessionProxy( #endregion - }//End Class -}//End namespace + } +} diff --git a/src/Microsoft.Management.Infrastructure.CimCmdlets/CimGetInstance.cs b/src/Microsoft.Management.Infrastructure.CimCmdlets/CimGetInstance.cs index 562bce3b626..371b06d9356 100644 --- a/src/Microsoft.Management.Infrastructure.CimCmdlets/CimGetInstance.cs +++ b/src/Microsoft.Management.Infrastructure.CimCmdlets/CimGetInstance.cs @@ -1,12 +1,8 @@ -/*============================================================================ - * Copyright (C) Microsoft Corporation, All rights reserved. - *============================================================================ - */ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. #region Using directives -using System; -using System.Collections; using System.Collections.Generic; using System.Globalization; using System.Management.Automation; @@ -17,7 +13,7 @@ namespace Microsoft.Management.Infrastructure.CimCmdlets { /// - /// a class used to add pstypename to partial ciminstance + /// A class used to add pstypename to partial ciminstance /// for , if -KeyOnly /// or -SelectProperties is been specified, then add a pstypename: /// "Microsoft.Management.Infrastructure.CimInstance#__PartialCIMInstance" @@ -25,12 +21,12 @@ namespace Microsoft.Management.Infrastructure.CimCmdlets internal class FormatPartialCimInstance : IObjectPreProcess { /// - /// Partial ciminstance pstypename + /// Partial ciminstance pstypename. /// internal const string PartialPSTypeName = @"Microsoft.Management.Infrastructure.CimInstance#__PartialCIMInstance"; /// - /// Add pstypename to the resultobject if necessary + /// Add pstypename to the resultobject if necessary. /// /// /// @@ -42,6 +38,7 @@ public object Process(object resultObject) obj.TypeNames.Insert(0, PartialPSTypeName); return obj; } + return resultObject; } } @@ -54,6 +51,7 @@ public object Process(object resultObject) internal class CimGetInstance : CimAsyncOperation { /// + /// Initializes a new instance of the class. /// /// Constructor /// @@ -67,7 +65,7 @@ public CimGetInstance() : base() /// Base on parametersetName to retrieve ciminstances /// /// - /// object + /// object. public void GetCimInstance(GetCimInstanceCommand cmdlet) { GetCimInstanceInternal(cmdlet); @@ -84,8 +82,8 @@ protected void GetCimInstanceInternal(CimBaseCommand cmdlet) IEnumerable computerNames = ConstValue.GetComputerNames( GetComputerName(cmdlet)); string nameSpace; - List proxys = new List(); - bool isGetCimInstanceCommand = (cmdlet is GetCimInstanceCommand); + List proxys = new(); + bool isGetCimInstanceCommand = cmdlet is GetCimInstanceCommand; CimInstance targetCimInstance = null; switch (cmdlet.ParameterSetName) { @@ -96,10 +94,12 @@ protected void GetCimInstanceInternal(CimBaseCommand cmdlet) CimSessionProxy proxy = CreateSessionProxy(computerName, targetCimInstance, cmdlet); if (isGetCimInstanceCommand) { - this.SetPreProcess(proxy, cmdlet as GetCimInstanceCommand); + SetPreProcess(proxy, cmdlet as GetCimInstanceCommand); } + proxys.Add(proxy); } + break; case CimBaseCommand.ClassNameComputerSet: case CimBaseCommand.QueryComputerSet: @@ -109,10 +109,12 @@ protected void GetCimInstanceInternal(CimBaseCommand cmdlet) CimSessionProxy proxy = CreateSessionProxy(computerName, cmdlet); if (isGetCimInstanceCommand) { - this.SetPreProcess(proxy, cmdlet as GetCimInstanceCommand); + SetPreProcess(proxy, cmdlet as GetCimInstanceCommand); } + proxys.Add(proxy); } + break; case CimBaseCommand.ClassNameSessionSet: case CimBaseCommand.CimInstanceSessionSet: @@ -123,14 +125,17 @@ protected void GetCimInstanceInternal(CimBaseCommand cmdlet) CimSessionProxy proxy = CreateSessionProxy(session, cmdlet); if (isGetCimInstanceCommand) { - this.SetPreProcess(proxy, cmdlet as GetCimInstanceCommand); + SetPreProcess(proxy, cmdlet as GetCimInstanceCommand); } + proxys.Add(proxy); } + break; default: break; } + switch (cmdlet.ParameterSetName) { case CimBaseCommand.ClassNameComputerSet: @@ -154,6 +159,7 @@ protected void GetCimInstanceInternal(CimBaseCommand cmdlet) proxy.EnumerateInstancesAsync(nameSpace, GetClassName(cmdlet)); } } + break; case CimBaseCommand.CimInstanceComputerSet: case CimBaseCommand.CimInstanceSessionSet: @@ -165,6 +171,7 @@ protected void GetCimInstanceInternal(CimBaseCommand cmdlet) proxy.GetInstanceAsync(nameSpace, instance); } } + break; case CimBaseCommand.QueryComputerSet: case CimBaseCommand.QuerySessionSet: @@ -175,6 +182,7 @@ protected void GetCimInstanceInternal(CimBaseCommand cmdlet) ConstValue.GetQueryDialectWithDefault(GetQueryDialect(cmdlet)), GetQuery(cmdlet)); } + break; case CimBaseCommand.ResourceUriSessionSet: case CimBaseCommand.ResourceUriComputerSet: @@ -182,6 +190,7 @@ protected void GetCimInstanceInternal(CimBaseCommand cmdlet) { proxy.EnumerateInstancesAsync(GetNamespace(cmdlet), GetClassName(cmdlet)); } + break; default: break; @@ -190,7 +199,7 @@ protected void GetCimInstanceInternal(CimBaseCommand cmdlet) #region bridge methods to read properties from cmdlet - protected static String[] GetComputerName(CimBaseCommand cmdlet) + protected static string[] GetComputerName(CimBaseCommand cmdlet) { if (cmdlet is GetCimInstanceCommand) { @@ -204,10 +213,11 @@ protected static String[] GetComputerName(CimBaseCommand cmdlet) { return (cmdlet as SetCimInstanceCommand).ComputerName; } + return null; } - protected static String GetNamespace(CimBaseCommand cmdlet) + protected static string GetNamespace(CimBaseCommand cmdlet) { if (cmdlet is GetCimInstanceCommand) { @@ -221,6 +231,7 @@ protected static String GetNamespace(CimBaseCommand cmdlet) { return (cmdlet as SetCimInstanceCommand).Namespace; } + return null; } @@ -238,19 +249,21 @@ protected static CimSession[] GetCimSession(CimBaseCommand cmdlet) { return (cmdlet as SetCimInstanceCommand).CimSession; } + return null; } - protected static String GetClassName(CimBaseCommand cmdlet) + protected static string GetClassName(CimBaseCommand cmdlet) { if (cmdlet is GetCimInstanceCommand) { return (cmdlet as GetCimInstanceCommand).ClassName; } + return null; } - protected static String GetQuery(CimBaseCommand cmdlet) + protected static string GetQuery(CimBaseCommand cmdlet) { if (cmdlet is GetCimInstanceCommand) { @@ -264,33 +277,33 @@ protected static String GetQuery(CimBaseCommand cmdlet) { return (cmdlet as SetCimInstanceCommand).Query; } + return null; } internal static bool IsClassNameQuerySet(CimBaseCommand cmdlet) { DebugHelper.WriteLogEx(); - GetCimInstanceCommand cmd = cmdlet as GetCimInstanceCommand; - if (cmd != null) + if (cmdlet is GetCimInstanceCommand cmd) { if (cmd.QueryDialect != null || cmd.SelectProperties != null || cmd.Filter != null) { return true; } } + return false; } - protected static String CreateQuery(CimBaseCommand cmdlet) + protected static string CreateQuery(CimBaseCommand cmdlet) { DebugHelper.WriteLogEx(); - GetCimInstanceCommand cmd = cmdlet as GetCimInstanceCommand; - if (cmd != null) + if (cmdlet is GetCimInstanceCommand cmd) { - StringBuilder propertyList = new StringBuilder(); + StringBuilder propertyList = new(); if (cmd.SelectProperties == null) { - propertyList.Append("*"); + propertyList.Append('*'); } else { @@ -298,19 +311,22 @@ protected static String CreateQuery(CimBaseCommand cmdlet) { if (propertyList.Length > 0) { - propertyList.Append(","); + propertyList.Append(','); } + propertyList.Append(property); } } + return (cmd.Filter == null) ? - String.Format(CultureInfo.CurrentUICulture, queryWithoutWhere, propertyList, cmd.ClassName) : - String.Format(CultureInfo.CurrentUICulture, queryWithWhere, propertyList, cmd.ClassName, cmd.Filter); + string.Format(CultureInfo.CurrentUICulture, queryWithoutWhere, propertyList, cmd.ClassName) : + string.Format(CultureInfo.CurrentUICulture, queryWithWhere, propertyList, cmd.ClassName, cmd.Filter); } + return null; } - protected static String GetQueryDialect(CimBaseCommand cmdlet) + protected static string GetQueryDialect(CimBaseCommand cmdlet) { if (cmdlet is GetCimInstanceCommand) { @@ -324,6 +340,7 @@ protected static String GetQueryDialect(CimBaseCommand cmdlet) { return (cmdlet as SetCimInstanceCommand).QueryDialect; } + return null; } @@ -341,6 +358,7 @@ protected static CimInstance GetCimInstanceParameter(CimBaseCommand cmdlet) { return (cmdlet as SetCimInstanceCommand).CimInstance; } + return null; } #endregion @@ -354,7 +372,7 @@ protected static CimInstance GetCimInstanceParameter(CimBaseCommand cmdlet) /// /// /// - private void SetSessionProxyProperties( + private static void SetSessionProxyProperties( ref CimSessionProxy proxy, CimBaseCommand cmdlet) { @@ -364,7 +382,7 @@ private void SetSessionProxyProperties( proxy.KeyOnly = getCimInstance.KeyOnly; proxy.Shallow = getCimInstance.Shallow; proxy.OperationTimeout = getCimInstance.OperationTimeoutSec; - if(getCimInstance.ResourceUri != null ) + if (getCimInstance.ResourceUri != null) { proxy.ResourceUri = getCimInstance.ResourceUri; } @@ -373,11 +391,12 @@ private void SetSessionProxyProperties( { RemoveCimInstanceCommand removeCimInstance = cmdlet as RemoveCimInstanceCommand; proxy.OperationTimeout = removeCimInstance.OperationTimeoutSec; - if(removeCimInstance.ResourceUri != null ) + if (removeCimInstance.ResourceUri != null) { proxy.ResourceUri = removeCimInstance.ResourceUri; } - CimRemoveCimInstanceContext context = new CimRemoveCimInstanceContext( + + CimRemoveCimInstanceContext context = new( ConstValue.GetNamespace(removeCimInstance.Namespace), proxy); proxy.ContextObject = context; @@ -386,11 +405,12 @@ private void SetSessionProxyProperties( { SetCimInstanceCommand setCimInstance = cmdlet as SetCimInstanceCommand; proxy.OperationTimeout = setCimInstance.OperationTimeoutSec; - if(setCimInstance.ResourceUri != null ) + if (setCimInstance.ResourceUri != null) { proxy.ResourceUri = setCimInstance.ResourceUri; } - CimSetCimInstanceContext context = new CimSetCimInstanceContext( + + CimSetCimInstanceContext context = new( ConstValue.GetNamespace(setCimInstance.Namespace), setCimInstance.Property, proxy, @@ -438,7 +458,7 @@ protected CimSessionProxy CreateSessionProxy( } /// - /// Create and set properties + /// Create and set properties. /// /// /// @@ -472,7 +492,7 @@ protected CimSessionProxy CreateSessionProxy( } /// - /// Create and set properties + /// Create and set properties. /// /// /// @@ -489,11 +509,11 @@ protected CimSessionProxy CreateSessionProxy( /// /// Set object to proxy to pre-process - /// the result object if necessary + /// the result object if necessary. /// /// /// - private void SetPreProcess(CimSessionProxy proxy, GetCimInstanceCommand cmdlet) + private static void SetPreProcess(CimSessionProxy proxy, GetCimInstanceCommand cmdlet) { if (cmdlet.KeyOnly || (cmdlet.SelectProperties != null)) { @@ -504,14 +524,14 @@ private void SetPreProcess(CimSessionProxy proxy, GetCimInstanceCommand cmdlet) #region const strings /// - /// wql query format with where clause + /// Wql query format with where clause. /// private const string queryWithWhere = @"SELECT {0} FROM {1} WHERE {2}"; /// - /// wql query format without where clause + /// Wql query format without where clause. /// private const string queryWithoutWhere = @"SELECT {0} FROM {1}"; #endregion - }//End Class -}//End namespace + } +} diff --git a/src/Microsoft.Management.Infrastructure.CimCmdlets/CimIndicationWatcher.cs b/src/Microsoft.Management.Infrastructure.CimCmdlets/CimIndicationWatcher.cs index 2d33fc3cfd4..899e67495cc 100644 --- a/src/Microsoft.Management.Infrastructure.CimCmdlets/CimIndicationWatcher.cs +++ b/src/Microsoft.Management.Infrastructure.CimCmdlets/CimIndicationWatcher.cs @@ -1,7 +1,5 @@ -/*============================================================================ - * Copyright (C) Microsoft Corporation, All rights reserved. - *============================================================================ - */ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. #region Using directives @@ -26,18 +24,19 @@ public abstract class CimIndicationEventArgs : EventArgs /// Returns an Object value for an operation context /// /// - public Object Context + public object Context { get { return context; } } - internal Object context; + + internal object context; } /// - /// Cimindication exception event args, which containing occurred exception + /// Cimindication exception event args, which containing occurred exception. /// public class CimIndicationEventExceptionEventArgs : CimIndicationEventArgs { @@ -46,25 +45,16 @@ public class CimIndicationEventExceptionEventArgs : CimIndicationEventArgs /// Returns an exception /// /// - public Exception Exception - { - get - { - return exception; - } - } - private Exception exception; + public Exception Exception { get; } /// - /// - /// Constructor - /// + /// Initializes a new instance of the class. /// /// public CimIndicationEventExceptionEventArgs(Exception theException) { context = null; - this.exception = theException; + this.Exception = theException; } } @@ -75,42 +65,40 @@ public CimIndicationEventExceptionEventArgs(Exception theException) public class CimIndicationEventInstanceEventArgs : CimIndicationEventArgs { /// - /// Get ciminstance of the indication object + /// Get ciminstance of the indication object. /// public CimInstance NewEvent { get { - return (result == null) ? null : result.Instance; + return result?.Instance; } } /// - /// Get MachineId of the indication object + /// Get MachineId of the indication object. /// public string MachineId { get { - return (result == null) ? null : result.MachineId; + return result?.MachineId; } } /// - /// Get BookMark of the indication object + /// Get BookMark of the indication object. /// public string Bookmark { get { - return (result == null) ? null : result.Bookmark; + return result?.Bookmark; } } /// - /// - /// Constructor - /// + /// Initializes a new instance of the class. /// /// public CimIndicationEventInstanceEventArgs(CimSubscriptionResult result) @@ -124,7 +112,7 @@ public CimIndicationEventInstanceEventArgs(CimSubscriptionResult result) /// subscription result /// /// - private CimSubscriptionResult result; + private readonly CimSubscriptionResult result; } /// @@ -137,7 +125,7 @@ public CimIndicationEventInstanceEventArgs(CimSubscriptionResult result) public class CimIndicationWatcher { /// - /// status of object. + /// Status of object. /// internal enum Status { @@ -154,9 +142,7 @@ internal enum Status public event EventHandler CimIndicationArrived; /// - /// - /// Constructor with given computerName, namespace, queryExpression and timeout - /// + /// Initializes a new instance of the class. /// /// /// @@ -167,7 +153,7 @@ public CimIndicationWatcher( string theNamespace, string queryDialect, string queryExpression, - UInt32 operationTimeout) + uint operationTimeout) { ValidationHelper.ValidateNoNullorWhiteSpaceArgument(queryExpression, queryExpressionParameterName); computerName = ConstValue.GetComputerName(computerName); @@ -176,9 +162,7 @@ public CimIndicationWatcher( } /// - /// - /// Constructor with given cimsession, namespace, queryExpression and timeout - /// + /// Initializes a new instance of the class. /// /// /// @@ -189,7 +173,7 @@ public CimIndicationWatcher( string theNamespace, string queryDialect, string queryExpression, - UInt32 operationTimeout) + uint operationTimeout) { ValidationHelper.ValidateNoNullorWhiteSpaceArgument(queryExpression, queryExpressionParameterName); ValidationHelper.ValidateNoNullArgument(cimSession, cimSessionParameterName); @@ -208,7 +192,7 @@ private void Initialize( string theNameSpace, string theQueryDialect, string theQueryExpression, - UInt32 theOperationTimeout) + uint theOperationTimeout) { enableRaisingEvents = false; status = Status.Default; @@ -237,14 +221,11 @@ private void NewSubscriptionResultHandler(object src, CimSubscriptionEventArgs a if (temp != null) { // raise the event - CimSubscriptionResultEventArgs resultArgs = args as CimSubscriptionResultEventArgs; - if (resultArgs != null) + if (args is CimSubscriptionResultEventArgs resultArgs) temp(this, new CimIndicationEventInstanceEventArgs(resultArgs.Result)); - else + else if (args is CimSubscriptionExceptionEventArgs exceptionArgs) { - CimSubscriptionExceptionEventArgs exceptionArgs = args as CimSubscriptionExceptionEventArgs; - if (exceptionArgs != null) - temp(this, new CimIndicationEventExceptionEventArgs(exceptionArgs.Exception)); + temp(this, new CimIndicationEventExceptionEventArgs(exceptionArgs.Exception)); } } } @@ -258,13 +239,14 @@ private void NewSubscriptionResultHandler(object src, CimSubscriptionEventArgs a /// If set EnableRaisingEvents to false, which will be ignored /// /// - [BrowsableAttribute(false)] + [Browsable(false)] public bool EnableRaisingEvents { get { return enableRaisingEvents; } + set { DebugHelper.WriteLogEx(); @@ -275,6 +257,7 @@ public bool EnableRaisingEvents } } } + private bool enableRaisingEvents; /// @@ -286,7 +269,7 @@ public void Start() { DebugHelper.WriteLogEx(); - lock(myLock) + lock (myLock) { if (status == Status.Default) { @@ -308,6 +291,7 @@ public void Start() this.queryExpression, this.operationTimeout); } + status = Status.Started; } } @@ -331,6 +315,7 @@ public void Stop() DebugHelper.WriteLog("Dispose CimRegisterCimIndication object", 4); this.cimRegisterCimIndication.Dispose(); } + status = Status.Stopped; } } @@ -339,7 +324,7 @@ public void Stop() #region internal method /// /// Set the cmdlet object to throw ThrowTerminatingError - /// in case there is a subscription failure + /// in case there is a subscription failure. /// /// internal void SetCmdlet(Cmdlet cmdlet) @@ -360,22 +345,22 @@ internal void SetCmdlet(Cmdlet cmdlet) private CimRegisterCimIndication cimRegisterCimIndication; /// - /// the status of object + /// The status of object. /// private Status status; /// - /// lock started field + /// Lock started field. /// private object myLock; /// - /// CimSession parameter name + /// CimSession parameter name. /// private const string cimSessionParameterName = "cimSession"; /// - /// QueryExpression parameter name + /// QueryExpression parameter name. /// private const string queryExpressionParameterName = "queryExpression"; @@ -390,8 +375,8 @@ internal void SetCmdlet(Cmdlet cmdlet) private string nameSpace; private string queryDialect; private string queryExpression; - private UInt32 operationTimeout; + private uint operationTimeout; #endregion #endregion } -}//End namespace +} diff --git a/src/Microsoft.Management.Infrastructure.CimCmdlets/CimInvokeCimMethod.cs b/src/Microsoft.Management.Infrastructure.CimCmdlets/CimInvokeCimMethod.cs index 63c8bafbe01..50f4d12dd74 100644 --- a/src/Microsoft.Management.Infrastructure.CimCmdlets/CimInvokeCimMethod.cs +++ b/src/Microsoft.Management.Infrastructure.CimCmdlets/CimInvokeCimMethod.cs @@ -1,7 +1,5 @@ -/*============================================================================ - * Copyright (C) Microsoft Corporation, All rights reserved. - *============================================================================ - */ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. #region Using directives @@ -10,7 +8,6 @@ using System.Collections.Generic; using System.Diagnostics; using System.Globalization; -using System.Management.Automation; #endregion @@ -30,9 +27,7 @@ internal sealed class CimInvokeCimMethod : CimAsyncOperation internal class CimInvokeCimMethodContext : XOperationContextBase { /// - /// - /// Constructor - /// + /// Initializes a new instance of the class. /// /// /// @@ -43,40 +38,24 @@ internal CimInvokeCimMethodContext(string theNamespace, CimSessionProxy theProxy) { this.proxy = theProxy; - this.methodName = theMethodName; - this.collection = theCollection; + this.MethodName = theMethodName; + this.ParametersCollection = theCollection; this.nameSpace = theNamespace; } /// /// namespace /// - internal string MethodName - { - get - { - return this.methodName; - } - } - private string methodName; + internal string MethodName { get; } /// /// parameters collection /// - internal CimMethodParametersCollection ParametersCollection - { - get - { - return this.collection; - } - } - private CimMethodParametersCollection collection; + internal CimMethodParametersCollection ParametersCollection { get; } } /// - /// - /// Constructor - /// + /// Initializes a new instance of the class. /// public CimInvokeCimMethod() : base() @@ -88,12 +67,12 @@ public CimInvokeCimMethod() /// Base on parametersetName to retrieve ciminstances /// /// - /// object + /// object. public void InvokeCimMethod(InvokeCimMethodCommand cmdlet) { IEnumerable computerNames = ConstValue.GetComputerNames(cmdlet.ComputerName); string nameSpace; - List proxys = new List(); + List proxys = new(); string action = string.Format(CultureInfo.CurrentUICulture, actionTemplate, cmdlet.MethodName); switch (cmdlet.ParameterSetName) @@ -103,6 +82,7 @@ public void InvokeCimMethod(InvokeCimMethodCommand cmdlet) { proxys.Add(CreateSessionProxy(computerName, cmdlet.CimInstance, cmdlet)); } + break; case CimBaseCommand.ClassNameComputerSet: case CimBaseCommand.CimClassComputerSet: @@ -112,6 +92,7 @@ public void InvokeCimMethod(InvokeCimMethodCommand cmdlet) { proxys.Add(CreateSessionProxy(computerName, cmdlet)); } + break; case CimBaseCommand.ClassNameSessionSet: case CimBaseCommand.CimClassSessionSet: @@ -123,10 +104,12 @@ public void InvokeCimMethod(InvokeCimMethodCommand cmdlet) CimSessionProxy proxy = CreateSessionProxy(session, cmdlet); proxys.Add(proxy); } + break; default: break; } + CimMethodParametersCollection paramsCollection = CreateParametersCollection(cmdlet.Arguments, cmdlet.CimClass, cmdlet.CimInstance, cmdlet.MethodName); @@ -139,7 +122,7 @@ public void InvokeCimMethod(InvokeCimMethodCommand cmdlet) case CimBaseCommand.ResourceUriComputerSet: { string target = string.Format(CultureInfo.CurrentUICulture, targetClass, cmdlet.ClassName); - if(cmdlet.ResourceUri != null ) + if (cmdlet.ResourceUri != null) { nameSpace = cmdlet.Namespace; } @@ -147,12 +130,14 @@ public void InvokeCimMethod(InvokeCimMethodCommand cmdlet) { nameSpace = ConstValue.GetNamespace(cmdlet.Namespace); } + foreach (CimSessionProxy proxy in proxys) { if (!cmdlet.ShouldProcess(target, action)) { return; } + proxy.InvokeMethodAsync( nameSpace, cmdlet.ClassName, @@ -160,6 +145,7 @@ public void InvokeCimMethod(InvokeCimMethodCommand cmdlet) paramsCollection); } } + break; case CimBaseCommand.CimClassComputerSet: case CimBaseCommand.CimClassSessionSet: @@ -172,6 +158,7 @@ public void InvokeCimMethod(InvokeCimMethodCommand cmdlet) { return; } + proxy.InvokeMethodAsync( nameSpace, cmdlet.CimClass.CimSystemProperties.ClassName, @@ -179,6 +166,7 @@ public void InvokeCimMethod(InvokeCimMethodCommand cmdlet) paramsCollection); } } + break; case CimBaseCommand.QueryComputerSet: case CimBaseCommand.QuerySessionSet: @@ -186,7 +174,7 @@ public void InvokeCimMethod(InvokeCimMethodCommand cmdlet) foreach (CimSessionProxy proxy in proxys) { // create context object - CimInvokeCimMethodContext context = new CimInvokeCimMethodContext( + CimInvokeCimMethodContext context = new( nameSpace, cmdlet.MethodName, paramsCollection, @@ -195,12 +183,13 @@ public void InvokeCimMethod(InvokeCimMethodCommand cmdlet) // firstly query instance and then invoke method upon returned instances proxy.QueryInstancesAsync(nameSpace, ConstValue.GetQueryDialectWithDefault(cmdlet.QueryDialect), cmdlet.Query); } + break; case CimBaseCommand.CimInstanceComputerSet: case CimBaseCommand.CimInstanceSessionSet: { string target = cmdlet.CimInstance.ToString(); - if(cmdlet.ResourceUri != null ) + if (cmdlet.ResourceUri != null) { nameSpace = cmdlet.Namespace; } @@ -208,12 +197,14 @@ public void InvokeCimMethod(InvokeCimMethodCommand cmdlet) { nameSpace = ConstValue.GetNamespace(cmdlet.CimInstance.CimSystemProperties.Namespace); } + foreach (CimSessionProxy proxy in proxys) { if (!cmdlet.ShouldProcess(target, action)) { return; } + proxy.InvokeMethodAsync( nameSpace, cmdlet.CimInstance, @@ -221,6 +212,7 @@ public void InvokeCimMethod(InvokeCimMethodCommand cmdlet) paramsCollection); } } + break; default: break; @@ -262,12 +254,12 @@ public void InvokeCimMethodOnCimInstance(CimInstance cimInstance, XOperationCont /// /// /// - private void SetSessionProxyProperties( + private static void SetSessionProxyProperties( ref CimSessionProxy proxy, InvokeCimMethodCommand cmdlet) { proxy.OperationTimeout = cmdlet.OperationTimeoutSec; - if(cmdlet.ResourceUri != null ) + if (cmdlet.ResourceUri != null) { proxy.ResourceUri = cmdlet.ResourceUri; } @@ -310,7 +302,7 @@ private CimSessionProxy CreateSessionProxy( } /// - /// Create and set properties + /// Create and set properties. /// /// /// @@ -335,8 +327,8 @@ private CimSessionProxy CreateSessionProxy( /// /// /// - /// See CimProperty.Create - /// CimProperty.Create + /// See CimProperty.Create. + /// CimProperty.Create. private CimMethodParametersCollection CreateParametersCollection( IDictionary parameters, CimClass cimClass, @@ -361,7 +353,7 @@ private CimMethodParametersCollection CreateParametersCollection( { string parameterName = enumerator.Key.ToString(); - CimFlags parameterFlags = CimFlags.In; + const CimFlags parameterFlags = CimFlags.In; object parameterValue = GetBaseObject(enumerator.Value); DebugHelper.WriteLog(@"Create parameter name= {0}, value= {1}, flags= {2}.", 4, @@ -378,8 +370,8 @@ private CimMethodParametersCollection CreateParametersCollection( declaration = cimClass.CimClassMethods[methodName]; if (declaration == null) { - throw new ArgumentException(String.Format( - CultureInfo.CurrentUICulture, Strings.InvalidMethod, methodName, className)); + throw new ArgumentException(string.Format( + CultureInfo.CurrentUICulture, CimCmdletStrings.InvalidMethod, methodName, className)); } } else if (cimInstance != null) @@ -393,9 +385,10 @@ private CimMethodParametersCollection CreateParametersCollection( CimMethodParameterDeclaration paramDeclaration = declaration.Parameters[parameterName]; if (paramDeclaration == null) { - throw new ArgumentException(String.Format( - CultureInfo.CurrentUICulture, Strings.InvalidMethodParameter, parameterName, methodName, className)); + throw new ArgumentException(string.Format( + CultureInfo.CurrentUICulture, CimCmdletStrings.InvalidMethodParameter, parameterName, methodName, className)); } + parameter = CimMethodParameter.Create( parameterName, parameterValue, @@ -436,23 +429,25 @@ private CimMethodParametersCollection CreateParametersCollection( } } } + if (parameter != null) collection.Add(parameter); } + return collection; } #endregion #region const strings /// - /// operation target + /// Operation target. /// private const string targetClass = @"{0}"; /// - /// action + /// Action. /// private const string actionTemplate = @"Invoke-CimMethod: {0}"; #endregion - }//End Class -}//End namespace + } +} diff --git a/src/Microsoft.Management.Infrastructure.CimCmdlets/CimNewCimInstance.cs b/src/Microsoft.Management.Infrastructure.CimCmdlets/CimNewCimInstance.cs index c56e604d903..beb3e551d33 100644 --- a/src/Microsoft.Management.Infrastructure.CimCmdlets/CimNewCimInstance.cs +++ b/src/Microsoft.Management.Infrastructure.CimCmdlets/CimNewCimInstance.cs @@ -1,7 +1,5 @@ -/*============================================================================ - * Copyright (C) Microsoft Corporation, All rights reserved. - *============================================================================ - */ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. #region Using directives @@ -22,9 +20,7 @@ namespace Microsoft.Management.Infrastructure.CimCmdlets internal class CimNewCimInstanceContext : XOperationContextBase { /// - /// - /// Constructor - /// + /// Initializes a new instance of the class. /// /// /// @@ -46,6 +42,7 @@ internal CimNewCimInstanceContext( internal sealed class CimNewCimInstance : CimAsyncOperation { /// + /// Initializes a new instance of the class. /// /// Constructor /// @@ -61,7 +58,7 @@ public CimNewCimInstance() /// either remotely or locally /// /// - /// object + /// object. public void NewCimInstance(NewCimInstanceCommand cmdlet) { DebugHelper.WriteLogEx(); @@ -81,20 +78,20 @@ public void NewCimInstance(NewCimInstanceCommand cmdlet) cmdlet.Key, cmdlet.Property, cmdlet); - } + break; case CimBaseCommand.ResourceUriSessionSet: case CimBaseCommand.ResourceUriComputerSet: { - nameSpace = cmdlet.Namespace; //passing null is ok for resourceUri set + nameSpace = cmdlet.Namespace; // passing null is ok for resourceUri set cimInstance = CreateCimInstance("DummyClass", nameSpace, cmdlet.Key, cmdlet.Property, cmdlet); - } + break; case CimBaseCommand.CimClassComputerSet: case CimBaseCommand.CimClassSessionSet: @@ -103,8 +100,8 @@ public void NewCimInstance(NewCimInstanceCommand cmdlet) cimInstance = CreateCimInstance(cmdlet.CimClass, cmdlet.Property, cmdlet); - } + break; default: return; @@ -135,7 +132,7 @@ public void NewCimInstance(NewCimInstanceCommand cmdlet) } // create ciminstance on server - List proxys = new List(); + List proxys = new(); switch (cmdlet.ParameterSetName) { @@ -150,6 +147,7 @@ public void NewCimInstance(NewCimInstanceCommand cmdlet) proxys.Add(CreateSessionProxy(computerName, cmdlet)); } } + break; case CimBaseCommand.CimClassSessionSet: case CimBaseCommand.ClassNameSessionSet: @@ -158,6 +156,7 @@ public void NewCimInstance(NewCimInstanceCommand cmdlet) { proxys.Add(CreateSessionProxy(session, cmdlet)); } + break; } @@ -180,15 +179,14 @@ internal void GetCimInstance(CimInstance cimInstance, XOperationContextBase cont { DebugHelper.WriteLogEx(); - CimNewCimInstanceContext newCimInstanceContext = context as CimNewCimInstanceContext; - if (newCimInstanceContext == null) + if (context is not CimNewCimInstanceContext newCimInstanceContext) { DebugHelper.WriteLog("Invalid (null) CimNewCimInstanceContext", 1); return; } CimSessionProxy proxy = CreateCimSessionProxy(newCimInstanceContext.Proxy); - string nameSpace = (cimInstance.CimSystemProperties.Namespace == null) ? newCimInstanceContext.Namespace : cimInstance.CimSystemProperties.Namespace; + string nameSpace = cimInstance.CimSystemProperties.Namespace ?? newCimInstanceContext.Namespace; proxy.GetInstanceAsync(nameSpace, cimInstance); } @@ -203,12 +201,12 @@ internal void GetCimInstance(CimInstance cimInstance, XOperationContextBase cont /// /// /// - private void SetSessionProxyProperties( + private static void SetSessionProxyProperties( ref CimSessionProxy proxy, NewCimInstanceCommand cmdlet) { proxy.OperationTimeout = cmdlet.OperationTimeoutSec; - if(cmdlet.ResourceUri != null) + if (cmdlet.ResourceUri != null) { proxy.ResourceUri = cmdlet.ResourceUri; } @@ -233,7 +231,7 @@ private CimSessionProxy CreateSessionProxy( } /// - /// Create and set properties + /// Create and set properties. /// /// /// @@ -258,8 +256,8 @@ private CimSessionProxy CreateSessionProxy( /// /// /// - /// See CimProperty.Create - /// CimProperty.Create + /// See CimProperty.Create. + /// CimProperty.Create. private CimInstance CreateCimInstance( string className, string cimNamespace, @@ -267,13 +265,13 @@ private CimInstance CreateCimInstance( IDictionary properties, NewCimInstanceCommand cmdlet) { - CimInstance cimInstance = new CimInstance(className,cimNamespace); + CimInstance cimInstance = new(className, cimNamespace); if (properties == null) { return cimInstance; } - List keys = new List(); + List keys = new(); if (key != null) { foreach (string keyName in key) @@ -291,12 +289,12 @@ private CimInstance CreateCimInstance( { flag = CimFlags.Key; } + object propertyValue = GetBaseObject(enumerator.Value); DebugHelper.WriteLog("Create and add new property to ciminstance: name = {0}; value = {1}; flags = {2}", 5, propertyName, propertyValue, flag); - PSReference cimReference = propertyValue as PSReference; - if( cimReference != null ) + if (propertyValue is PSReference cimReference) { CimProperty newProperty = CimProperty.Create(propertyName, GetBaseObject(cimReference.Value), CimType.Reference, flag); cimInstance.CimInstanceProperties.Add(newProperty); @@ -309,8 +307,8 @@ private CimInstance CreateCimInstance( flag); cimInstance.CimInstanceProperties.Add(newProperty); } - } + return cimInstance; } @@ -323,19 +321,20 @@ private CimInstance CreateCimInstance( /// /// /// - /// See CimProperty.Create - /// CimProperty.Create + /// See CimProperty.Create. + /// CimProperty.Create. private CimInstance CreateCimInstance( CimClass cimClass, IDictionary properties, NewCimInstanceCommand cmdlet) { - CimInstance cimInstance = new CimInstance(cimClass); + CimInstance cimInstance = new(cimClass); if (properties == null) { return cimInstance; } - List notfoundProperties = new List(); + + List notfoundProperties = new(); foreach (string property in properties.Keys) { if (cimInstance.CimInstanceProperties[property] == null) @@ -344,9 +343,11 @@ private CimInstance CreateCimInstance( cmdlet.ThrowInvalidProperty(notfoundProperties, cmdlet.CimClass.CimSystemProperties.ClassName, @"Property", action, properties); return null; } + object propertyValue = GetBaseObject(properties[property]); cimInstance.CimInstanceProperties[property].Value = propertyValue; } + return cimInstance; } @@ -354,9 +355,9 @@ private CimInstance CreateCimInstance( #region const strings /// - /// action + /// Action. /// private const string action = @"New-CimInstance"; #endregion - }//End Class -}//End namespace + } +} diff --git a/src/Microsoft.Management.Infrastructure.CimCmdlets/CimPromptUser.cs b/src/Microsoft.Management.Infrastructure.CimCmdlets/CimPromptUser.cs index 709e0690a11..48b887f00b4 100644 --- a/src/Microsoft.Management.Infrastructure.CimCmdlets/CimPromptUser.cs +++ b/src/Microsoft.Management.Infrastructure.CimCmdlets/CimPromptUser.cs @@ -1,7 +1,5 @@ -/*============================================================================ - * Copyright (C) Microsoft Corporation, All rights reserved. - *============================================================================ - */ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. #region Using directives using Microsoft.Management.Infrastructure.Options; @@ -24,12 +22,12 @@ namespace Microsoft.Management.Infrastructure.CimCmdlets internal sealed class CimPromptUser : CimSyncAction { /// - /// Constructor + /// Initializes a new instance of the class. /// public CimPromptUser(string message, CimPromptType prompt) { - this.message = message; + this.Message = message; this.prompt = prompt; } @@ -56,7 +54,7 @@ public override void Execute(CmdletOperationBase cmdlet) // NOTES: prepare the whatif message and caption try { - result = cmdlet.ShouldContinue(message, "caption", ref yestoall, ref notoall); + result = cmdlet.ShouldContinue(Message, "caption", ref yestoall, ref notoall); if (yestoall) { this.responseType = CimResponseType.YesToAll; @@ -84,11 +82,12 @@ public override void Execute(CmdletOperationBase cmdlet) // unblocking the waiting thread this.OnComplete(); } + break; case CimPromptType.Normal: try { - result = cmdlet.ShouldProcess(message); + result = cmdlet.ShouldProcess(Message); if (result) { this.responseType = CimResponseType.Yes; @@ -108,32 +107,27 @@ public override void Execute(CmdletOperationBase cmdlet) // unblocking the waiting thread this.OnComplete(); } + break; default: break; } + this.OnComplete(); } #region members /// - /// prompt message + /// Prompt message. /// - public string Message - { - get - { - return message; - } - } - private string message; + public string Message { get; } /// - /// prompt type -Normal or Critical + /// Prompt type -Normal or Critical. /// - private CimPromptType prompt; + private readonly CimPromptType prompt; #endregion - }//End Class -}//End namespace + } +} diff --git a/src/Microsoft.Management.Infrastructure.CimCmdlets/CimRegisterCimIndication.cs b/src/Microsoft.Management.Infrastructure.CimCmdlets/CimRegisterCimIndication.cs index 81e3cf2adb0..692a5f123e8 100644 --- a/src/Microsoft.Management.Infrastructure.CimCmdlets/CimRegisterCimIndication.cs +++ b/src/Microsoft.Management.Infrastructure.CimCmdlets/CimRegisterCimIndication.cs @@ -1,7 +1,5 @@ -/*============================================================================ - * Copyright (C) Microsoft Corporation, All rights reserved. - *============================================================================ - */ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. #region Using directives @@ -14,7 +12,6 @@ namespace Microsoft.Management.Infrastructure.CimCmdlets { - /// /// /// Subscription result event args @@ -27,14 +24,15 @@ internal abstract class CimSubscriptionEventArgs : EventArgs /// Returns an Object value for an operation context /// /// - public Object Context + public object Context { get { return context; } } - protected Object context; + + protected object context; } /// @@ -49,24 +47,17 @@ internal class CimSubscriptionResultEventArgs : CimSubscriptionEventArgs /// subscription result /// /// - public CimSubscriptionResult Result - { - get - { - return result; - } - } - private CimSubscriptionResult result; + public CimSubscriptionResult Result { get; } /// - /// Constructor + /// Initializes a new instance of the class. /// /// public CimSubscriptionResultEventArgs( CimSubscriptionResult theResult) { this.context = null; - this.result = theResult; + this.Result = theResult; } } @@ -82,24 +73,17 @@ internal class CimSubscriptionExceptionEventArgs : CimSubscriptionEventArgs /// subscription result /// /// - public Exception Exception - { - get - { - return exception; - } - } - private Exception exception; + public Exception Exception { get; } /// - /// Constructor + /// Initializes a new instance of the class. /// /// public CimSubscriptionExceptionEventArgs( Exception theException) { this.context = null; - this.exception = theException; + this.Exception = theException; } } @@ -118,9 +102,7 @@ internal sealed class CimRegisterCimIndication : CimAsyncOperation public event EventHandler OnNewSubscriptionResult; /// - /// - /// Constructor - /// + /// Initializes a new instance of the class. /// public CimRegisterCimIndication() : base() @@ -131,7 +113,7 @@ public CimRegisterCimIndication() /// /// Start an indication subscription target to the given computer. /// - /// null stands for localhost + /// Null stands for localhost. /// /// /// @@ -141,7 +123,7 @@ public void RegisterCimIndication( string nameSpace, string queryDialect, string queryExpression, - UInt32 operationTimeout) + uint operationTimeout) { DebugHelper.WriteLogEx("queryDialect = '{0}'; queryExpression = '{1}'", 0, queryDialect, queryExpression); this.TargetComputerName = computerName; @@ -153,24 +135,23 @@ public void RegisterCimIndication( /// /// Start an indication subscription through a given . /// - /// Cannot be null + /// Cannot be null. /// /// /// /// - /// throw if cimSession is null + /// Throw if cimSession is null. public void RegisterCimIndication( CimSession cimSession, string nameSpace, string queryDialect, string queryExpression, - UInt32 operationTimeout) + uint operationTimeout) { DebugHelper.WriteLogEx("queryDialect = '{0}'; queryExpression = '{1}'", 0, queryDialect, queryExpression); - if (cimSession == null) - { - throw new ArgumentNullException(String.Format(CultureInfo.CurrentUICulture, Strings.NullArgument, @"cimSession")); - } + + ArgumentNullException.ThrowIfNull(cimSession, string.Format(CultureInfo.CurrentUICulture, CimCmdletStrings.NullArgument, nameof(cimSession))); + this.TargetComputerName = cimSession.ComputerName; CimSessionProxy proxy = CreateSessionProxy(cimSession, operationTimeout); proxy.SubscribeAsync(nameSpace, queryDialect, queryExpression); @@ -204,7 +185,7 @@ protected override void SubscribeToCimSessionProxyEvent(CimSessionProxy proxy) /// /// object raised the event /// - /// event argument + /// Event argument. private void CimIndicationHandler(object cimSession, CmdletActionEventArgs actionArgs) { DebugHelper.WriteLogEx("action is {0}. Disposed {1}", 0, actionArgs.Action, this.Disposed); @@ -215,10 +196,9 @@ private void CimIndicationHandler(object cimSession, CmdletActionEventArgs actio } // NOTES: should move after this.Disposed, but need to log the exception - CimWriteError cimWriteError = actionArgs.Action as CimWriteError; - if (cimWriteError != null) + if (actionArgs.Action is CimWriteError cimWriteError) { - this.exception = cimWriteError.Exception; + this.Exception = cimWriteError.Exception; if (!this.ackedEvent.IsSet) { // an exception happened @@ -226,21 +206,21 @@ private void CimIndicationHandler(object cimSession, CmdletActionEventArgs actio this.ackedEvent.Set(); return; } + EventHandler temp = this.OnNewSubscriptionResult; if (temp != null) { DebugHelper.WriteLog("Raise an exception event", 2); - temp(this, new CimSubscriptionExceptionEventArgs(this.exception)); + temp(this, new CimSubscriptionExceptionEventArgs(this.Exception)); } - DebugHelper.WriteLog("Got an exception: {0}", 2, exception); + + DebugHelper.WriteLog("Got an exception: {0}", 2, Exception); } - CimWriteResultObject cimWriteResultObject = actionArgs.Action as CimWriteResultObject; - if (cimWriteResultObject != null) + if (actionArgs.Action is CimWriteResultObject cimWriteResultObject) { - CimSubscriptionResult result = cimWriteResultObject.Result as CimSubscriptionResult; - if (result != null) + if (cimWriteResultObject.Result is CimSubscriptionResult result) { EventHandler temp = this.OnNewSubscriptionResult; if (temp != null) @@ -267,13 +247,13 @@ private void CimIndicationHandler(object cimSession, CmdletActionEventArgs actio } /// - /// block the ps thread until ACK message or Error happened. + /// Block the ps thread until ACK message or Error happened. /// private void WaitForAckMessage() { DebugHelper.WriteLogEx(); this.ackedEvent.Wait(); - if (this.exception != null) + if (this.Exception != null) { DebugHelper.WriteLogEx("error happened", 0); if (this.Cmdlet != null) @@ -282,16 +262,17 @@ private void WaitForAckMessage() // throw terminating error ErrorRecord errorRecord = ErrorToErrorRecord.ErrorRecordFromAnyException( - new InvocationContext(this.TargetComputerName, null), this.exception, null); + new InvocationContext(this.TargetComputerName, null), this.Exception, null); this.Cmdlet.ThrowTerminatingError(errorRecord); } else { DebugHelper.WriteLogEx("Throw exception", 1); // throw exception out - throw this.exception; + throw this.Exception; } } + DebugHelper.WriteLogEx("ACK happened", 0); } #endregion @@ -300,22 +281,22 @@ private void WaitForAckMessage() /// /// The cmdlet object who issue this subscription, /// to throw ThrowTerminatingError - /// in case there is a subscription failure + /// in case there is a subscription failure. /// /// internal Cmdlet Cmdlet { - set; get; + set; } /// - /// target computername + /// Target computername. /// - internal String TargetComputerName + internal string TargetComputerName { - set; get; + set; } #endregion @@ -331,7 +312,7 @@ internal String TargetComputerName /// private CimSessionProxy CreateSessionProxy( string computerName, - UInt32 timeout) + uint timeout) { CimSessionProxy proxy = CreateCimSessionProxy(computerName); proxy.OperationTimeout = timeout; @@ -339,14 +320,14 @@ private CimSessionProxy CreateSessionProxy( } /// - /// Create and set properties + /// Create and set properties. /// /// /// /// private CimSessionProxy CreateSessionProxy( CimSession session, - UInt32 timeout) + uint timeout) { CimSessionProxy proxy = CreateCimSessionProxy(session); proxy.OperationTimeout = timeout; @@ -357,18 +338,11 @@ private CimSessionProxy CreateSessionProxy( #region private members /// - /// Exception occurred while start the subscription + /// Exception occurred while start the subscription. /// - internal Exception Exception - { - get - { - return exception; - } - } - private Exception exception; + internal Exception Exception { get; private set; } #endregion - }//End Class -}//End namespace + } +} diff --git a/src/Microsoft.Management.Infrastructure.CimCmdlets/CimRemoveCimInstance.cs b/src/Microsoft.Management.Infrastructure.CimCmdlets/CimRemoveCimInstance.cs index 726136fa334..a6182166170 100644 --- a/src/Microsoft.Management.Infrastructure.CimCmdlets/CimRemoveCimInstance.cs +++ b/src/Microsoft.Management.Infrastructure.CimCmdlets/CimRemoveCimInstance.cs @@ -1,12 +1,8 @@ -/*============================================================================ - * Copyright (C) Microsoft Corporation, All rights reserved. - *============================================================================ - */ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. #region Using directives -using System.Collections; -using System; using System.Collections.Generic; using System.Diagnostics; @@ -21,9 +17,7 @@ namespace Microsoft.Management.Infrastructure.CimCmdlets internal class CimRemoveCimInstanceContext : XOperationContextBase { /// - /// - /// Constructor - /// + /// Initializes a new instance of the class. /// /// /// @@ -43,9 +37,7 @@ internal CimRemoveCimInstanceContext(string theNamespace, internal sealed class CimRemoveCimInstance : CimGetInstance { /// - /// - /// Constructor - /// + /// Initializes a new instance of the class. /// public CimRemoveCimInstance() : base() @@ -57,14 +49,14 @@ public CimRemoveCimInstance() /// Base on parametersetName to retrieve ciminstances /// /// - /// object + /// object. public void RemoveCimInstance(RemoveCimInstanceCommand cmdlet) { DebugHelper.WriteLogEx(); IEnumerable computerNames = ConstValue.GetComputerNames( GetComputerName(cmdlet)); - List proxys = new List(); + List proxys = new(); switch (cmdlet.ParameterSetName) { case CimBaseCommand.CimInstanceComputerSet: @@ -72,22 +64,25 @@ public void RemoveCimInstance(RemoveCimInstanceCommand cmdlet) { proxys.Add(CreateSessionProxy(computerName, cmdlet.CimInstance, cmdlet)); } + break; case CimBaseCommand.CimInstanceSessionSet: foreach (CimSession session in GetCimSession(cmdlet)) { proxys.Add(CreateSessionProxy(session, cmdlet)); } + break; default: break; } + switch (cmdlet.ParameterSetName) { case CimBaseCommand.CimInstanceComputerSet: case CimBaseCommand.CimInstanceSessionSet: string nameSpace = null; - if(cmdlet.ResourceUri != null ) + if (cmdlet.ResourceUri != null) { nameSpace = GetCimInstanceParameter(cmdlet).CimSystemProperties.Namespace; } @@ -95,6 +90,7 @@ public void RemoveCimInstance(RemoveCimInstanceCommand cmdlet) { nameSpace = ConstValue.GetNamespace(GetCimInstanceParameter(cmdlet).CimSystemProperties.Namespace); } + string target = cmdlet.CimInstance.ToString(); foreach (CimSessionProxy proxy in proxys) { @@ -102,8 +98,10 @@ public void RemoveCimInstance(RemoveCimInstanceCommand cmdlet) { return; } + proxy.DeleteInstanceAsync(nameSpace, cmdlet.CimInstance); } + break; case CimBaseCommand.QueryComputerSet: case CimBaseCommand.QuerySessionSet: @@ -139,9 +137,9 @@ internal void RemoveCimInstance(CimInstance cimInstance, XOperationContextBase c #region const strings /// - /// action + /// Action. /// private const string action = @"Remove-CimInstance"; #endregion - }//End Class -}//End namespace + } +} diff --git a/src/Microsoft.Management.Infrastructure.CimCmdlets/CimResultObserver.cs b/src/Microsoft.Management.Infrastructure.CimCmdlets/CimResultObserver.cs index 0ea01b2a64a..389c45c8314 100644 --- a/src/Microsoft.Management.Infrastructure.CimCmdlets/CimResultObserver.cs +++ b/src/Microsoft.Management.Infrastructure.CimCmdlets/CimResultObserver.cs @@ -1,13 +1,11 @@ -/*============================================================================ - * Copyright (C) Microsoft Corporation, All rights reserved. - *============================================================================ - */ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. #region Using directives using System; -using System.Management.Automation; using System.Globalization; +using System.Management.Automation; #endregion @@ -29,30 +27,23 @@ public enum AsyncResultType #region CimResultContext /// - /// Cim Result Context + /// Cim Result Context. /// internal class CimResultContext { /// - /// constructor + /// Initializes a new instance of the class. /// /// internal CimResultContext(object ErrorSource) { - this.errorSource = ErrorSource; + this.ErrorSource = ErrorSource; } /// - /// ErrorSource property + /// ErrorSource property. /// - internal object ErrorSource - { - get - { - return this.errorSource; - } - } - private object errorSource; + internal object ErrorSource { get; } } #endregion @@ -65,12 +56,12 @@ internal object ErrorSource internal abstract class AsyncResultEventArgsBase : EventArgs { /// - /// Constructor + /// Initializes a new instance of the class. /// /// /// /// - public AsyncResultEventArgsBase( + protected AsyncResultEventArgsBase( CimSession session, IObservable observable, AsyncResultType resultType) @@ -81,13 +72,13 @@ public AsyncResultEventArgsBase( } /// - /// Constructor + /// Initializes a new instance of the class. /// /// /// /// /// - public AsyncResultEventArgsBase( + protected AsyncResultEventArgsBase( CimSession session, IObservable observable, AsyncResultType resultType, @@ -118,16 +109,14 @@ public AsyncResultEventArgsBase( internal class AsyncResultCompleteEventArgs : AsyncResultEventArgsBase { /// - /// - /// Constructor - /// + /// Initializes a new instance of the class. /// - /// object + /// object. /// public AsyncResultCompleteEventArgs( CimSession session, - IObservable observable) : - base(session, observable, AsyncResultType.Completion) + IObservable observable) + : base(session, observable, AsyncResultType.Completion) { } } @@ -140,7 +129,7 @@ public AsyncResultCompleteEventArgs( internal class AsyncResultObjectEventArgs : AsyncResultEventArgsBase { /// - /// Constructor + /// Initializes a new instance of the class. /// /// /// @@ -148,8 +137,8 @@ internal class AsyncResultObjectEventArgs : AsyncResultEventArgsBase public AsyncResultObjectEventArgs( CimSession session, IObservable observable, - object resultObject) : - base(session, observable, AsyncResultType.Result) + object resultObject) + : base(session, observable, AsyncResultType.Result) { this.resultObject = resultObject; } @@ -165,7 +154,7 @@ public AsyncResultObjectEventArgs( internal class AsyncResultErrorEventArgs : AsyncResultEventArgsBase { /// - /// Constructor + /// Initializes a new instance of the class. /// /// /// @@ -173,14 +162,14 @@ internal class AsyncResultErrorEventArgs : AsyncResultEventArgsBase public AsyncResultErrorEventArgs( CimSession session, IObservable observable, - Exception error) : - base(session, observable, AsyncResultType.Exception) + Exception error) + : base(session, observable, AsyncResultType.Exception) { this.error = error; } /// - /// Constructor + /// Initializes a new instance of the class. /// /// /// @@ -190,8 +179,8 @@ public AsyncResultErrorEventArgs( CimSession session, IObservable observable, Exception error, - CimResultContext cimResultContext) : - base(session, observable, AsyncResultType.Exception, cimResultContext) + CimResultContext cimResultContext) + : base(session, observable, AsyncResultType.Exception, cimResultContext) { this.error = error; } @@ -207,7 +196,7 @@ public AsyncResultErrorEventArgs( /// EnumerateInstancesAsync operation of object. /// /// - /// (See http://channel9.msdn.com/posts/J.Van.Gogh/Reactive-Extensions-API-in-depth-Contract/) + /// (See https://channel9.msdn.com/posts/J.Van.Gogh/Reactive-Extensions-API-in-depth-Contract/) /// for the IObserver/IObservable contact /// - the only possible sequence is OnNext* (OnCompleted|OnError)? /// - callbacks are serialized @@ -218,41 +207,31 @@ public AsyncResultErrorEventArgs( internal class CimResultObserver : IObserver { /// - /// Define delegate that handles new cmdlet action come from - /// the operations related to the current CimSession object. - /// - /// cimSession object, which raised the event - /// Event args - public delegate void ResultEventHandler( - object observer, - AsyncResultEventArgsBase resultArgs); - - /// - /// Define an Event based on the NewActionHandler + /// Define an Event based on the NewActionHandler. /// - public event ResultEventHandler OnNewResult; + public event EventHandler OnNewResult; /// - /// Constructor + /// Initializes a new instance of the class. /// - /// object that issued the operation - /// Operation that can be observed + /// object that issued the operation. + /// Operation that can be observed. public CimResultObserver(CimSession session, IObservable observable) { - this.session = session; + this.CurrentSession = session; this.observable = observable; } /// - /// Constructor + /// Initializes a new instance of the class. /// - /// object that issued the operation - /// Operation that can be observed + /// object that issued the operation. + /// Operation that can be observed. public CimResultObserver(CimSession session, IObservable observable, CimResultContext cimResultContext) { - this.session = session; + this.CurrentSession = session; this.observable = observable; this.context = cimResultContext; } @@ -270,8 +249,8 @@ public virtual void OnCompleted() // OnNext, OnError try { - AsyncResultCompleteEventArgs completeArgs = new AsyncResultCompleteEventArgs( - this.session, this.observable); + AsyncResultCompleteEventArgs completeArgs = new( + this.CurrentSession, this.observable); this.OnNewResult(this, completeArgs); } catch (Exception ex) @@ -286,13 +265,13 @@ public virtual void OnCompleted() /// Operation completed with an error /// /// - /// error object + /// Error object. public virtual void OnError(Exception error) { try { - AsyncResultErrorEventArgs errorArgs = new AsyncResultErrorEventArgs( - this.session, this.observable, error, this.context); + AsyncResultErrorEventArgs errorArgs = new( + this.CurrentSession, this.observable, error, this.context); this.OnNewResult(this, errorArgs); } catch (Exception ex) @@ -303,7 +282,7 @@ public virtual void OnError(Exception error) } /// - /// Deliver the result value + /// Deliver the result value. /// /// protected void OnNextCore(object value) @@ -311,8 +290,8 @@ protected void OnNextCore(object value) DebugHelper.WriteLogEx("value = {0}.", 1, value); try { - AsyncResultObjectEventArgs resultArgs = new AsyncResultObjectEventArgs( - this.session, this.observable, value); + AsyncResultObjectEventArgs resultArgs = new( + this.CurrentSession, this.observable, value); this.OnNewResult(this, resultArgs); } catch (Exception ex) @@ -327,7 +306,7 @@ protected void OnNextCore(object value) /// Operation got a new result object /// /// - /// result object + /// Result object. public virtual void OnNext(T value) { DebugHelper.WriteLogEx("value = {0}.", 1, value); @@ -336,42 +315,36 @@ public virtual void OnNext(T value) { return; } + this.OnNextCore(value); } #region members /// - /// Session object of the operation + /// Session object of the operation. /// - protected CimSession CurrentSession - { - get - { - return session; - } - } - private CimSession session; + protected CimSession CurrentSession { get; } /// - /// async operation that can be observed + /// Async operation that can be observed. /// - private IObservable observable; + private readonly IObservable observable; /// /// object used during delivering result. /// - private CimResultContext context; + private readonly CimResultContext context; #endregion } /// - /// CimSubscriptionResultObserver class definition + /// CimSubscriptionResultObserver class definition. /// internal class CimSubscriptionResultObserver : CimResultObserver { /// - /// constructor + /// Initializes a new instance of the class. /// /// /// @@ -381,7 +354,7 @@ public CimSubscriptionResultObserver(CimSession session, IObservable obs } /// - /// constructor + /// Initializes a new instance of the class. /// /// /// @@ -394,7 +367,7 @@ public CimSubscriptionResultObserver( } /// - /// Override the OnNext method + /// Override the OnNext method. /// /// public override void OnNext(CimSubscriptionResult value) @@ -405,12 +378,12 @@ public override void OnNext(CimSubscriptionResult value) } /// - /// CimMethodResultObserver class definition + /// CimMethodResultObserver class definition. /// internal class CimMethodResultObserver : CimResultObserver { /// - /// constructor + /// Initializes a new instance of the class. /// /// /// @@ -420,7 +393,7 @@ public CimMethodResultObserver(CimSession session, IObservable observabl } /// - /// constructor + /// Initializes a new instance of the class. /// /// /// @@ -434,7 +407,7 @@ public CimMethodResultObserver( } /// - /// Override the OnNext method + /// Override the OnNext method. /// /// public override void OnNext(CimMethodResultBase value) @@ -446,8 +419,7 @@ public override void OnNext(CimMethodResultBase value) string resultObjectPSType = null; PSObject resultObject = null; - CimMethodResult methodResult = value as CimMethodResult; - if (methodResult != null) + if (value is CimMethodResult methodResult) { resultObjectPSType = PSTypeCimMethodResult; resultObject = new PSObject(); @@ -458,8 +430,7 @@ public override void OnNext(CimMethodResultBase value) } else { - CimMethodStreamedResult methodStreamedResult = value as CimMethodStreamedResult; - if (methodStreamedResult != null) + if (value is CimMethodStreamedResult methodStreamedResult) { resultObjectPSType = PSTypeCimMethodStreamedResult; resultObject = new PSObject(); @@ -468,28 +439,29 @@ public override void OnNext(CimMethodResultBase value) resultObject.Properties.Add(new PSNoteProperty(@"ItemValue", methodStreamedResult.ItemValue)); } } + if (resultObject != null) { resultObject.Properties.Add(new PSNoteProperty(@"PSComputerName", this.CurrentSession.ComputerName)); resultObject.TypeNames.Insert(0, resultObjectPSType); - resultObject.TypeNames.Insert(0, String.Format(CultureInfo.InvariantCulture, PSTypeCimMethodResultTemplate, resultObjectPSType, ClassName, MethodName)); + resultObject.TypeNames.Insert(0, string.Format(CultureInfo.InvariantCulture, PSTypeCimMethodResultTemplate, resultObjectPSType, ClassName, MethodName)); base.OnNextCore(resultObject); } } /// - /// methodname + /// Methodname. /// - internal String MethodName + internal string MethodName { get; set; } /// - /// classname + /// Classname. /// - internal String ClassName + internal string ClassName { get; set; @@ -497,12 +469,12 @@ internal String ClassName } /// - /// IgnoreResultObserver class definition + /// IgnoreResultObserver class definition. /// internal class IgnoreResultObserver : CimResultObserver { /// - /// constructor + /// Initializes a new instance of the class. /// /// /// @@ -512,7 +484,7 @@ public IgnoreResultObserver(CimSession session, IObservable observable) } /// - /// Override the OnNext method + /// Override the OnNext method. /// /// public override void OnNext(CimInstance value) diff --git a/src/Microsoft.Management.Infrastructure.CimCmdlets/CimSessionOperations.cs b/src/Microsoft.Management.Infrastructure.CimCmdlets/CimSessionOperations.cs index bbf90949882..b8ebc9adaef 100644 --- a/src/Microsoft.Management.Infrastructure.CimCmdlets/CimSessionOperations.cs +++ b/src/Microsoft.Management.Infrastructure.CimCmdlets/CimSessionOperations.cs @@ -1,16 +1,14 @@ -/*============================================================================ - * Copyright (C) Microsoft Corporation, All rights reserved. - *============================================================================ - */ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. #region Using directives using System; using System.Collections.Concurrent; using System.Collections.Generic; +using System.Globalization; using System.Management.Automation; using System.Management.Automation.Runspaces; -using System.Globalization; using Microsoft.Management.Infrastructure.Options; #endregion @@ -24,67 +22,32 @@ internal class CimSessionWrapper #region members /// - /// id of the cimsession + /// Id of the cimsession. /// - public uint SessionId - { - get - { - return this.sessionId; - } - } - private uint sessionId; + public uint SessionId { get; } /// - /// instanceId of the cimsession + /// InstanceId of the cimsession. /// - public Guid InstanceId - { - get - { - return this.instanceId; - } - } - private Guid instanceId; + public Guid InstanceId { get; } /// - /// name of the cimsession + /// Name of the cimsession. /// - public string Name - { - get - { - return this.name; - } - } - private string name; + public string Name { get; } /// - /// computer name of the cimsession + /// Computer name of the cimsession. /// - public string ComputerName - { - get - { - return this.computerName; - } - } - private string computerName; + public string ComputerName { get; } /// - /// wrapped cimsession object + /// Wrapped cimsession object. /// - public CimSession CimSession - { - get - { - return this.cimSession; - } - } - private CimSession cimSession; + public CimSession CimSession { get; } /// - /// computer name of the cimsession + /// Computer name of the cimsession. /// public string Protocol { @@ -101,14 +64,16 @@ public string Protocol } } } + internal ProtocolType GetProtocolType() { return protocol; } - private ProtocolType protocol; + + private readonly ProtocolType protocol; /// - /// PSObject that wrapped the cimSession + /// PSObject that wrapped the cimSession. /// private PSObject psObject; @@ -122,11 +87,11 @@ internal CimSessionWrapper( CimSession theCimSession, ProtocolType theProtocol) { - this.sessionId = theSessionId; - this.instanceId = theInstanceId; - this.name = theName; - this.computerName = theComputerName; - this.cimSession = theCimSession; + this.SessionId = theSessionId; + this.InstanceId = theInstanceId; + this.Name = theName; + this.ComputerName = theComputerName; + this.CimSession = theCimSession; this.psObject = null; this.protocol = theProtocol; } @@ -135,21 +100,22 @@ internal PSObject GetPSObject() { if (psObject == null) { - psObject = new PSObject(this.cimSession); - psObject.Properties.Add(new PSNoteProperty(CimSessionState.idPropName, this.sessionId)); - psObject.Properties.Add(new PSNoteProperty(CimSessionState.namePropName, this.name)); - psObject.Properties.Add(new PSNoteProperty(CimSessionState.instanceidPropName, this.instanceId)); + psObject = new PSObject(this.CimSession); + psObject.Properties.Add(new PSNoteProperty(CimSessionState.idPropName, this.SessionId)); + psObject.Properties.Add(new PSNoteProperty(CimSessionState.namePropName, this.Name)); + psObject.Properties.Add(new PSNoteProperty(CimSessionState.instanceidPropName, this.InstanceId)); psObject.Properties.Add(new PSNoteProperty(CimSessionState.computernamePropName, this.ComputerName)); psObject.Properties.Add(new PSNoteProperty(CimSessionState.protocolPropName, this.Protocol)); } else { psObject.Properties[CimSessionState.idPropName].Value = this.SessionId; - psObject.Properties[CimSessionState.namePropName].Value = this.name; - psObject.Properties[CimSessionState.instanceidPropName].Value = this.instanceId; + psObject.Properties[CimSessionState.namePropName].Value = this.Name; + psObject.Properties[CimSessionState.instanceidPropName].Value = this.InstanceId; psObject.Properties[CimSessionState.computernamePropName].Value = this.ComputerName; psObject.Properties[CimSessionState.protocolPropName].Value = this.Protocol; } + return psObject; } } @@ -174,93 +140,91 @@ internal class CimSessionState : IDisposable /// where is the next available session number. /// For example, CimSession1, CimSession2, etc... /// - internal static string CimSessionClassName = "CimSession"; + internal static readonly string CimSessionClassName = "CimSession"; /// - /// CimSession object name + /// CimSession object name. /// - internal static string CimSessionObject = "{CimSession Object}"; + internal static readonly string CimSessionObject = "{CimSession Object}"; /// /// /// CimSession object path, which is identifying a cimsession object /// /// - internal static string SessionObjectPath = @"CimSession id = {0}, name = {2}, ComputerName = {3}, instance id = {1}"; + internal static readonly string SessionObjectPath = @"CimSession id = {0}, name = {2}, ComputerName = {3}, instance id = {1}"; /// - /// Id property name of cimsession wrapper object + /// Id property name of cimsession wrapper object. /// - internal static string idPropName = "Id"; + internal static readonly string idPropName = "Id"; /// - /// Instanceid property name of cimsession wrapper object + /// Instanceid property name of cimsession wrapper object. /// - internal static string instanceidPropName = "InstanceId"; + internal static readonly string instanceidPropName = "InstanceId"; /// - /// Name property name of cimsession wrapper object + /// Name property name of cimsession wrapper object. /// - internal static string namePropName = "Name"; + internal static readonly string namePropName = "Name"; /// - /// Computer name property name of cimsession object + /// Computer name property name of cimsession object. /// - internal static string computernamePropName = "ComputerName"; + internal static readonly string computernamePropName = "ComputerName"; /// - /// Protocol name property name of cimsession object + /// Protocol name property name of cimsession object. /// - internal static string protocolPropName = "Protocol"; + internal static readonly string protocolPropName = "Protocol"; /// /// - /// session counter bound to current runspace. + /// Session counter bound to current runspace. /// /// - private UInt32 sessionNameCounter; + private uint sessionNameCounter; /// /// /// Dictionary used to holds all CimSessions in current runspace by session name. /// /// - private Dictionary> curCimSessionsByName; + private readonly Dictionary> curCimSessionsByName; /// /// /// Dictionary used to holds all CimSessions in current runspace by computer name. /// /// - private Dictionary> curCimSessionsByComputerName; + private readonly Dictionary> curCimSessionsByComputerName; /// /// /// Dictionary used to holds all CimSessions in current runspace by instance ID. /// /// - private Dictionary curCimSessionsByInstanceId; + private readonly Dictionary curCimSessionsByInstanceId; /// /// /// Dictionary used to holds all CimSessions in current runspace by session id. /// /// - private Dictionary curCimSessionsById; + private readonly Dictionary curCimSessionsById; /// /// /// Dictionary used to link CimSession object with PSObject. /// /// - private Dictionary curCimSessionWrapper; + private readonly Dictionary curCimSessionWrapper; #endregion /// - /// - /// constructor - /// + /// Initializes a new instance of the class. /// internal CimSessionState() { @@ -290,8 +254,8 @@ internal int GetSessionsCount() /// Generates an unique session id. /// /// - /// Unique session id under current runspace - internal UInt32 GenerateSessionId() + /// Unique session id under current runspace. + internal uint GenerateSessionId() { return this.sessionNameCounter++; } @@ -299,7 +263,7 @@ internal UInt32 GenerateSessionId() /// /// - /// Indicates whether this object was disposed or not + /// Indicates whether this object was disposed or not. /// /// private bool _disposed; @@ -333,7 +297,7 @@ public void Dispose() /// other objects. Only unmanaged resources can be disposed. /// /// - /// Whether it is directly called + /// Whether it is directly called. protected virtual void Dispose(bool disposing) { if (!this._disposed) @@ -360,6 +324,7 @@ public void Cleanup() { session.Dispose(); } + curCimSessionWrapper.Clear(); curCimSessionsByName.Clear(); curCimSessionsByComputerName.Clear(); @@ -374,23 +339,25 @@ public void Cleanup() /// /// - /// Add new CimSession object to cache + /// Add new CimSession object to cache. /// /// /// /// /// /// + /// + /// /// internal PSObject AddObjectToCache( CimSession session, - UInt32 sessionId, + uint sessionId, Guid instanceId, - String name, - String computerName, + string name, + string computerName, ProtocolType protocol) { - CimSessionWrapper wrapper = new CimSessionWrapper( + CimSessionWrapper wrapper = new( sessionId, instanceId, name, computerName, session, protocol); HashSet objects; @@ -399,6 +366,7 @@ internal PSObject AddObjectToCache( objects = new HashSet(); this.curCimSessionsByComputerName.Add(computerName, objects); } + objects.Add(wrapper); if (!this.curCimSessionsByName.TryGetValue(name, out objects)) @@ -406,6 +374,7 @@ internal PSObject AddObjectToCache( objects = new HashSet(); this.curCimSessionsByName.Add(name, objects); } + objects.Add(wrapper); this.curCimSessionsByInstanceId.Add(instanceId, wrapper); @@ -422,37 +391,42 @@ internal PSObject AddObjectToCache( /// internal string GetRemoveSessionObjectTarget(PSObject psObject) { - String message = String.Empty; + string message = string.Empty; if (psObject.BaseObject is CimSession) { - UInt32 id = 0x0; + uint id = 0x0; Guid instanceId = Guid.Empty; - String name = String.Empty; - String computerName = string.Empty; - if (psObject.Properties[idPropName].Value is UInt32) + string name = string.Empty; + string computerName = string.Empty; + if (psObject.Properties[idPropName].Value is uint) { id = Convert.ToUInt32(psObject.Properties[idPropName].Value, null); } + if (psObject.Properties[instanceidPropName].Value is Guid) { instanceId = (Guid)psObject.Properties[instanceidPropName].Value; } - if (psObject.Properties[namePropName].Value is String) + + if (psObject.Properties[namePropName].Value is string) { - name = (String)psObject.Properties[namePropName].Value; + name = (string)psObject.Properties[namePropName].Value; } - if (psObject.Properties[computernamePropName].Value is String) + + if (psObject.Properties[computernamePropName].Value is string) { - computerName = (String)psObject.Properties[computernamePropName].Value; + computerName = (string)psObject.Properties[computernamePropName].Value; } - message = String.Format(CultureInfo.CurrentUICulture, SessionObjectPath, id, instanceId, name, computerName); + + message = string.Format(CultureInfo.CurrentUICulture, SessionObjectPath, id, instanceId, name, computerName); } + return message; } /// /// - /// Remove given object from cache + /// Remove given object from cache. /// /// /// @@ -468,7 +442,7 @@ internal void RemoveOneSessionObjectFromCache(PSObject psObject) /// /// - /// Remove given object from cache + /// Remove given object from cache. /// /// /// @@ -480,9 +454,10 @@ internal void RemoveOneSessionObjectFromCache(CimSession session) { return; } + CimSessionWrapper wrapper = this.curCimSessionWrapper[session]; - String name = wrapper.Name; - String computerName = wrapper.ComputerName; + string name = wrapper.Name; + string computerName = wrapper.ComputerName; DebugHelper.WriteLog("name {0}, computername {1}, id {2}, instanceId {3}", 1, name, computerName, wrapper.SessionId, wrapper.InstanceId); @@ -491,10 +466,12 @@ internal void RemoveOneSessionObjectFromCache(CimSession session) { objects.Remove(wrapper); } + if (this.curCimSessionsByName.TryGetValue(name, out objects)) { objects.Remove(wrapper); } + RemoveSessionInternal(session, wrapper); } @@ -527,39 +504,39 @@ private void RemoveSessionInternal(CimSession session, CimSessionWrapper wrapper /// /// /// - private void AddErrorRecord( + private static void AddErrorRecord( ref List errRecords, string propertyName, object propertyValue) { errRecords.Add( new ErrorRecord( - new CimException(String.Format(CultureInfo.CurrentUICulture, Strings.CouldNotFindCimsessionObject, propertyName, propertyValue)), + new CimException(string.Format(CultureInfo.CurrentUICulture, CimCmdletStrings.CouldNotFindCimsessionObject, propertyName, propertyValue)), string.Empty, ErrorCategory.ObjectNotFound, null)); } /// - /// Query session list by given id array + /// Query session list by given id array. /// /// - /// List of session wrapper objects - internal IEnumerable QuerySession(IEnumerable ids, + /// List of session wrapper objects. + internal IEnumerable QuerySession( + IEnumerable ids, out IEnumerable errorRecords) { - HashSet sessions = new HashSet(); - HashSet sessionIds = new HashSet(); - List errRecords = new List(); + HashSet sessions = new(); + HashSet sessionIds = new(); + List errRecords = new(); errorRecords = errRecords; // NOTES: use template function to implement this will save duplicate code - foreach (UInt32 id in ids) + foreach (uint id in ids) { if (this.curCimSessionsById.ContainsKey(id)) { - if (!sessionIds.Contains(id)) + if (sessionIds.Add(id)) { - sessionIds.Add(id); sessions.Add(this.curCimSessionsById[id].GetPSObject()); } } @@ -568,20 +545,22 @@ internal IEnumerable QuerySession(IEnumerable ids, AddErrorRecord(ref errRecords, idPropName, id); } } + return sessions; } /// - /// Query session list by given instance id array + /// Query session list by given instance id array. /// /// - /// List of session wrapper objects - internal IEnumerable QuerySession(IEnumerable instanceIds, + /// List of session wrapper objects. + internal IEnumerable QuerySession( + IEnumerable instanceIds, out IEnumerable errorRecords) { - HashSet sessions = new HashSet(); - HashSet sessionIds = new HashSet(); - List errRecords = new List(); + HashSet sessions = new(); + HashSet sessionIds = new(); + List errRecords = new(); errorRecords = errRecords; foreach (Guid instanceid in instanceIds) { @@ -599,31 +578,32 @@ internal IEnumerable QuerySession(IEnumerable instanceIds, AddErrorRecord(ref errRecords, instanceidPropName, instanceid); } } + return sessions; } /// - /// Query session list by given name array + /// Query session list by given name array. /// /// - /// List of session wrapper objects + /// List of session wrapper objects. internal IEnumerable QuerySession(IEnumerable nameArray, out IEnumerable errorRecords) { - HashSet sessions = new HashSet(); - HashSet sessionIds = new HashSet(); - List errRecords = new List(); + HashSet sessions = new(); + HashSet sessionIds = new(); + List errRecords = new(); errorRecords = errRecords; foreach (string name in nameArray) { bool foundSession = false; - WildcardPattern pattern = new WildcardPattern(name, WildcardOptions.IgnoreCase); - foreach (KeyValuePair> kvp in this.curCimSessionsByName) + WildcardPattern pattern = new(name, WildcardOptions.IgnoreCase); + foreach (KeyValuePair> kvp in this.curCimSessionsByName) { if (pattern.IsMatch(kvp.Key)) { HashSet wrappers = kvp.Value; - foundSession = (wrappers.Count > 0); + foundSession = wrappers.Count > 0; foreach (CimSessionWrapper wrapper in wrappers) { if (!sessionIds.Contains(wrapper.SessionId)) @@ -634,26 +614,28 @@ internal IEnumerable QuerySession(IEnumerable nameArray, } } } + if (!foundSession && !WildcardPattern.ContainsWildcardCharacters(name)) { AddErrorRecord(ref errRecords, namePropName, name); } } + return sessions; } /// - /// Query session list by given computer name array + /// Query session list by given computer name array. /// /// - /// List of session wrapper objects + /// List of session wrapper objects. internal IEnumerable QuerySessionByComputerName( IEnumerable computernameArray, out IEnumerable errorRecords) { - HashSet sessions = new HashSet(); - HashSet sessionIds = new HashSet(); - List errRecords = new List(); + HashSet sessions = new(); + HashSet sessionIds = new(); + List errRecords = new(); errorRecords = errRecords; foreach (string computername in computernameArray) { @@ -661,7 +643,7 @@ internal IEnumerable QuerySessionByComputerName( if (this.curCimSessionsByComputerName.ContainsKey(computername)) { HashSet wrappers = this.curCimSessionsByComputerName[computername]; - foundSession = (wrappers.Count > 0); + foundSession = wrappers.Count > 0; foreach (CimSessionWrapper wrapper in wrappers) { if (!sessionIds.Contains(wrapper.SessionId)) @@ -671,25 +653,27 @@ internal IEnumerable QuerySessionByComputerName( } } } + if (!foundSession) { AddErrorRecord(ref errRecords, computernamePropName, computername); } } + return sessions; } /// - /// Query session list by given session objects array + /// Query session list by given session objects array. /// /// - /// List of session wrapper objects + /// List of session wrapper objects. internal IEnumerable QuerySession(IEnumerable cimsessions, out IEnumerable errorRecords) { - HashSet sessions = new HashSet(); - HashSet sessionIds = new HashSet(); - List errRecords = new List(); + HashSet sessions = new(); + HashSet sessionIds = new(); + List errRecords = new(); errorRecords = errRecords; foreach (CimSession cimsession in cimsessions) { @@ -707,14 +691,15 @@ internal IEnumerable QuerySession(IEnumerable cimsessions, AddErrorRecord(ref errRecords, CimSessionClassName, CimSessionObject); } } + return sessions; } /// - /// Query session wrapper object + /// Query session wrapper object. /// /// - /// session wrapper + /// Session wrapper. internal CimSessionWrapper QuerySession(CimSession cimsession) { CimSessionWrapper wrapper; @@ -723,10 +708,10 @@ internal CimSessionWrapper QuerySession(CimSession cimsession) } /// - /// Query session object with given CimSessionInstanceID + /// Query session object with given CimSessionInstanceID. /// /// - /// CimSession object + /// CimSession object. internal CimSession QuerySession(Guid cimSessionInstanceId) { if (this.curCimSessionsByInstanceId.ContainsKey(cimSessionInstanceId)) @@ -734,6 +719,7 @@ internal CimSession QuerySession(Guid cimSessionInstanceId) CimSessionWrapper wrapper = this.curCimSessionsByInstanceId[cimSessionInstanceId]; return wrapper.CimSession; } + return null; } #endregion @@ -756,18 +742,19 @@ internal class CimSessionBase #region constructor /// - /// Constructor + /// Initializes a new instance of the class. /// public CimSessionBase() { this.sessionState = cimSessions.GetOrAdd( CurrentRunspaceId, - delegate(Guid instanceId) + (Guid instanceId) => { if (Runspace.DefaultRunspace != null) { Runspace.DefaultRunspace.StateChanged += DefaultRunspace_StateChanged; } + return new CimSessionState(); }); } @@ -783,15 +770,15 @@ public CimSessionBase() /// can running parallelly under more than one runspace(s). /// /// - static internal ConcurrentDictionary cimSessions - = new ConcurrentDictionary(); + internal static readonly ConcurrentDictionary cimSessions + = new(); /// /// - /// Default runspace id + /// Default runspace Id. /// /// - static internal Guid defaultRunspaceId = Guid.Empty; + internal static readonly Guid defaultRunspaceId = Guid.Empty; /// /// @@ -802,7 +789,7 @@ static internal ConcurrentDictionary cimSessions internal CimSessionState sessionState; /// - /// Get current runspace id + /// Get current runspace id. /// private static Guid CurrentRunspaceId { @@ -829,11 +816,11 @@ public static CimSessionState GetCimSessionState() /// /// - /// clean up the dictionaries if the runspace is closed or broken. + /// Clean up the dictionaries if the runspace is closed or broken. /// /// - /// Runspace - /// Event args + /// Runspace. + /// Event args. private static void DefaultRunspace_StateChanged(object sender, RunspaceStateEventArgs e) { Runspace runspace = (Runspace)sender; @@ -844,9 +831,10 @@ private static void DefaultRunspace_StateChanged(object sender, RunspaceStateEve CimSessionState state; if (cimSessions.TryRemove(runspace.InstanceId, out state)) { - DebugHelper.WriteLog(String.Format(CultureInfo.CurrentUICulture, DebugHelper.runspaceStateChanged, runspace.InstanceId, e.RunspaceStateInfo.State)); + DebugHelper.WriteLog(string.Format(CultureInfo.CurrentUICulture, DebugHelper.runspaceStateChanged, runspace.InstanceId, e.RunspaceStateInfo.State)); state.Dispose(); } + runspace.StateChanged -= DefaultRunspace_StateChanged; break; default: @@ -872,14 +860,12 @@ private static void DefaultRunspace_StateChanged(object sender, RunspaceStateEve internal class CimNewSession : CimSessionBase, IDisposable { /// - /// CimTestCimSessionContext + /// CimTestCimSessionContext. /// internal class CimTestCimSessionContext : XOperationContextBase { /// - /// - /// Constructor - /// + /// Initializes a new instance of the class. /// /// /// @@ -888,37 +874,28 @@ internal CimTestCimSessionContext( CimSessionWrapper wrapper) { this.proxy = theProxy; - this.cimSessionWrapper = wrapper; + this.CimSessionWrapper = wrapper; this.nameSpace = null; } /// - /// namespace + /// Namespace /// - internal CimSessionWrapper CimSessionWrapper - { - get - { - return this.cimSessionWrapper; - } - } - private CimSessionWrapper cimSessionWrapper; + internal CimSessionWrapper CimSessionWrapper { get; } } /// - /// - /// constructor - /// + /// Initializes a new instance of the class. /// internal CimNewSession() : base() { this.cimTestSession = new CimTestSession(); - this._disposed = false; + this.Disposed = false; } /// /// Create a new base on given cmdlet - /// and its parameter + /// and its parameter. /// /// /// @@ -939,19 +916,20 @@ internal void NewCimSession(NewCimSessionCommand cmdlet, sessionOptions = CimSessionProxy.CreateCimSessionOption(computerName, cmdlet.OperationTimeoutSec, credential); } + proxy = new CimSessionProxyTestConnection(computerName, sessionOptions); string computerNameValue = (computerName == ConstValue.NullComputerName) ? ConstValue.LocalhostComputerName : computerName; - CimSessionWrapper wrapper = new CimSessionWrapper(0, Guid.Empty, cmdlet.Name, computerNameValue, proxy.CimSession, proxy.Protocol); - CimTestCimSessionContext context = new CimTestCimSessionContext(proxy, wrapper); + CimSessionWrapper wrapper = new(0, Guid.Empty, cmdlet.Name, computerNameValue, proxy.CimSession, proxy.Protocol); + CimTestCimSessionContext context = new(proxy, wrapper); proxy.ContextObject = context; // Skip test the connection if user intend to - if(cmdlet.SkipTestConnection.IsPresent) + if (cmdlet.SkipTestConnection.IsPresent) { AddSessionToCache(proxy.CimSession, context, new CmdletOperationBase(cmdlet)); } else { - //CimSession will be returned as part of TestConnection + // CimSession will be returned as part of TestConnection this.cimTestSession.TestCimSession(computerName, proxy); } } @@ -959,7 +937,7 @@ internal void NewCimSession(NewCimSessionCommand cmdlet, /// /// - /// Add session to global cache + /// Add session to global cache, /// /// /// @@ -970,9 +948,9 @@ internal void AddSessionToCache(CimSession cimSession, XOperationContextBase con DebugHelper.WriteLogEx(); CimTestCimSessionContext testCimSessionContext = context as CimTestCimSessionContext; - UInt32 sessionId = this.sessionState.GenerateSessionId(); + uint sessionId = this.sessionState.GenerateSessionId(); string originalSessionName = testCimSessionContext.CimSessionWrapper.Name; - string sessionName = (originalSessionName != null) ? originalSessionName : String.Format(CultureInfo.CurrentUICulture, @"{0}{1}", CimSessionState.CimSessionClassName, sessionId); + string sessionName = originalSessionName ?? string.Create(CultureInfo.CurrentUICulture, $"{CimSessionState.CimSessionClassName}{sessionId}"); // detach CimSession from the proxy object CimSession createdCimSession = testCimSessionContext.Proxy.Detach(); @@ -988,11 +966,11 @@ internal void AddSessionToCache(CimSession cimSession, XOperationContextBase con /// /// - /// process all actions in the action queue + /// Process all actions in the action queue. /// /// /// - /// wrapper of cmdlet, for details + /// Wrapper of cmdlet, for details. /// public void ProcessActions(CmdletOperationBase cmdletOperation) { @@ -1001,12 +979,12 @@ public void ProcessActions(CmdletOperationBase cmdletOperation) /// /// - /// process remaining actions until all operations are completed or - /// current cmdlet is terminated by user + /// Process remaining actions until all operations are completed or + /// current cmdlet is terminated by user. /// /// /// - /// wrapper of cmdlet, for details + /// Wrapper of cmdlet, for details. /// public void ProcessRemainActions(CmdletOperationBase cmdletOperation) { @@ -1019,24 +997,17 @@ public void ProcessRemainActions(CmdletOperationBase cmdletOperation) /// object. /// /// - private CimTestSession cimTestSession; - #endregion //private members + private readonly CimTestSession cimTestSession; + #endregion // private members #region IDisposable /// /// - /// Indicates whether this object was disposed or not + /// Indicates whether this object was disposed or not. /// /// - protected bool Disposed - { - get - { - return _disposed; - } - } - private bool _disposed; + protected bool Disposed { get; private set; } /// /// @@ -1067,22 +1038,22 @@ public void Dispose() /// other objects. Only unmanaged resources can be disposed. /// /// - /// Whether it is directly called + /// Whether it is directly called. protected virtual void Dispose(bool disposing) { - if (!this._disposed) + if (!this.Disposed) { if (disposing) { // free managed resources this.cimTestSession.Dispose(); - this._disposed = true; + this.Disposed = true; } // free native resources if there are any } } #endregion - }//End Class + } #endregion @@ -1090,13 +1061,13 @@ protected virtual void Dispose(bool disposing) /// /// - /// Get CimSession based on given id/instanceid/computername/name + /// Get CimSession based on given id/instanceid/computername/name. /// /// internal class CimGetSession : CimSessionBase { /// - /// constructor + /// Initializes a new instance of the class. /// public CimGetSession() : base() { @@ -1104,7 +1075,7 @@ public CimGetSession() : base() /// /// Get objects based on the given cmdlet - /// and its parameter + /// and its parameter. /// /// public void GetCimSession(GetCimSessionCommand cmdlet) @@ -1124,6 +1095,7 @@ public void GetCimSession(GetCimSessionCommand cmdlet) { sessionToGet = this.sessionState.QuerySessionByComputerName(cmdlet.ComputerName, out errorRecords); } + break; case CimBaseCommand.SessionIdSet: sessionToGet = this.sessionState.QuerySession(cmdlet.Id, out errorRecords); @@ -1137,13 +1109,15 @@ public void GetCimSession(GetCimSessionCommand cmdlet) default: break; } + if (sessionToGet != null) { - foreach(PSObject psobject in sessionToGet) + foreach (PSObject psobject in sessionToGet) { cmdlet.WriteObject(psobject); } } + if (errorRecords != null) { foreach (ErrorRecord errRecord in errorRecords) @@ -1156,7 +1130,7 @@ public void GetCimSession(GetCimSessionCommand cmdlet) #region helper methods #endregion - }//End Class + } #endregion @@ -1164,18 +1138,18 @@ public void GetCimSession(GetCimSessionCommand cmdlet) /// /// - /// Get CimSession based on given id/instanceid/computername/name + /// Get CimSession based on given id/instanceid/computername/name. /// /// internal class CimRemoveSession : CimSessionBase { /// - /// Remove session action string + /// Remove session action string. /// - internal static string RemoveCimSessionActionName = "Remove CimSession"; + internal static readonly string RemoveCimSessionActionName = "Remove CimSession"; /// - /// constructor + /// Initializes a new instance of the class. /// public CimRemoveSession() : base() { @@ -1183,7 +1157,7 @@ public CimRemoveSession() : base() /// /// Remove the objects based on given cmdlet - /// and its parameter + /// and its parameter. /// /// public void RemoveCimSession(RemoveCimSessionCommand cmdlet) @@ -1212,6 +1186,7 @@ public void RemoveCimSession(RemoveCimSessionCommand cmdlet) default: break; } + if (sessionToRemove != null) { foreach (PSObject psobject in sessionToRemove) @@ -1222,6 +1197,7 @@ public void RemoveCimSession(RemoveCimSessionCommand cmdlet) } } } + if (errorRecords != null) { foreach (ErrorRecord errRecord in errorRecords) @@ -1230,7 +1206,7 @@ public void RemoveCimSession(RemoveCimSessionCommand cmdlet) } } } - }//End Class + } #endregion @@ -1243,7 +1219,7 @@ public void RemoveCimSession(RemoveCimSessionCommand cmdlet) internal class CimTestSession : CimAsyncOperation { /// - /// Constructor + /// Initializes a new instance of the class. /// internal CimTestSession() : base() @@ -1268,4 +1244,4 @@ internal void TestCimSession( #endregion -}//End namespace +} diff --git a/src/Microsoft.Management.Infrastructure.CimCmdlets/CimSessionProxy.cs b/src/Microsoft.Management.Infrastructure.CimCmdlets/CimSessionProxy.cs index ba2cd51372f..5cdc168ae24 100644 --- a/src/Microsoft.Management.Infrastructure.CimCmdlets/CimSessionProxy.cs +++ b/src/Microsoft.Management.Infrastructure.CimCmdlets/CimSessionProxy.cs @@ -1,7 +1,5 @@ -/*============================================================================ - * Copyright (C) Microsoft Corporation, All rights reserved. - *============================================================================ - */ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. #region Using directives @@ -24,7 +22,7 @@ namespace Microsoft.Management.Infrastructure.CimCmdlets #region Context base class /// - /// context base class for cross operations + /// Context base class for cross operations /// for example, some cmdlets need to query instance first and then /// remove instance, those scenarios need context object transferred /// from one operation to another. @@ -41,11 +39,12 @@ internal string Namespace return this.nameSpace; } } + protected string nameSpace; /// /// - /// Session proxy + /// Session proxy. /// /// internal CimSessionProxy Proxy @@ -55,17 +54,18 @@ internal CimSessionProxy Proxy return this.proxy; } } + protected CimSessionProxy proxy; } /// - /// class provides all information regarding the - /// current invocation to .net api + /// Class provides all information regarding the + /// current invocation to the .NET API. /// internal class InvocationContext { /// - /// Constructor + /// Initializes a new instance of the class. /// /// internal InvocationContext(CimSessionProxy proxy) @@ -78,7 +78,7 @@ internal InvocationContext(CimSessionProxy proxy) } /// - /// Constructor + /// Initializes a new instance of the class. /// /// internal InvocationContext(string computerName, CimInstance targetCimInstance) @@ -91,36 +91,28 @@ internal InvocationContext(string computerName, CimInstance targetCimInstance) /// /// ComputerName of the session /// + /// /// /// return value could be null /// - /// - internal virtual string ComputerName - { - get; - private set; - } + internal virtual string ComputerName { get; } /// /// /// CimInstance on which the current operation against. /// + /// /// /// return value could be null /// - /// - internal virtual CimInstance TargetCimInstance - { - get; - private set; - } + internal virtual CimInstance TargetCimInstance { get; } } #endregion #region Preprocessing of result object interface /// - /// Defines a method to preprocessing an result object before sending to - /// output pipeline. + /// Defines a method to preprocessing an result object before sending to + /// output pipeline. /// [ComVisible(false)] internal interface IObjectPreProcess @@ -129,7 +121,7 @@ internal interface IObjectPreProcess /// Performs pre processing of given result object. /// /// - /// Pre-processed object + /// Pre-processed object. object Process(object resultObject); } #endregion @@ -143,13 +135,14 @@ internal interface IObjectPreProcess internal sealed class CmdletActionEventArgs : EventArgs { /// - /// Constructor + /// Initializes a new instance of the class. /// - /// CimBaseAction object bound to the event + /// CimBaseAction object bound to the event. public CmdletActionEventArgs(CimBaseAction action) { this.Action = action; } + public readonly CimBaseAction Action; } @@ -159,10 +152,10 @@ public CmdletActionEventArgs(CimBaseAction action) internal sealed class OperationEventArgs : EventArgs { /// - /// constructor + /// Initializes a new instance of the class. /// - /// Object used to cancel the operation - /// Async observable operation + /// Object used to cancel the operation. + /// Async observable operation. public OperationEventArgs(IDisposable operationCancellation, IObservable operation, bool theSuccess) @@ -171,6 +164,7 @@ public OperationEventArgs(IDisposable operationCancellation, this.operation = operation; this.success = theSuccess; } + public readonly IDisposable operationCancellation; public readonly IObservable operation; public readonly bool success; @@ -196,9 +190,9 @@ internal class CimSessionProxy : IDisposable private static long gOperationCounter = 0; /// - /// temporary CimSession cache lock + /// Temporary CimSession cache lock. /// - private static readonly object temporarySessionCacheLock = new object(); + private static readonly object temporarySessionCacheLock = new(); /// /// temporary CimSession cache @@ -216,7 +210,7 @@ internal class CimSessionProxy : IDisposable /// then call Dispose on it. /// /// - private static Dictionary temporarySessionCache = new Dictionary(); + private static readonly Dictionary temporarySessionCache = new(); /// /// @@ -225,10 +219,10 @@ internal class CimSessionProxy : IDisposable /// otherwise insert it into the cache. /// /// - /// CimSession to be added + /// CimSession to be added. internal static void AddCimSessionToTemporaryCache(CimSession session) { - if (null != session) + if (session != null) { lock (temporarySessionCacheLock) { @@ -250,11 +244,11 @@ internal static void AddCimSessionToTemporaryCache(CimSession session) /// Wrapper function to remove CimSession from cache /// /// - /// Whether need to dispose the object + /// Whether need to dispose the object. private static void RemoveCimSessionFromTemporaryCache(CimSession session, bool dispose) { - if (null != session) + if (session != null) { bool removed = false; lock (temporarySessionCacheLock) @@ -293,7 +287,7 @@ private static void RemoveCimSessionFromTemporaryCache(CimSession session, /// If refcount became 0, call dispose on the object. /// /// - /// CimSession to be added + /// CimSession to be added. internal static void RemoveCimSessionFromTemporaryCache(CimSession session) { RemoveCimSessionFromTemporaryCache(session, true); @@ -303,87 +297,79 @@ internal static void RemoveCimSessionFromTemporaryCache(CimSession session) #region Event definitions /// - /// Define delegate that handles new cmdlet action come from - /// the operations related to the current CimSession object. - /// - /// cimSession object, which raised the event - /// Event args - public delegate void NewCmdletActionHandler( - object cimSession, - CmdletActionEventArgs actionArgs); - - /// - /// Define an Event based on the NewActionHandler + /// Define an Event based on the NewActionHandler. /// - public event NewCmdletActionHandler OnNewCmdletAction; + public event EventHandler OnNewCmdletAction; /// - /// Define delegate that handles operation creation and complete - /// issued by the current CimSession object. + /// Event triggered when a new operation is started. /// - /// cimSession object, which raised the event - /// Event args - public delegate void OperationEventHandler( - object cimSession, - OperationEventArgs actionArgs); - - /// - /// Event triggered when a new operation is started - /// - public event OperationEventHandler OnOperationCreated; + public event EventHandler OnOperationCreated; /// /// Event triggered when a new operation is completed, /// either success or failed. /// - public event OperationEventHandler OnOperationDeleted; + public event EventHandler OnOperationDeleted; #endregion #region constructors /// - /// Then create wrapper object by given CimSessionProxy object. + /// Initializes a new instance of the class. /// + /// + /// Then create wrapper object by given CimSessionProxy object. + /// /// public CimSessionProxy(CimSessionProxy proxy) { DebugHelper.WriteLogEx("protocol = {0}", 1, proxy.Protocol); CreateSetSession(null, proxy.CimSession, null, proxy.OperationOptions, proxy.IsTemporaryCimSession); - this.protocol = proxy.Protocol; + this.Protocol = proxy.Protocol; this.OperationTimeout = proxy.OperationTimeout; this.isDefaultSession = proxy.isDefaultSession; } /// + /// Initializes a new instance of the class. + /// + /// /// Create by given computer name. /// Then create wrapper object. - /// + /// /// public CimSessionProxy(string computerName) { CreateSetSession(computerName, null, null, null, false); - this.isDefaultSession = (computerName == ConstValue.NullComputerName); + this.isDefaultSession = computerName == ConstValue.NullComputerName; } /// + /// Initializes a new instance of the class. + /// + /// /// Create by given computer name /// and session options. /// Then create wrapper object. - /// + /// /// /// public CimSessionProxy(string computerName, CimSessionOptions sessionOptions) { CreateSetSession(computerName, null, sessionOptions, null, false); - this.isDefaultSession = (computerName == ConstValue.NullComputerName); + this.isDefaultSession = computerName == ConstValue.NullComputerName; } /// + /// Initializes a new instance of the class. + /// + /// /// Create by given computer name /// and cimInstance. Then create wrapper object. - /// + /// /// /// public CimSessionProxy(string computerName, CimInstance cimInstance) @@ -414,41 +400,51 @@ public CimSessionProxy(string computerName, CimInstance cimInstance) return; } } - String cimsessionComputerName = cimInstance.GetCimSessionComputerName(); + + string cimsessionComputerName = cimInstance.GetCimSessionComputerName(); CreateSetSession(cimsessionComputerName, null, null, null, false); - this.isDefaultSession = (cimsessionComputerName == ConstValue.NullComputerName); + this.isDefaultSession = cimsessionComputerName == ConstValue.NullComputerName; DebugHelper.WriteLogEx("Create a temp session with computerName = {0}.", 0, cimsessionComputerName); } /// - /// Create by given computer name, - /// session options + /// Initializes a new instance of the class. /// + /// + /// Create by given computer name, + /// session options. + /// /// /// - /// Used when create async operation + /// Used when create async operation. public CimSessionProxy(string computerName, CimSessionOptions sessionOptions, CimOperationOptions operOptions) { CreateSetSession(computerName, null, sessionOptions, operOptions, false); - this.isDefaultSession = (computerName == ConstValue.NullComputerName); + this.isDefaultSession = computerName == ConstValue.NullComputerName; } /// + /// Initializes a new instance of the class. + /// + /// /// Create by given computer name. /// Then create wrapper object. - /// + /// /// - /// Used when create async operation + /// Used when create async operation. public CimSessionProxy(string computerName, CimOperationOptions operOptions) { CreateSetSession(computerName, null, null, operOptions, false); - this.isDefaultSession = (computerName == ConstValue.NullComputerName); + this.isDefaultSession = computerName == ConstValue.NullComputerName; } /// - /// Create wrapper object by given session object + /// Initializes a new instance of the class. /// + /// + /// Create wrapper object by given session object. + /// /// public CimSessionProxy(CimSession session) { @@ -456,17 +452,20 @@ public CimSessionProxy(CimSession session) } /// - /// Create wrapper object by given session object + /// Initializes a new instance of the class. /// + /// + /// Create wrapper object by given session object. + /// /// - /// Used when create async operation + /// Used when create async operation. public CimSessionProxy(CimSession session, CimOperationOptions operOptions) { CreateSetSession(null, session, null, operOptions, false); } /// - /// Initialize CimSessionProxy object + /// Initialize CimSessionProxy object. /// /// /// @@ -486,20 +485,21 @@ private void CreateSetSession( this.CancelOperation = null; this.operation = null; } + InitOption(operOptions); - this.protocol = ProtocolType.Wsman; - this.isTemporaryCimSession = temporaryCimSession; + this.Protocol = ProtocolType.Wsman; + this.IsTemporaryCimSession = temporaryCimSession; if (cimSession != null) { - this.session = cimSession; + this.CimSession = cimSession; CimSessionState state = CimSessionBase.GetCimSessionState(); if (state != null) { CimSessionWrapper wrapper = state.QuerySession(cimSession); if (wrapper != null) { - this.protocol = wrapper.GetProtocolType(); + this.Protocol = wrapper.GetProtocolType(); } } } @@ -510,27 +510,29 @@ private void CreateSetSession( if (sessionOptions is DComSessionOptions) { string defaultComputerName = ConstValue.IsDefaultComputerName(computerName) ? ConstValue.NullComputerName : computerName; - this.session = CimSession.Create(defaultComputerName, sessionOptions); - this.protocol = ProtocolType.Dcom; + this.CimSession = CimSession.Create(defaultComputerName, sessionOptions); + this.Protocol = ProtocolType.Dcom; } else { - this.session = CimSession.Create(computerName, sessionOptions); + this.CimSession = CimSession.Create(computerName, sessionOptions); } } else { - this.session = CreateCimSessionByComputerName(computerName); + this.CimSession = CreateCimSessionByComputerName(computerName); } - this.isTemporaryCimSession = true; + + this.IsTemporaryCimSession = true; } - if (this.isTemporaryCimSession) + if (this.IsTemporaryCimSession) { - AddCimSessionToTemporaryCache(this.session); + AddCimSessionToTemporaryCache(this.CimSession); } + this.invocationContextObject = new InvocationContext(this); - DebugHelper.WriteLog("Protocol {0}, Is temporary session ? {1}", 1, this.protocol, this.isTemporaryCimSession); + DebugHelper.WriteLog("Protocol {0}, Is temporary session ? {1}", 1, this.Protocol, this.IsTemporaryCimSession); } #endregion @@ -538,36 +540,58 @@ private void CreateSetSession( #region set operation options /// - /// Set timeout value (seconds) of the operation + /// Gets or sets a value indicating whether to retrieve localized information for the CIM class. /// - public UInt32 OperationTimeout + public bool Amended { + get => OperationOptions.Flags.HasFlag(CimOperationFlags.LocalizedQualifiers); + set { - DebugHelper.WriteLogEx("OperationTimeout {0},", 0, value); - - this.options.Timeout = TimeSpan.FromSeconds((double)value); + if (value) + { + OperationOptions.Flags |= CimOperationFlags.LocalizedQualifiers; + } + else + { + OperationOptions.Flags &= ~CimOperationFlags.LocalizedQualifiers; + } } + } + + /// + /// Set timeout value (seconds) of the operation. + /// + public uint OperationTimeout + { get { - return (UInt32)this.options.Timeout.TotalSeconds; + return (uint)this.OperationOptions.Timeout.TotalSeconds; + } + + set + { + DebugHelper.WriteLogEx("OperationTimeout {0},", 0, value); + + this.OperationOptions.Timeout = TimeSpan.FromSeconds((double)value); } } /// - /// Set resource URI of the operation + /// Set resource URI of the operation. /// public Uri ResourceUri { + get + { + return this.OperationOptions.ResourceUri; + } + set { DebugHelper.WriteLogEx("ResourceUri {0},", 0, value); - this.options.ResourceUri= value; - } - get - { - return this.options.ResourceUri; + this.OperationOptions.ResourceUri = value; } } @@ -579,12 +603,13 @@ public bool EnableMethodResultStreaming { get { - return this.options.EnableMethodResultStreaming; + return this.OperationOptions.EnableMethodResultStreaming; } + set { DebugHelper.WriteLogEx("EnableMethodResultStreaming {0}", 0, value); - this.options.EnableMethodResultStreaming = value; + this.OperationOptions.EnableMethodResultStreaming = value; } } @@ -597,15 +622,15 @@ public bool EnablePromptUser set { DebugHelper.WriteLogEx("EnablePromptUser {0}", 0, value); - if(value) + if (value) { - this.options.PromptUser = this.PromptUser; + this.OperationOptions.PromptUser = this.PromptUser; } } } /// - /// Enable the pssemantics + /// Enable the pssemantics. /// private void EnablePSSemantics() { @@ -613,15 +638,15 @@ private void EnablePSSemantics() // this.options.PromptUserForceFlag... // this.options.WriteErrorMode - this.options.WriteErrorMode = CimCallbackMode.Inquire; + this.OperationOptions.WriteErrorMode = CimCallbackMode.Inquire; // !!!NOTES: Does not subscribe to PromptUser for CimCmdlets now // since cmdlet does not provider an approach // to let user select how to handle prompt message // this can be enabled later if needed. - this.options.WriteError = this.WriteError; - this.options.WriteMessage = this.WriteMessage; - this.options.WriteProgress = this.WriteProgress; + this.OperationOptions.WriteError = this.WriteError; + this.OperationOptions.WriteMessage = this.WriteMessage; + this.OperationOptions.WriteProgress = this.WriteProgress; } /// @@ -629,7 +654,7 @@ private void EnablePSSemantics() /// public SwitchParameter KeyOnly { - set { this.options.KeysOnly = value.IsPresent; } + set { this.OperationOptions.KeysOnly = value.IsPresent; } } /// @@ -641,17 +666,17 @@ public SwitchParameter Shallow { if (value.IsPresent) { - this.options.Flags = CimOperationFlags.PolymorphismShallow; + this.OperationOptions.Flags = CimOperationFlags.PolymorphismShallow; } else { - this.options.Flags = CimOperationFlags.None; + this.OperationOptions.Flags = CimOperationFlags.None; } } } /// - /// Initialize the operation option + /// Initialize the operation option. /// private void InitOption(CimOperationOptions operOptions) { @@ -659,12 +684,13 @@ private void InitOption(CimOperationOptions operOptions) if (operOptions != null) { - this.options = new CimOperationOptions(operOptions); + this.OperationOptions = new CimOperationOptions(operOptions); } - else if (this.options == null) + else { - this.options = new CimOperationOptions(); + this.OperationOptions ??= new CimOperationOptions(); } + this.EnableMethodResultStreaming = true; this.EnablePSSemantics(); } @@ -683,10 +709,10 @@ public CimSession Detach() DebugHelper.WriteLogEx(); // Remove the CimSession from cache but don't dispose it - RemoveCimSessionFromTemporaryCache(this.session, false); - CimSession sessionToReturn = this.session; - this.session = null; - this.isTemporaryCimSession = false; + RemoveCimSessionFromTemporaryCache(this.CimSession, false); + CimSession sessionToReturn = this.CimSession; + this.CimSession = null; + this.IsTemporaryCimSession = false; return sessionToReturn; } @@ -707,7 +733,7 @@ private void AddOperation(IObservable operation) } /// - /// Remove object from cache + /// Remove object from cache. /// /// private void RemoveOperation(IObservable operation) @@ -724,7 +750,8 @@ private void RemoveOperation(IObservable operation) { this.operation = null; } - if (this.session != null && this.ContextObject == null) + + if (this.CimSession != null && this.ContextObject == null) { DebugHelper.WriteLog("Dispose this proxy object @ RemoveOperation"); this.Dispose(); @@ -742,21 +769,22 @@ protected void FireNewActionEvent(CimBaseAction action) { DebugHelper.WriteLogEx(); - CmdletActionEventArgs actionArgs = new CmdletActionEventArgs(action); + CmdletActionEventArgs actionArgs = new(action); if (!PreNewActionEvent(actionArgs)) { return; } - NewCmdletActionHandler temp = this.OnNewCmdletAction; + EventHandler temp = this.OnNewCmdletAction; if (temp != null) { - temp(this.session, actionArgs); + temp(this.CimSession, actionArgs); } else { DebugHelper.WriteLog("Ignore action since OnNewCmdletAction is null.", 5); } + this.PostNewActionEvent(actionArgs); } @@ -773,13 +801,10 @@ private void FireOperationCreatedEvent( { DebugHelper.WriteLogEx(); - OperationEventArgs args = new OperationEventArgs( + OperationEventArgs args = new( cancelOperation, operation, false); - OperationEventHandler temp = this.OnOperationCreated; - if (temp != null) - { - temp(this.session, args); - } + this.OnOperationCreated?.Invoke(this.CimSession, args); + this.PostOperationCreateEvent(args); } @@ -795,14 +820,11 @@ private void FireOperationDeletedEvent( { DebugHelper.WriteLogEx(); this.WriteOperationCompleteMessage(this.operationName); - OperationEventArgs args = new OperationEventArgs( + OperationEventArgs args = new( null, operation, success); PreOperationDeleteEvent(args); - OperationEventHandler temp = this.OnOperationDeleted; - if (temp != null) - { - temp(this.session, args); - } + this.OnOperationDeleted?.Invoke(this.CimSession, args); + this.PostOperationDeleteEvent(args); this.RemoveOperation(operation); this.operationName = null; @@ -819,12 +841,12 @@ private void FireOperationDeletedEvent( /// /// /// - internal void WriteMessage(UInt32 channel, string message) + internal void WriteMessage(uint channel, string message) { DebugHelper.WriteLogEx("Channel = {0} message = {1}", 0, channel, message); try { - CimWriteMessage action = new CimWriteMessage(channel, message); + CimWriteMessage action = new(channel, message); this.FireNewActionEvent(action); } catch (Exception ex) @@ -843,23 +865,25 @@ internal void WriteMessage(UInt32 channel, string message) internal void WriteOperationStartMessage(string operation, Hashtable parameterList) { DebugHelper.WriteLogEx(); - StringBuilder parameters = new StringBuilder(); + StringBuilder parameters = new(); if (parameterList != null) { foreach (string key in parameterList.Keys) { if (parameters.Length > 0) { - parameters.Append(","); + parameters.Append(','); } - parameters.Append(string.Format(CultureInfo.CurrentUICulture, @"'{0}' = {1}", key, parameterList[key])); + + parameters.Append(CultureInfo.CurrentUICulture, $@"'{key}' = {parameterList[key]}"); } } + string operationStartMessage = string.Format(CultureInfo.CurrentUICulture, - Strings.CimOperationStart, + CimCmdletStrings.CimOperationStart, operation, (parameters.Length == 0) ? "null" : parameters.ToString()); - WriteMessage((UInt32)CimWriteMessageChannel.Verbose, operationStartMessage); + WriteMessage((uint)CimWriteMessageChannel.Verbose, operationStartMessage); } /// @@ -872,9 +896,9 @@ internal void WriteOperationCompleteMessage(string operation) { DebugHelper.WriteLogEx(); string operationCompleteMessage = string.Format(CultureInfo.CurrentUICulture, - Strings.CimOperationCompleted, + CimCmdletStrings.CimOperationCompleted, operation); - WriteMessage((UInt32)CimWriteMessageChannel.Verbose, operationCompleteMessage); + WriteMessage((uint)CimWriteMessageChannel.Verbose, operationCompleteMessage); } /// @@ -890,15 +914,15 @@ internal void WriteOperationCompleteMessage(string operation) public void WriteProgress(string activity, string currentOperation, string statusDescription, - UInt32 percentageCompleted, - UInt32 secondsRemaining) + uint percentageCompleted, + uint secondsRemaining) { DebugHelper.WriteLogEx("activity:{0}; currentOperation:{1}; percentageCompleted:{2}; secondsRemaining:{3}", 0, activity, currentOperation, percentageCompleted, secondsRemaining); try { - CimWriteProgress action = new CimWriteProgress( + CimWriteProgress action = new( activity, (int)this.operationID, currentOperation, @@ -925,7 +949,7 @@ public CimResponseType WriteError(CimInstance instance) DebugHelper.WriteLogEx("Error:{0}", 0, instance); try { - CimWriteError action = new CimWriteError(instance, this.invocationContextObject); + CimWriteError action = new(instance, this.invocationContextObject); this.FireNewActionEvent(action); return action.GetResponse(); } @@ -937,7 +961,7 @@ public CimResponseType WriteError(CimInstance instance) } /// - /// PromptUser callback + /// PromptUser callback. /// /// /// @@ -947,7 +971,7 @@ public CimResponseType PromptUser(string message, CimPromptType prompt) DebugHelper.WriteLogEx("message:{0} prompt:{1}", 0, message, prompt); try { - CimPromptUser action = new CimPromptUser(message, prompt); + CimPromptUser action = new(message, prompt); this.FireNewActionEvent(action); return action.GetResponse(); } @@ -963,11 +987,11 @@ public CimResponseType PromptUser(string message, CimPromptType prompt) /// /// - /// Handle async event triggered by + /// Handle async event triggered by /// /// - /// object triggered the event - /// async result event argument + /// Object triggered the event. + /// Async result event argument. internal void ResultEventHandler( object observer, AsyncResultEventArgsBase resultArgs) @@ -982,18 +1006,21 @@ internal void ResultEventHandler( AsyncResultCompleteEventArgs args = resultArgs as AsyncResultCompleteEventArgs; this.FireOperationDeletedEvent(args.observable, true); } + break; case AsyncResultType.Exception: { AsyncResultErrorEventArgs args = resultArgs as AsyncResultErrorEventArgs; DebugHelper.WriteLog("ResultEventHandler::Exception {0}", 4, args.error); - using (CimWriteError action = new CimWriteError(args.error, this.invocationContextObject, args.context)) + using (CimWriteError action = new(args.error, this.invocationContextObject, args.context)) { this.FireNewActionEvent(action); } + this.FireOperationDeletedEvent(args.observable, false); } + break; case AsyncResultType.Result: { @@ -1004,6 +1031,7 @@ internal void ResultEventHandler( { AddShowComputerNameMarker(resultObject); } + if (this.ObjectPreProcess != null) { resultObject = this.ObjectPreProcess.Process(resultObject); @@ -1011,9 +1039,10 @@ internal void ResultEventHandler( #if DEBUG resultObject = PostProcessCimInstance(resultObject); #endif - CimWriteResultObject action = new CimWriteResultObject(resultObject, this.ContextObject); + CimWriteResultObject action = new(resultObject, this.ContextObject); this.FireNewActionEvent(action); } + break; default: break; @@ -1034,22 +1063,24 @@ private static void AddShowComputerNameMarker(object o) } PSObject pso = PSObject.AsPSObject(o); - if (!(pso.BaseObject is CimInstance)) + if (pso.BaseObject is not CimInstance) { return; } - PSNoteProperty psShowComputerNameProperty = new PSNoteProperty(ConstValue.ShowComputerNameNoteProperty, true); + PSNoteProperty psShowComputerNameProperty = new(ConstValue.ShowComputerNameNoteProperty, true); pso.Members.Add(psShowComputerNameProperty); } #if DEBUG - private static bool isCliXmlTestabilityHookActive = GetIsCliXmlTestabilityHookActive(); + private static readonly bool isCliXmlTestabilityHookActive = GetIsCliXmlTestabilityHookActive(); + private static bool GetIsCliXmlTestabilityHookActive() { return !string.IsNullOrEmpty(Environment.GetEnvironmentVariable("CDXML_CLIXML_TEST")); } - private object PostProcessCimInstance(object resultObject) + + private static object PostProcessCimInstance(object resultObject) { DebugHelper.WriteLogEx(); if (isCliXmlTestabilityHookActive && (resultObject is CimInstance)) @@ -1057,9 +1088,10 @@ private object PostProcessCimInstance(object resultObject) string serializedForm = PSSerializer.Serialize(resultObject as CimInstance, depth: 1); object deserializedObject = PSSerializer.Deserialize(serializedForm); object returnObject = (deserializedObject is PSObject) ? (deserializedObject as PSObject).BaseObject : deserializedObject; - DebugHelper.WriteLogEx("Deserialized Object is {0}, type {1}", 1, returnObject, returnObject.GetType()); + DebugHelper.WriteLogEx("Deserialized object is {0}, type {1}", 1, returnObject, returnObject.GetType()); return returnObject; } + return resultObject; } #endif @@ -1077,20 +1109,20 @@ private object PostProcessCimInstance(object resultObject) public void CreateInstanceAsync(string namespaceName, CimInstance instance) { Debug.Assert(instance != null, "Caller should verify that instance != NULL."); - DebugHelper.WriteLogEx("EnableMethodResultStreaming = {0}", 0, this.options.EnableMethodResultStreaming); + DebugHelper.WriteLogEx("EnableMethodResultStreaming = {0}", 0, this.OperationOptions.EnableMethodResultStreaming); this.CheckAvailability(); - this.targetCimInstance = instance; - this.operationName = Strings.CimOperationNameCreateInstance; + this.TargetCimInstance = instance; + this.operationName = CimCmdletStrings.CimOperationNameCreateInstance; this.operationParameters.Clear(); this.operationParameters.Add(@"namespaceName", namespaceName); this.operationParameters.Add(@"instance", instance); this.WriteOperationStartMessage(this.operationName, this.operationParameters); - CimAsyncResult asyncResult = this.session.CreateInstanceAsync(namespaceName, instance, this.options); + CimAsyncResult asyncResult = this.CimSession.CreateInstanceAsync(namespaceName, instance, this.OperationOptions); ConsumeCimInstanceAsync(asyncResult, new CimResultContext(instance)); } /// - /// delete a cim instance asynchronously + /// Delete a cim instance asynchronously. /// /// /// @@ -1099,38 +1131,38 @@ public void DeleteInstanceAsync(string namespaceName, CimInstance instance) Debug.Assert(instance != null, "Caller should verify that instance != NULL."); DebugHelper.WriteLogEx("namespace = {0}; classname = {1};", 0, namespaceName, instance.CimSystemProperties.ClassName); this.CheckAvailability(); - this.targetCimInstance = instance; - this.operationName = Strings.CimOperationNameDeleteInstance; + this.TargetCimInstance = instance; + this.operationName = CimCmdletStrings.CimOperationNameDeleteInstance; this.operationParameters.Clear(); this.operationParameters.Add(@"namespaceName", namespaceName); this.operationParameters.Add(@"instance", instance); this.WriteOperationStartMessage(this.operationName, this.operationParameters); - CimAsyncStatus asyncResult = this.session.DeleteInstanceAsync(namespaceName, instance, this.options); + CimAsyncStatus asyncResult = this.CimSession.DeleteInstanceAsync(namespaceName, instance, this.OperationOptions); ConsumeObjectAsync(asyncResult, new CimResultContext(instance)); } /// - /// Get cim instance asynchronously + /// Get cim instance asynchronously. /// /// /// public void GetInstanceAsync(string namespaceName, CimInstance instance) { Debug.Assert(instance != null, "Caller should verify that instance != NULL."); - DebugHelper.WriteLogEx("namespace = {0}; classname = {1}; keyonly = {2}", 0, namespaceName, instance.CimSystemProperties.ClassName, this.options.KeysOnly); + DebugHelper.WriteLogEx("namespace = {0}; classname = {1}; keyonly = {2}", 0, namespaceName, instance.CimSystemProperties.ClassName, this.OperationOptions.KeysOnly); this.CheckAvailability(); - this.targetCimInstance = instance; - this.operationName = Strings.CimOperationNameGetInstance; + this.TargetCimInstance = instance; + this.operationName = CimCmdletStrings.CimOperationNameGetInstance; this.operationParameters.Clear(); this.operationParameters.Add(@"namespaceName", namespaceName); this.operationParameters.Add(@"instance", instance); this.WriteOperationStartMessage(this.operationName, this.operationParameters); - CimAsyncResult asyncResult = this.session.GetInstanceAsync(namespaceName, instance, this.options); + CimAsyncResult asyncResult = this.CimSession.GetInstanceAsync(namespaceName, instance, this.OperationOptions); ConsumeCimInstanceAsync(asyncResult, new CimResultContext(instance)); } /// - /// Modify cim instance asynchronously + /// Modify cim instance asynchronously. /// /// /// @@ -1139,19 +1171,19 @@ public void ModifyInstanceAsync(string namespaceName, CimInstance instance) Debug.Assert(instance != null, "Caller should verify that instance != NULL."); DebugHelper.WriteLogEx("namespace = {0}; classname = {1}", 0, namespaceName, instance.CimSystemProperties.ClassName); this.CheckAvailability(); - this.targetCimInstance = instance; - this.operationName = Strings.CimOperationNameModifyInstance; + this.TargetCimInstance = instance; + this.operationName = CimCmdletStrings.CimOperationNameModifyInstance; this.operationParameters.Clear(); this.operationParameters.Add(@"namespaceName", namespaceName); this.operationParameters.Add(@"instance", instance); this.WriteOperationStartMessage(this.operationName, this.operationParameters); - CimAsyncResult asyncResult = this.session.ModifyInstanceAsync(namespaceName, instance, this.options); + CimAsyncResult asyncResult = this.CimSession.ModifyInstanceAsync(namespaceName, instance, this.OperationOptions); ConsumeObjectAsync(asyncResult, new CimResultContext(instance)); } /// /// Enumerate cim instance associated with the - /// given instance asynchronously + /// given instance asynchronously. /// /// /// @@ -1170,8 +1202,8 @@ public void EnumerateAssociatedInstancesAsync( Debug.Assert(sourceInstance != null, "Caller should verify that sourceInstance != NULL."); DebugHelper.WriteLogEx("Instance class {0}, association class {1}", 0, sourceInstance.CimSystemProperties.ClassName, associationClassName); this.CheckAvailability(); - this.targetCimInstance = sourceInstance; - this.operationName = Strings.CimOperationNameEnumerateAssociatedInstances; + this.TargetCimInstance = sourceInstance; + this.operationName = CimCmdletStrings.CimOperationNameEnumerateAssociatedInstances; this.operationParameters.Clear(); this.operationParameters.Add(@"namespaceName", namespaceName); this.operationParameters.Add(@"sourceInstance", sourceInstance); @@ -1180,27 +1212,27 @@ public void EnumerateAssociatedInstancesAsync( this.operationParameters.Add(@"sourceRole", sourceRole); this.operationParameters.Add(@"resultRole", resultRole); this.WriteOperationStartMessage(this.operationName, this.operationParameters); - CimAsyncMultipleResults asyncResult = this.session.EnumerateAssociatedInstancesAsync(namespaceName, sourceInstance, associationClassName, resultClassName, sourceRole, resultRole, this.options); + CimAsyncMultipleResults asyncResult = this.CimSession.EnumerateAssociatedInstancesAsync(namespaceName, sourceInstance, associationClassName, resultClassName, sourceRole, resultRole, this.OperationOptions); ConsumeCimInstanceAsync(asyncResult, new CimResultContext(sourceInstance)); } /// - /// Enumerate cim instance asynchronously + /// Enumerate cim instance asynchronously. /// /// /// public void EnumerateInstancesAsync(string namespaceName, string className) { - DebugHelper.WriteLogEx("KeyOnly {0}", 0, this.options.KeysOnly); + DebugHelper.WriteLogEx("KeyOnly {0}", 0, this.OperationOptions.KeysOnly); this.CheckAvailability(); - this.targetCimInstance = null; - this.operationName = Strings.CimOperationNameEnumerateInstances; + this.TargetCimInstance = null; + this.operationName = CimCmdletStrings.CimOperationNameEnumerateInstances; this.operationParameters.Clear(); this.operationParameters.Add(@"namespaceName", namespaceName); this.operationParameters.Add(@"className", className); this.WriteOperationStartMessage(this.operationName, this.operationParameters); - CimAsyncMultipleResults asyncResult = this.session.EnumerateInstancesAsync(namespaceName, className, this.options); - string errorSource = String.Format(CultureInfo.CurrentUICulture, "{0}:{1}", namespaceName, className); + CimAsyncMultipleResults asyncResult = this.CimSession.EnumerateInstancesAsync(namespaceName, className, this.OperationOptions); + string errorSource = string.Create(CultureInfo.CurrentUICulture, $"{namespaceName}:{className}"); ConsumeCimInstanceAsync(asyncResult, new CimResultContext(errorSource)); } @@ -1236,21 +1268,21 @@ public void QueryInstancesAsync( string queryDialect, string queryExpression) { - DebugHelper.WriteLogEx("KeyOnly = {0}", 0, this.options.KeysOnly); + DebugHelper.WriteLogEx("KeyOnly = {0}", 0, this.OperationOptions.KeysOnly); this.CheckAvailability(); - this.targetCimInstance = null; - this.operationName = Strings.CimOperationNameQueryInstances; + this.TargetCimInstance = null; + this.operationName = CimCmdletStrings.CimOperationNameQueryInstances; this.operationParameters.Clear(); this.operationParameters.Add(@"namespaceName", namespaceName); this.operationParameters.Add(@"queryDialect", queryDialect); this.operationParameters.Add(@"queryExpression", queryExpression); this.WriteOperationStartMessage(this.operationName, this.operationParameters); - CimAsyncMultipleResults asyncResult = this.session.QueryInstancesAsync(namespaceName, queryDialect, queryExpression, this.options); + CimAsyncMultipleResults asyncResult = this.CimSession.QueryInstancesAsync(namespaceName, queryDialect, queryExpression, this.OperationOptions); ConsumeCimInstanceAsync(asyncResult, null); } /// - /// Enumerate cim class asynchronously + /// Enumerate cim class asynchronously. /// /// /// @@ -1258,36 +1290,36 @@ public void EnumerateClassesAsync(string namespaceName) { DebugHelper.WriteLogEx("namespace {0}", 0, namespaceName); this.CheckAvailability(); - this.targetCimInstance = null; - this.operationName = Strings.CimOperationNameEnumerateClasses; + this.TargetCimInstance = null; + this.operationName = CimCmdletStrings.CimOperationNameEnumerateClasses; this.operationParameters.Clear(); this.operationParameters.Add(@"namespaceName", namespaceName); this.WriteOperationStartMessage(this.operationName, this.operationParameters); - CimAsyncMultipleResults asyncResult = this.session.EnumerateClassesAsync(namespaceName, null, this.options); + CimAsyncMultipleResults asyncResult = this.CimSession.EnumerateClassesAsync(namespaceName, null, this.OperationOptions); ConsumeCimClassAsync(asyncResult, null); } /// - /// Enumerate cim class asynchronously + /// Enumerate cim class asynchronously. /// /// /// public void EnumerateClassesAsync(string namespaceName, string className) { this.CheckAvailability(); - this.targetCimInstance = null; - this.operationName = Strings.CimOperationNameEnumerateClasses; + this.TargetCimInstance = null; + this.operationName = CimCmdletStrings.CimOperationNameEnumerateClasses; this.operationParameters.Clear(); this.operationParameters.Add(@"namespaceName", namespaceName); this.operationParameters.Add(@"className", className); this.WriteOperationStartMessage(this.operationName, this.operationParameters); - CimAsyncMultipleResults asyncResult = this.session.EnumerateClassesAsync(namespaceName, className, this.options); - string errorSource = String.Format(CultureInfo.CurrentUICulture, "{0}:{1}", namespaceName, className); + CimAsyncMultipleResults asyncResult = this.CimSession.EnumerateClassesAsync(namespaceName, className, this.OperationOptions); + string errorSource = string.Create(CultureInfo.CurrentUICulture, $"{namespaceName}:{className}"); ConsumeCimClassAsync(asyncResult, new CimResultContext(errorSource)); } /// - /// Get cim class asynchronously + /// Get cim class asynchronously. /// /// /// @@ -1295,19 +1327,19 @@ public void GetClassAsync(string namespaceName, string className) { DebugHelper.WriteLogEx("namespace = {0}, className = {1}", 0, namespaceName, className); this.CheckAvailability(); - this.targetCimInstance = null; - this.operationName = Strings.CimOperationNameGetClass; + this.TargetCimInstance = null; + this.operationName = CimCmdletStrings.CimOperationNameGetClass; this.operationParameters.Clear(); this.operationParameters.Add(@"namespaceName", namespaceName); this.operationParameters.Add(@"className", className); this.WriteOperationStartMessage(this.operationName, this.operationParameters); - CimAsyncResult asyncResult = this.session.GetClassAsync(namespaceName, className, this.options); - string errorSource = String.Format(CultureInfo.CurrentUICulture, "{0}:{1}", namespaceName, className); + CimAsyncResult asyncResult = this.CimSession.GetClassAsync(namespaceName, className, this.OperationOptions); + string errorSource = string.Create(CultureInfo.CurrentUICulture, $"{namespaceName}:{className}"); ConsumeCimClassAsync(asyncResult, new CimResultContext(errorSource)); } /// - /// Invoke method of a given cim instance asynchronously + /// Invoke method of a given cim instance asynchronously. /// /// /// @@ -1320,21 +1352,21 @@ public void InvokeMethodAsync( CimMethodParametersCollection methodParameters) { Debug.Assert(instance != null, "Caller should verify that instance != NULL."); - DebugHelper.WriteLogEx("EnableMethodResultStreaming = {0}", 0, this.options.EnableMethodResultStreaming); + DebugHelper.WriteLogEx("EnableMethodResultStreaming = {0}", 0, this.OperationOptions.EnableMethodResultStreaming); this.CheckAvailability(); - this.targetCimInstance = instance; - this.operationName = Strings.CimOperationNameInvokeMethod; + this.TargetCimInstance = instance; + this.operationName = CimCmdletStrings.CimOperationNameInvokeMethod; this.operationParameters.Clear(); this.operationParameters.Add(@"namespaceName", namespaceName); this.operationParameters.Add(@"instance", instance); this.operationParameters.Add(@"methodName", methodName); this.WriteOperationStartMessage(this.operationName, this.operationParameters); - CimAsyncMultipleResults asyncResult = this.session.InvokeMethodAsync(namespaceName, instance, methodName, methodParameters, this.options); + CimAsyncMultipleResults asyncResult = this.CimSession.InvokeMethodAsync(namespaceName, instance, methodName, methodParameters, this.OperationOptions); ConsumeCimInvokeMethodResultAsync(asyncResult, instance.CimSystemProperties.ClassName, methodName, new CimResultContext(instance)); } /// - /// Invoke static method of a given class asynchronously + /// Invoke static method of a given class asynchronously. /// /// /// @@ -1346,17 +1378,17 @@ public void InvokeMethodAsync( string methodName, CimMethodParametersCollection methodParameters) { - DebugHelper.WriteLogEx("EnableMethodResultStreaming = {0}", 0, this.options.EnableMethodResultStreaming); + DebugHelper.WriteLogEx("EnableMethodResultStreaming = {0}", 0, this.OperationOptions.EnableMethodResultStreaming); this.CheckAvailability(); - this.targetCimInstance = null; - this.operationName = Strings.CimOperationNameInvokeMethod; + this.TargetCimInstance = null; + this.operationName = CimCmdletStrings.CimOperationNameInvokeMethod; this.operationParameters.Clear(); this.operationParameters.Add(@"namespaceName", namespaceName); this.operationParameters.Add(@"className", className); this.operationParameters.Add(@"methodName", methodName); this.WriteOperationStartMessage(this.operationName, this.operationParameters); - CimAsyncMultipleResults asyncResult = this.session.InvokeMethodAsync(namespaceName, className, methodName, methodParameters, this.options); - string errorSource = String.Format(CultureInfo.CurrentUICulture, "{0}:{1}", namespaceName, className); + CimAsyncMultipleResults asyncResult = this.CimSession.InvokeMethodAsync(namespaceName, className, methodName, methodParameters, this.OperationOptions); + string errorSource = string.Create(CultureInfo.CurrentUICulture, $"{namespaceName}:{className}"); ConsumeCimInvokeMethodResultAsync(asyncResult, className, methodName, new CimResultContext(errorSource)); } @@ -1375,16 +1407,16 @@ public void SubscribeAsync( { DebugHelper.WriteLogEx("QueryDialect = '{0}'; queryExpression = '{1}'", 0, queryDialect, queryExpression); this.CheckAvailability(); - this.targetCimInstance = null; - this.operationName = Strings.CimOperationNameSubscribeIndication; + this.TargetCimInstance = null; + this.operationName = CimCmdletStrings.CimOperationNameSubscribeIndication; this.operationParameters.Clear(); this.operationParameters.Add(@"namespaceName", namespaceName); this.operationParameters.Add(@"queryDialect", queryDialect); this.operationParameters.Add(@"queryExpression", queryExpression); this.WriteOperationStartMessage(this.operationName, this.operationParameters); - this.options.Flags |= CimOperationFlags.ReportOperationStarted; - CimAsyncMultipleResults asyncResult = this.session.SubscribeAsync(namespaceName, queryDialect, queryExpression, this.options); + this.OperationOptions.Flags |= CimOperationFlags.ReportOperationStarted; + CimAsyncMultipleResults asyncResult = this.CimSession.SubscribeAsync(namespaceName, queryDialect, queryExpression, this.OperationOptions); ConsumeCimSubscriptionResultAsync(asyncResult, null); } @@ -1397,8 +1429,8 @@ public void TestConnectionAsync() { DebugHelper.WriteLogEx("Start test connection", 0); this.CheckAvailability(); - this.targetCimInstance = null; - CimAsyncResult asyncResult = this.session.TestConnectionAsync(); + this.TargetCimInstance = null; + CimAsyncResult asyncResult = this.CimSession.TestConnectionAsync(); // ignore the test connection result objects ConsumeCimInstanceAsync(asyncResult, true, null); } @@ -1407,7 +1439,7 @@ public void TestConnectionAsync() #region pre action APIs /// - /// Called before new action event + /// Called before new action event. /// /// protected virtual bool PreNewActionEvent(CmdletActionEventArgs args) @@ -1415,7 +1447,7 @@ protected virtual bool PreNewActionEvent(CmdletActionEventArgs args) return true; } /// - /// Called before operation delete event + /// Called before operation delete event. /// /// protected virtual void PreOperationDeleteEvent(OperationEventArgs args) @@ -1426,21 +1458,21 @@ protected virtual void PreOperationDeleteEvent(OperationEventArgs args) #region post action APIs /// - /// Called after new action event + /// Called after new action event. /// /// protected virtual void PostNewActionEvent(CmdletActionEventArgs args) { } /// - /// Called after operation create event + /// Called after operation create event. /// /// protected virtual void PostOperationCreateEvent(OperationEventArgs args) { } /// - /// Called after operation delete event + /// Called after operation delete event. /// /// protected virtual void PostOperationDeleteEvent(OperationEventArgs args) @@ -1461,41 +1493,17 @@ protected virtual void PostOperationDeleteEvent(OperationEventArgs args) /// The CimSession object managed by this proxy object, /// which is either created by constructor OR passed in by caller. /// The session will be closed while disposing this proxy object - /// if it is created by constuctor. + /// if it is created by constructor. /// - internal CimSession CimSession - { - get - { - return this.session; - } - } - private CimSession session; + internal CimSession CimSession { get; private set; } /// /// The current CimInstance object, against which issued - /// current operation, it could be null + /// current operation, it could be null. /// - internal CimInstance TargetCimInstance - { - get - { - return this.targetCimInstance; - } - } - private CimInstance targetCimInstance = null; + internal CimInstance TargetCimInstance { get; private set; } - /// - /// Flag controls whether session object should be closed or not. - /// - private bool isTemporaryCimSession; - internal bool IsTemporaryCimSession - { - get - { - return isTemporaryCimSession; - } - } + internal bool IsTemporaryCimSession { get; private set; } /// /// The CimOperationOptions object, which specifies the options @@ -1505,14 +1513,7 @@ internal bool IsTemporaryCimSession /// The setting MUST be set before start new operation on the /// this proxy object. /// - internal CimOperationOptions OperationOptions - { - get - { - return this.options; - } - } - private CimOperationOptions options; + internal CimOperationOptions OperationOptions { get; private set; } /// /// All operations completed. @@ -1523,38 +1524,38 @@ private bool Completed } /// - /// lock object used to lock - /// operation & cancelOperation members + /// Lock object used to lock + /// operation & cancelOperation members. /// - private readonly object stateLock = new object(); + private readonly object stateLock = new(); /// - /// the operation issued by cimSession + /// The operation issued by cimSession. /// private IObservable operation; /// - /// the current operation name + /// The current operation name. /// private string operationName; /// - /// the current operation parameters + /// The current operation parameters. /// - private Hashtable operationParameters = new Hashtable(); + private readonly Hashtable operationParameters = new(); /// - /// handler used to cancel operation + /// Handler used to cancel operation. /// private IDisposable _cancelOperation; /// - /// cancelOperation disposed flag + /// CancelOperation disposed flag. /// private int _cancelOperationDisposed = 0; /// - /// Dispose the cancel operation + /// Dispose the cancel operation. /// private void DisposeCancelOperation() { @@ -1571,86 +1572,58 @@ private void DisposeCancelOperation() } /// - /// Set the cancel operation + /// Set the cancel operation. /// private IDisposable CancelOperation { + get + { + return this._cancelOperation; + } + set { DebugHelper.WriteLogEx(); this._cancelOperation = value; Interlocked.Exchange(ref this._cancelOperationDisposed, 0); } - get - { - return this._cancelOperation; - } } /// - /// current protocol name - /// DCOM or WSMAN + /// Current protocol name + /// DCOM or WSMAN. /// - internal ProtocolType Protocol - { - get - { - return protocol; - } - } - private ProtocolType protocol; + internal ProtocolType Protocol { get; private set; } /// - /// cross operation context object + /// Cross operation context object. /// - internal XOperationContextBase ContextObject - { - set - { - this.contextObject = value; - } - get - { - return this.contextObject; - } - } - private XOperationContextBase contextObject; + internal XOperationContextBase ContextObject { get; set; } /// - /// invocation context object + /// Invocation context object. /// private InvocationContext invocationContextObject; /// - /// a preprocess object to pre-processing the result object, + /// A preprocess object to pre-processing the result object, /// for example, adding PSTypeName, etc. /// - internal IObjectPreProcess ObjectPreProcess - { - set - { - this.objectPreprocess = value; - } - get - { - return this.objectPreprocess; - } - } - private IObjectPreProcess objectPreprocess; + internal IObjectPreProcess ObjectPreProcess { get; set; } /// - /// is true if this was + /// is if this was /// created to handle the "default" session, in cases where cmdlets are invoked without /// ComputerName and/or CimSession parameters. /// - private bool isDefaultSession; + private readonly bool isDefaultSession; #endregion #region IDisposable /// - /// IDisposable interface + /// IDisposable interface. /// private int _disposed; @@ -1686,11 +1659,12 @@ protected virtual void Dispose(bool disposing) // Dispose managed resources. this.DisposeCancelOperation(); - if (this.options != null) + if (this.OperationOptions != null) { - this.options.Dispose(); - this.options = null; + this.OperationOptions.Dispose(); + this.OperationOptions = null; } + DisposeTemporaryCimSession(); } } @@ -1711,12 +1685,12 @@ public bool IsDisposed /// private void DisposeTemporaryCimSession() { - if (this.isTemporaryCimSession && this.session != null) + if (this.IsTemporaryCimSession && this.CimSession != null) { // remove the cimsession from temporary cache - RemoveCimSessionFromTemporaryCache(this.session); - this.isTemporaryCimSession = false; - this.session = null; + RemoveCimSessionFromTemporaryCache(this.CimSession); + this.IsTemporaryCimSession = false; + this.CimSession = null; } } #endregion @@ -1752,11 +1726,11 @@ protected void ConsumeCimInstanceAsync( CimResultObserver observer; if (ignoreResultObjects) { - observer = new IgnoreResultObserver(this.session, asyncResult); + observer = new IgnoreResultObserver(this.CimSession, asyncResult); } else { - observer = new CimResultObserver(this.session, asyncResult, cimResultContext); + observer = new CimResultObserver(this.CimSession, asyncResult, cimResultContext); } observer.OnNewResult += this.ResultEventHandler; @@ -1776,8 +1750,8 @@ protected void ConsumeCimInstanceAsync( protected void ConsumeObjectAsync(IObservable asyncResult, CimResultContext cimResultContext) { - CimResultObserver observer = new CimResultObserver( - this.session, asyncResult, cimResultContext); + CimResultObserver observer = new( + this.CimSession, asyncResult, cimResultContext); observer.OnNewResult += this.ResultEventHandler; this.operationID = Interlocked.Increment(ref gOperationCounter); @@ -1797,8 +1771,8 @@ protected void ConsumeObjectAsync(IObservable asyncResult, protected void ConsumeCimClassAsync(IObservable asyncResult, CimResultContext cimResultContext) { - CimResultObserver observer = new CimResultObserver( - this.session, asyncResult, cimResultContext); + CimResultObserver observer = new( + this.CimSession, asyncResult, cimResultContext); observer.OnNewResult += this.ResultEventHandler; this.operationID = Interlocked.Increment(ref gOperationCounter); @@ -1818,8 +1792,8 @@ protected void ConsumeCimSubscriptionResultAsync( IObservable asyncResult, CimResultContext cimResultContext) { - CimSubscriptionResultObserver observer = new CimSubscriptionResultObserver( - this.session, asyncResult, cimResultContext); + CimSubscriptionResultObserver observer = new( + this.CimSession, asyncResult, cimResultContext); observer.OnNewResult += this.ResultEventHandler; this.operationID = Interlocked.Increment(ref gOperationCounter); this.AddOperation(asyncResult); @@ -1838,15 +1812,15 @@ protected void ConsumeCimSubscriptionResultAsync( /// protected void ConsumeCimInvokeMethodResultAsync( IObservable asyncResult, - String className, - String methodName, + string className, + string methodName, CimResultContext cimResultContext) { - CimMethodResultObserver observer = new CimMethodResultObserver(this.session, asyncResult, cimResultContext) - { - ClassName = className, - MethodName = methodName - }; + CimMethodResultObserver observer = new(this.CimSession, asyncResult, cimResultContext) + { + ClassName = className, + MethodName = methodName + }; observer.OnNewResult += this.ResultEventHandler; this.operationID = Interlocked.Increment(ref gOperationCounter); @@ -1869,10 +1843,11 @@ private void CheckAvailability() { if (!this.Completed) { - throw new InvalidOperationException(Strings.OperationInProgress); + throw new InvalidOperationException(CimCmdletStrings.OperationInProgress); } } - DebugHelper.WriteLog("KeyOnly {0},", 1, this.options.KeysOnly); + + DebugHelper.WriteLog("KeyOnly {0},", 1, this.OperationOptions.KeysOnly); } /// @@ -1882,9 +1857,9 @@ private void CheckAvailability() /// private void AssertSession() { - if (this.IsDisposed || (this.session == null)) + if (this.IsDisposed || (this.CimSession == null)) { - DebugHelper.WriteLogEx("Invalid CimSessionProxy object, disposed? {0}; session object {1}", 1, this.IsDisposed, this.session); + DebugHelper.WriteLogEx("Invalid CimSessionProxy object, disposed? {0}; session object {1}", 1, this.IsDisposed, this.CimSession); throw new ObjectDisposedException(this.ToString()); } } @@ -1903,7 +1878,7 @@ private CimSession CreateCimSessionByComputerName(string computerName) if (option is DComSessionOptions) { DebugHelper.WriteLog("Create dcom cimSession"); - this.protocol = ProtocolType.Dcom; + this.Protocol = ProtocolType.Dcom; return CimSession.Create(ConstValue.NullComputerName, option); } else @@ -1924,7 +1899,7 @@ private CimSession CreateCimSessionByComputerName(string computerName) /// /// internal static CimSessionOptions CreateCimSessionOption(string computerName, - UInt32 timeout, CimCredential credential) + uint timeout, CimCredential credential) { DebugHelper.WriteLogEx(); @@ -1939,21 +1914,24 @@ internal static CimSessionOptions CreateCimSessionOption(string computerName, DebugHelper.WriteLog("<<<<<<<<<< Use protocol WSMAN {0}", 1, computerName); option = new WSManSessionOptions(); } + if (timeout != 0) { option.Timeout = TimeSpan.FromSeconds((double)timeout); } + if (credential != null) { option.AddDestinationCredentials(credential); } + DebugHelper.WriteLogEx("returned option :{0}.", 1, option); return option; } #endregion - }//End Class + } #region class CimSessionProxyTestConnection /// @@ -1966,10 +1944,13 @@ internal class CimSessionProxyTestConnection : CimSessionProxy #region constructors /// + /// Initializes a new instance of the class. + /// + /// /// Create by given computer name /// and session options. /// Then create wrapper object. - /// + /// /// /// public CimSessionProxyTestConnection(string computerName, CimSessionOptions sessionOptions) @@ -1982,7 +1963,7 @@ public CimSessionProxyTestConnection(string computerName, CimSessionOptions sess #region pre action APIs /// - /// Called after operation delete event + /// Called after operation delete event. /// /// protected override void PreOperationDeleteEvent(OperationEventArgs args) @@ -1992,7 +1973,7 @@ protected override void PreOperationDeleteEvent(OperationEventArgs args) if (args.success) { // test connection success, write session object to pipeline - CimWriteResultObject result = new CimWriteResultObject(this.CimSession, this.ContextObject); + CimWriteResultObject result = new(this.CimSession, this.ContextObject); this.FireNewActionEvent(result); } } @@ -2015,9 +1996,12 @@ internal class CimSessionProxyGetCimClass : CimSessionProxy #region constructors /// + /// Initializes a new instance of the class. + /// + /// /// Create by given computer name. /// Then create wrapper object. - /// + /// /// public CimSessionProxyGetCimClass(string computerName) : base(computerName) @@ -2025,10 +2009,13 @@ public CimSessionProxyGetCimClass(string computerName) } /// + /// Initializes a new instance of the class. + /// + /// /// Create by given computer name /// and session options. /// Then create wrapper object. - /// + /// /// /// public CimSessionProxyGetCimClass(CimSession session) @@ -2040,22 +2027,21 @@ public CimSessionProxyGetCimClass(CimSession session) #region pre action APIs /// - /// Called before new action event + /// Called before new action event. /// /// protected override bool PreNewActionEvent(CmdletActionEventArgs args) { DebugHelper.WriteLogEx(); - if (!(args.Action is CimWriteResultObject)) + if (args.Action is not CimWriteResultObject) { // allow all other actions return true; } CimWriteResultObject writeResultObject = args.Action as CimWriteResultObject; - CimClass cimClass = writeResultObject.Result as CimClass; - if (cimClass == null) + if (writeResultObject.Result is not CimClass cimClass) { return true; } @@ -2074,12 +2060,13 @@ protected override bool PreNewActionEvent(CmdletActionEventArgs args) return false; } } + if (context.PropertyName != null) { - pattern = new WildcardPattern(context.PropertyName, WildcardOptions.IgnoreCase); bool match = false; if (cimClass.CimClassProperties != null) { + pattern = new WildcardPattern(context.PropertyName, WildcardOptions.IgnoreCase); foreach (CimPropertyDeclaration decl in cimClass.CimClassProperties) { DebugHelper.WriteLog("--- property name : {0}", 1, decl.Name); @@ -2097,12 +2084,13 @@ protected override bool PreNewActionEvent(CmdletActionEventArgs args) return match; } } + if (context.MethodName != null) { - pattern = new WildcardPattern(context.MethodName, WildcardOptions.IgnoreCase); bool match = false; if (cimClass.CimClassMethods != null) { + pattern = new WildcardPattern(context.MethodName, WildcardOptions.IgnoreCase); foreach (CimMethodDeclaration decl in cimClass.CimClassMethods) { DebugHelper.WriteLog("--- method name : {0}", 1, decl.Name); @@ -2120,12 +2108,13 @@ protected override bool PreNewActionEvent(CmdletActionEventArgs args) return match; } } + if (context.QualifierName != null) { - pattern = new WildcardPattern(context.QualifierName, WildcardOptions.IgnoreCase); bool match = false; if (cimClass.CimClassQualifiers != null) { + pattern = new WildcardPattern(context.QualifierName, WildcardOptions.IgnoreCase); foreach (CimQualifier qualifier in cimClass.CimClassQualifiers) { DebugHelper.WriteLog("--- qualifier name : {0}", 1, qualifier.Name); @@ -2143,6 +2132,7 @@ protected override bool PreNewActionEvent(CmdletActionEventArgs args) return match; } } + DebugHelper.WriteLog("CimClass '{0}' is qualified.", 1, cimClass.CimSystemProperties.ClassName); return true; } @@ -2163,49 +2153,54 @@ internal class CimSessionProxyNewCimInstance : CimSessionProxy #region constructors /// + /// Initializes a new instance of the class. + /// + /// /// Create by given computer name. /// Then create wrapper object. - /// - /// + /// public CimSessionProxyNewCimInstance(string computerName, CimNewCimInstance operation) : base(computerName) { - this.newCimInstance = operation; + this.NewCimInstanceOperation = operation; } /// + /// Initializes a new instance of the class. + /// + /// + /// /// Create by given computer name /// and session options. /// Then create wrapper object. - /// + /// /// /// public CimSessionProxyNewCimInstance(CimSession session, CimNewCimInstance operation) : base(session) { - this.newCimInstance = operation; + this.NewCimInstanceOperation = operation; } #endregion #region pre action APIs /// - /// Called before new action event + /// Called before new action event. /// /// protected override bool PreNewActionEvent(CmdletActionEventArgs args) { DebugHelper.WriteLogEx(); - if (!(args.Action is CimWriteResultObject)) + if (args.Action is not CimWriteResultObject) { // allow all other actions return true; } CimWriteResultObject writeResultObject = args.Action as CimWriteResultObject; - CimInstance cimInstance = writeResultObject.Result as CimInstance; - if (cimInstance == null) + if (writeResultObject.Result is not CimInstance cimInstance) { return true; } @@ -2218,14 +2213,7 @@ protected override bool PreNewActionEvent(CmdletActionEventArgs args) #region private members - private CimNewCimInstance newCimInstance = null; - internal CimNewCimInstance NewCimInstanceOperation - { - get - { - return this.newCimInstance; - } - } + internal CimNewCimInstance NewCimInstanceOperation { get; } #endregion } @@ -2243,11 +2231,14 @@ internal class CimSessionProxySetCimInstance : CimSessionProxy { #region constructors /// + /// Initializes a new instance of the class. + /// + /// /// Create by given object. /// Then create wrapper object. - /// + /// /// object to clone. - /// PassThru, true means output the modified instance; otherwise does not output + /// PassThru, true means output the modified instance; otherwise does not output. public CimSessionProxySetCimInstance(CimSessionProxy originalProxy, bool passThru) : base(originalProxy) { @@ -2255,9 +2246,12 @@ public CimSessionProxySetCimInstance(CimSessionProxy originalProxy, bool passThr } /// + /// Initializes a new instance of the class. + /// + /// /// Create by given computer name. /// Then create wrapper object. - /// + /// /// /// /// @@ -2270,10 +2264,13 @@ public CimSessionProxySetCimInstance(string computerName, } /// + /// Initializes a new instance of the class. + /// + /// /// Create by given computer name /// and session options. /// Then create wrapper object. - /// + /// /// /// public CimSessionProxySetCimInstance(CimSession session, bool passThru) @@ -2285,7 +2282,7 @@ public CimSessionProxySetCimInstance(CimSession session, bool passThru) #region pre action APIs /// - /// Called before new action event + /// Called before new action event. /// /// protected override bool PreNewActionEvent(CmdletActionEventArgs args) @@ -2305,12 +2302,12 @@ protected override bool PreNewActionEvent(CmdletActionEventArgs args) #region private members /// - /// Ture indicates need to output the modified result + /// Ture indicates need to output the modified result. /// - private bool passThru = false; + private readonly bool passThru = false; #endregion } #endregion -}//End namespace +} diff --git a/src/Microsoft.Management.Infrastructure.CimCmdlets/CimSetCimInstance.cs b/src/Microsoft.Management.Infrastructure.CimCmdlets/CimSetCimInstance.cs index 57ddb857cc2..91cf332bde9 100644 --- a/src/Microsoft.Management.Infrastructure.CimCmdlets/CimSetCimInstance.cs +++ b/src/Microsoft.Management.Infrastructure.CimCmdlets/CimSetCimInstance.cs @@ -1,16 +1,12 @@ -/*============================================================================ - * Copyright (C) Microsoft Corporation, All rights reserved. - *============================================================================ - */ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. #region Using directives -using System.Collections; using System; +using System.Collections; using System.Collections.Generic; using System.Globalization; -using System.Management.Automation; - #endregion @@ -23,9 +19,7 @@ namespace Microsoft.Management.Infrastructure.CimCmdlets internal class CimSetCimInstanceContext : XOperationContextBase { /// - /// - /// Constructor - /// + /// Initializes a new instance of the class. /// /// /// @@ -37,47 +31,26 @@ internal CimSetCimInstanceContext(string theNamespace, bool passThru) { this.proxy = theProxy; - this.property = theProperty; + this.Property = theProperty; this.nameSpace = theNamespace; - this.parameterSetName = theParameterSetName; - this.passThru = passThru; + this.ParameterSetName = theParameterSetName; + this.PassThru = passThru; } /// /// property value /// - internal IDictionary Property - { - get - { - return this.property; - } - } - private IDictionary property; + internal IDictionary Property { get; } /// /// parameter set name /// - internal string ParameterSetName - { - get - { - return this.parameterSetName; - } - } - private string parameterSetName; + internal string ParameterSetName { get; } /// /// PassThru value /// - internal bool PassThru - { - get - { - return this.passThru; - } - } - private bool passThru; + internal bool PassThru { get; } } /// @@ -88,9 +61,7 @@ internal bool PassThru internal sealed class CimSetCimInstance : CimGetInstance { /// - /// - /// Constructor - /// + /// Initializes a new instance of the class. /// public CimSetCimInstance() : base() @@ -102,12 +73,12 @@ public CimSetCimInstance() /// Base on parametersetName to set ciminstances /// /// - /// object + /// object. public void SetCimInstance(SetCimInstanceCommand cmdlet) { IEnumerable computerNames = ConstValue.GetComputerNames( GetComputerName(cmdlet)); - List proxys = new List(); + List proxys = new(); switch (cmdlet.ParameterSetName) { case CimBaseCommand.CimInstanceComputerSet: @@ -116,6 +87,7 @@ public void SetCimInstance(SetCimInstanceCommand cmdlet) // create CimSessionProxySetCimInstance object internally proxys.Add(CreateSessionProxy(computerName, cmdlet.CimInstance, cmdlet, cmdlet.PassThru)); } + break; case CimBaseCommand.CimInstanceSessionSet: foreach (CimSession session in GetCimSession(cmdlet)) @@ -123,10 +95,12 @@ public void SetCimInstance(SetCimInstanceCommand cmdlet) // create CimSessionProxySetCimInstance object internally proxys.Add(CreateSessionProxy(session, cmdlet, cmdlet.PassThru)); } + break; default: break; } + switch (cmdlet.ParameterSetName) { case CimBaseCommand.CimInstanceComputerSet: @@ -151,8 +125,10 @@ public void SetCimInstance(SetCimInstanceCommand cmdlet) return; } } + proxy.ModifyInstanceAsync(nameSpace, instance); } + break; case CimBaseCommand.QueryComputerSet: case CimBaseCommand.QuerySessionSet: @@ -184,6 +160,7 @@ public void SetCimInstance(CimInstance cimInstance, CimSetCimInstanceContext con cmdlet.ThrowTerminatingError(exception, action); return; } + CimSessionProxy proxy = CreateCimSessionProxy(context.Proxy, context.PassThru); proxy.ModifyInstanceAsync(cimInstance.CimSystemProperties.Namespace, cimInstance); } @@ -208,6 +185,7 @@ private bool SetProperty(IDictionary properties, ref CimInstance cimInstance, re // simply ignore if empty properties was provided return true; } + IDictionaryEnumerator enumerator = properties.GetEnumerator(); while (enumerator.MoveNext()) { @@ -224,8 +202,8 @@ private bool SetProperty(IDictionary properties, ref CimInstance cimInstance, re if ((property.Flags & CimFlags.ReadOnly) == CimFlags.ReadOnly) { // can not modify ReadOnly property - exception = new CimException(String.Format(CultureInfo.CurrentUICulture, - Strings.CouldNotModifyReadonlyProperty, key, cimInstance)); + exception = new CimException(string.Format(CultureInfo.CurrentUICulture, + CimCmdletStrings.CouldNotModifyReadonlyProperty, key, cimInstance)); return false; } // allow modify the key property value as long as it is not readonly, @@ -236,7 +214,7 @@ private bool SetProperty(IDictionary properties, ref CimInstance cimInstance, re else // For dynamic instance, it is valid to add a new property { CimProperty newProperty; - if( value == null ) + if (value == null) { newProperty = CimProperty.Create( key, @@ -264,6 +242,7 @@ private bool SetProperty(IDictionary properties, ref CimInstance cimInstance, re CimFlags.Property); } } + try { cimInstance.CimInstanceProperties.Add(newProperty); @@ -272,8 +251,8 @@ private bool SetProperty(IDictionary properties, ref CimInstance cimInstance, re { if (e.NativeErrorCode == NativeErrorCode.Failed) { - string errorMessage = String.Format(CultureInfo.CurrentUICulture, - Strings.UnableToAddPropertyToInstance, + string errorMessage = string.Format(CultureInfo.CurrentUICulture, + CimCmdletStrings.UnableToAddPropertyToInstance, newProperty.Name, cimInstance); exception = new CimException(errorMessage, e); @@ -282,8 +261,10 @@ private bool SetProperty(IDictionary properties, ref CimInstance cimInstance, re { exception = e; } + return false; } + DebugHelper.WriteLog("Add non-key property name '{0}' with value '{1}'.", 3, key, value); } } @@ -294,6 +275,7 @@ private bool SetProperty(IDictionary properties, ref CimInstance cimInstance, re return false; } } + return true; } @@ -301,9 +283,9 @@ private bool SetProperty(IDictionary properties, ref CimInstance cimInstance, re #region const strings /// - /// action + /// Action. /// private const string action = @"Set-CimInstance"; #endregion - }//End Class -}//End namespace + } +} diff --git a/src/Microsoft.Management.Infrastructure.CimCmdlets/CimWriteError.cs b/src/Microsoft.Management.Infrastructure.CimCmdlets/CimWriteError.cs index 97c45cd77ce..f1ebe911603 100644 --- a/src/Microsoft.Management.Infrastructure.CimCmdlets/CimWriteError.cs +++ b/src/Microsoft.Management.Infrastructure.CimCmdlets/CimWriteError.cs @@ -1,7 +1,5 @@ -/*============================================================================ - * Copyright (C) Microsoft Corporation, All rights reserved. - *============================================================================ - */ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. #region Using directives @@ -31,8 +29,8 @@ internal sealed class ErrorToErrorRecord /// /// /// - /// the context starting the operation, which generated the error - /// the CimResultContext used to provide ErrorSource, etc. info. + /// The context starting the operation, which generated the error. + /// The CimResultContext used to provide ErrorSource, etc. info. /// internal static ErrorRecord ErrorRecordFromAnyException( InvocationContext context, @@ -41,17 +39,15 @@ internal static ErrorRecord ErrorRecordFromAnyException( { Debug.Assert(inner != null, "Caller should verify inner != null"); - CimException cimException = inner as CimException; - if (cimException != null) + if (inner is CimException cimException) { return CreateFromCimException(context, cimException, cimResultContext); } - var containsErrorRecord = inner as IContainsErrorRecord; - if (containsErrorRecord != null) + if (inner is IContainsErrorRecord containsErrorRecord) { return InitializeErrorRecord(context, - exception : inner, + exception: inner, errorId: "CimCmdlet_" + containsErrorRecord.ErrorRecord.FullyQualifiedErrorId, errorCategory: containsErrorRecord.ErrorRecord.CategoryInfo.Category, cimResultContext: cimResultContext); @@ -59,7 +55,7 @@ internal static ErrorRecord ErrorRecordFromAnyException( else { return InitializeErrorRecord(context, - exception :inner, + exception: inner, errorId: "CimCmdlet_" + inner.GetType().Name, errorCategory: ErrorCategory.NotSpecified, cimResultContext: cimResultContext); @@ -72,7 +68,7 @@ internal static ErrorRecord ErrorRecordFromAnyException( /// /// /// - /// the CimResultContext used to provide ErrorSource, etc. info. + /// The CimResultContext used to provide ErrorSource, etc. info. /// internal static ErrorRecord CreateFromCimException( InvocationContext context, @@ -91,7 +87,7 @@ internal static ErrorRecord CreateFromCimException( /// /// /// - /// the CimResultContext used to provide ErrorSource, etc. info. + /// The CimResultContext used to provide ErrorSource, etc. info. /// internal static ErrorRecord InitializeErrorRecord( InvocationContext context, @@ -113,7 +109,7 @@ internal static ErrorRecord InitializeErrorRecord( /// /// /// - /// the CimResultContext used to provide ErrorSource, etc. info. + /// The CimResultContext used to provide ErrorSource, etc. info. /// internal static ErrorRecord InitializeErrorRecord( InvocationContext context, @@ -131,6 +127,7 @@ internal static ErrorRecord InitializeErrorRecord( { errorRecord.CategoryInfo.TargetName = cimException.ErrorSource; } + return errorRecord; } @@ -141,7 +138,7 @@ internal static ErrorRecord InitializeErrorRecord( /// /// /// - /// the CimResultContext used to provide ErrorSource, etc. info. + /// The CimResultContext used to provide ErrorSource, etc. info. /// internal static ErrorRecord InitializeErrorRecordCore( InvocationContext context, @@ -155,6 +152,7 @@ internal static ErrorRecord InitializeErrorRecordCore( { theTargetObject = cimResultContext.ErrorSource; } + if (theTargetObject == null) { if (context != null) @@ -165,7 +163,8 @@ internal static ErrorRecord InitializeErrorRecordCore( } } } - ErrorRecord coreErrorRecord = new ErrorRecord( + + ErrorRecord coreErrorRecord = new( exception: exception, errorId: errorId, errorCategory: errorCategory, @@ -176,7 +175,7 @@ internal static ErrorRecord InitializeErrorRecordCore( return coreErrorRecord; } - System.Management.Automation.Remoting.OriginInfo originInfo = new System.Management.Automation.Remoting.OriginInfo( + System.Management.Automation.Remoting.OriginInfo originInfo = new( context.ComputerName, Guid.Empty); @@ -185,8 +184,6 @@ internal static ErrorRecord InitializeErrorRecordCore( originInfo); DebugHelper.WriteLogEx("Created RemotingErrorRecord.", 0); - // errorRecord.SetInvocationInfo(jobContext.CmdletInvocationInfo); - // errorRecord.PreserveInvocationInfoOnce = true; return errorRecord; } @@ -318,24 +315,26 @@ internal static ErrorCategory ConvertCimErrorToErrorCategory(CimInstance cimErro internal sealed class CimWriteError : CimSyncAction { /// - /// Constructor with an error + /// Initializes a new instance of the class + /// with the specified . /// /// public CimWriteError(CimInstance error, InvocationContext context) { - this.error = error; - this.invocationContext = context; + this.Error = error; + this.CimInvocationContext = context; } /// - /// Construct with an exception object + /// Initializes a new instance of the class + /// with the specified . /// /// public CimWriteError(Exception exception, InvocationContext context, CimResultContext cimResultContext) { - this.exception = exception; - this.invocationContext = context; - this.cimResultContext = cimResultContext; + this.Exception = exception; + this.CimInvocationContext = context; + this.ResultContext = cimResultContext; } /// @@ -349,10 +348,10 @@ public override void Execute(CmdletOperationBase cmdlet) Debug.Assert(cmdlet != null, "Caller should verify that cmdlet != null"); try { - Exception errorException = (error != null) ? new CimException(error) : this.Exception; + Exception errorException = (Error != null) ? new CimException(Error) : this.Exception; // PS engine takes care of handling error action - cmdlet.WriteError(ErrorToErrorRecord.ErrorRecordFromAnyException(this.invocationContext, errorException, this.cimResultContext)); + cmdlet.WriteError(ErrorToErrorRecord.ErrorRecordFromAnyException(this.CimInvocationContext, errorException, this.ResultContext)); // if user wants to continue, we will get here this.responseType = CimResponseType.Yes; @@ -376,60 +375,20 @@ public override void Execute(CmdletOperationBase cmdlet) /// Error instance /// /// - private CimInstance error; - internal CimInstance Error - { - get - { - return error; - } - } + internal CimInstance Error { get; } /// /// /// Exception object /// /// - internal Exception Exception - { - get - { - return exception; - } - } - private Exception exception; + internal Exception Exception { get; } - /// - /// - /// object that contains - /// the information while issuing the current operation - /// - /// - private InvocationContext invocationContext; + internal InvocationContext CimInvocationContext { get; } - internal InvocationContext CimInvocationContext - { - get - { - return invocationContext; - } - } - - - /// - /// - /// - private CimResultContext cimResultContext; - - internal CimResultContext ResultContext - { - get - { - return cimResultContext; - } - } + internal CimResultContext ResultContext { get; } #endregion - }//End Class -}//End namespace + } +} diff --git a/src/Microsoft.Management.Infrastructure.CimCmdlets/CimWriteMessage.cs b/src/Microsoft.Management.Infrastructure.CimCmdlets/CimWriteMessage.cs index b224364bfcc..f4b244b97f9 100644 --- a/src/Microsoft.Management.Infrastructure.CimCmdlets/CimWriteMessage.cs +++ b/src/Microsoft.Management.Infrastructure.CimCmdlets/CimWriteMessage.cs @@ -1,7 +1,5 @@ -/*============================================================================ - * Copyright (C) Microsoft Corporation, All rights reserved. - *============================================================================ - */ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. #region Using directives @@ -22,38 +20,26 @@ internal sealed class CimWriteMessage : CimBaseAction #region members /// - /// channel id + /// Channel id. /// - private UInt32 channel; - - /// - /// message to write to the channel - /// - private string message; #endregion #region Properties - internal UInt32 Channel - { - get { return channel; } - } + internal uint Channel { get; } - internal string Message - { - get { return message; } - } + internal string Message { get; } #endregion /// - /// Constructor method. + /// Initializes a new instance of the class. /// - public CimWriteMessage(UInt32 channel, + public CimWriteMessage(uint channel, string message) { - this.channel = channel; - this.message = message; + this.Channel = channel; + this.Message = message; } /// @@ -66,20 +52,20 @@ public override void Execute(CmdletOperationBase cmdlet) { ValidationHelper.ValidateNoNullArgument(cmdlet, "cmdlet"); - switch ((CimWriteMessageChannel)channel) + switch ((CimWriteMessageChannel)Channel) { case CimWriteMessageChannel.Verbose: - cmdlet.WriteVerbose(message); + cmdlet.WriteVerbose(Message); break; case CimWriteMessageChannel.Warning: - cmdlet.WriteWarning(message); + cmdlet.WriteWarning(Message); break; case CimWriteMessageChannel.Debug: - cmdlet.WriteDebug(message); + cmdlet.WriteDebug(Message); break; default: break; } } - }//End Class -}//End namespace + } +} diff --git a/src/Microsoft.Management.Infrastructure.CimCmdlets/CimWriteProgress.cs b/src/Microsoft.Management.Infrastructure.CimCmdlets/CimWriteProgress.cs index 08eb6acc877..b99407ccc81 100644 --- a/src/Microsoft.Management.Infrastructure.CimCmdlets/CimWriteProgress.cs +++ b/src/Microsoft.Management.Infrastructure.CimCmdlets/CimWriteProgress.cs @@ -1,7 +1,5 @@ -/*============================================================================ - * Copyright (C) Microsoft Corporation, All rights reserved. - *============================================================================ - */ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. #region Using directives @@ -20,7 +18,7 @@ namespace Microsoft.Management.Infrastructure.CimCmdlets internal sealed class CimWriteProgress : CimBaseAction { /// - /// Constructor + /// Initializes a new instance of the class. /// /// /// Activity identifier of the given activity @@ -42,22 +40,23 @@ public CimWriteProgress( int theActivityID, string theCurrentOperation, string theStatusDescription, - UInt32 thePercentageCompleted, - UInt32 theSecondsRemaining) + uint thePercentageCompleted, + uint theSecondsRemaining) { - this.activity = theActivity; - this.activityID = theActivityID; - this.currentOperation = theCurrentOperation; - if (String.IsNullOrEmpty(theStatusDescription)) + this.Activity = theActivity; + this.ActivityID = theActivityID; + this.CurrentOperation = theCurrentOperation; + if (string.IsNullOrEmpty(theStatusDescription)) { - this.statusDescription = Strings.DefaultStatusDescription; + this.StatusDescription = CimCmdletStrings.DefaultStatusDescription; } else { - this.statusDescription = theStatusDescription; + this.StatusDescription = theStatusDescription; } - this.percentageCompleted = thePercentageCompleted; - this.secondsRemaining = theSecondsRemaining; + + this.PercentageCompleted = thePercentageCompleted; + this.SecondsRemaining = theSecondsRemaining; } /// @@ -71,85 +70,55 @@ public override void Execute(CmdletOperationBase cmdlet) DebugHelper.WriteLog( "...Activity {0}: id={1}, remain seconds ={2}, percentage completed = {3}", 4, - this.activity, - this.activityID, - this.secondsRemaining, - this.percentageCompleted); + this.Activity, + this.ActivityID, + this.SecondsRemaining, + this.PercentageCompleted); ValidationHelper.ValidateNoNullArgument(cmdlet, "cmdlet"); - ProgressRecord record = new ProgressRecord( - this.activityID, - this.activity, - this.statusDescription); - record.Activity = this.activity; + ProgressRecord record = new( + this.ActivityID, + this.Activity, + this.StatusDescription); + record.Activity = this.Activity; record.ParentActivityId = 0; - record.SecondsRemaining = (int)this.secondsRemaining; - record.PercentComplete = (int)this.percentageCompleted; + record.SecondsRemaining = (int)this.SecondsRemaining; + record.PercentComplete = (int)this.PercentageCompleted; cmdlet.WriteProgress(record); } #region members /// - /// Activity of the given activity + /// Gets the activity of the given activity. /// - private string activity; + internal string Activity { get; } /// - /// Activity identifier of the given activity + /// Gets the activity identifier of the given activity. /// - private int activityID; + internal int ActivityID { get; } /// - /// current operation text of the given activity + /// Gets the current operation text of the given activity. /// - private string currentOperation; + internal string CurrentOperation { get; } /// - /// status description of the given activity + /// Gets the status description of the given activity. /// - private string statusDescription; + internal string StatusDescription { get; } /// - /// percentage completed of the given activity + /// Gets the percentage completed of the given activity. /// - private UInt32 percentageCompleted; + internal uint PercentageCompleted { get; } /// - /// how many seconds remained for the given activity + /// Gets the number of seconds remaining for the given activity. /// - private UInt32 secondsRemaining; - - internal string Activity - { - get { return activity; } - } - - internal int ActivityID - { - get { return activityID; } - } - - internal string CurrentOperation - { - get { return currentOperation; } - } - - internal string StatusDescription - { - get { return statusDescription; } - } - - internal UInt32 PercentageCompleted - { - get { return percentageCompleted; } - } - - internal UInt32 SecondsRemaining - { - get { return secondsRemaining; } - } + internal uint SecondsRemaining { get; } #endregion - }//End Class -}//End namespace + } +} diff --git a/src/Microsoft.Management.Infrastructure.CimCmdlets/CimWriteResultObject.cs b/src/Microsoft.Management.Infrastructure.CimCmdlets/CimWriteResultObject.cs index b060a728c01..a4dcdfaa5c4 100644 --- a/src/Microsoft.Management.Infrastructure.CimCmdlets/CimWriteResultObject.cs +++ b/src/Microsoft.Management.Infrastructure.CimCmdlets/CimWriteResultObject.cs @@ -1,7 +1,5 @@ -/*============================================================================ - * Copyright (C) Microsoft Corporation, All rights reserved. - *============================================================================ - */ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. #region Using directives @@ -17,11 +15,11 @@ namespace Microsoft.Management.Infrastructure.CimCmdlets internal sealed class CimWriteResultObject : CimBaseAction { /// - /// Constructor + /// Initializes a new instance of the class. /// public CimWriteResultObject(object result, XOperationContextBase theContext) { - this.result = result; + this.Result = result; this.Context = theContext; } @@ -34,22 +32,14 @@ public CimWriteResultObject(object result, XOperationContextBase theContext) public override void Execute(CmdletOperationBase cmdlet) { ValidationHelper.ValidateNoNullArgument(cmdlet, "cmdlet"); - cmdlet.WriteObject(result, this.Context); + cmdlet.WriteObject(Result, this.Context); } #region members /// - /// result object + /// Result object. /// - internal object Result - { - get - { - return result; - } - } - private object result; + internal object Result { get; } #endregion - }//End Class - -}//End namespace + } +} diff --git a/src/Microsoft.Management.Infrastructure.CimCmdlets/CmdletOperation.cs b/src/Microsoft.Management.Infrastructure.CimCmdlets/CmdletOperation.cs index 9d770ee22da..bd1a2751622 100644 --- a/src/Microsoft.Management.Infrastructure.CimCmdlets/CmdletOperation.cs +++ b/src/Microsoft.Management.Infrastructure.CimCmdlets/CmdletOperation.cs @@ -1,12 +1,9 @@ -/*============================================================================ - * Copyright (C) Microsoft Corporation, All rights reserved. - *============================================================================ - */ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. #region Using directives -using System.Management.Automation; using System; -using System.Globalization; +using System.Management.Automation; #endregion @@ -42,58 +39,73 @@ public virtual bool ShouldContinue(string query, string caption) { return cmdlet.ShouldContinue(query, caption); } + public virtual bool ShouldContinue(string query, string caption, ref bool yesToAll, ref bool noToAll) { return cmdlet.ShouldContinue(query, caption, ref yesToAll, ref noToAll); } + public virtual bool ShouldProcess(string target) { return cmdlet.ShouldProcess(target); } + public virtual bool ShouldProcess(string target, string action) { return cmdlet.ShouldProcess(target, action); } + public virtual bool ShouldProcess(string verboseDescription, string verboseWarning, string caption) { return cmdlet.ShouldProcess(verboseDescription, verboseWarning, caption); } + public virtual bool ShouldProcess(string verboseDescription, string verboseWarning, string caption, out ShouldProcessReason shouldProcessReason) { return cmdlet.ShouldProcess(verboseDescription, verboseWarning, caption, out shouldProcessReason); } + + [System.Diagnostics.CodeAnalysis.DoesNotReturn] public virtual void ThrowTerminatingError(ErrorRecord errorRecord) { cmdlet.ThrowTerminatingError(errorRecord); } + public virtual void WriteCommandDetail(string text) { cmdlet.WriteCommandDetail(text); } + public virtual void WriteDebug(string text) { cmdlet.WriteDebug(text); } + public virtual void WriteError(ErrorRecord errorRecord) { cmdlet.WriteError(errorRecord); } + public virtual void WriteObject(object sendToPipeline, XOperationContextBase context) { cmdlet.WriteObject(sendToPipeline); } + public virtual void WriteObject(object sendToPipeline, bool enumerateCollection, XOperationContextBase context) { cmdlet.WriteObject(sendToPipeline, enumerateCollection); } + public virtual void WriteProgress(ProgressRecord progressRecord) { cmdlet.WriteProgress(progressRecord); } + public virtual void WriteVerbose(string text) { cmdlet.WriteVerbose(text); } + public virtual void WriteWarning(string text) { cmdlet.WriteWarning(text); @@ -104,22 +116,23 @@ public virtual void WriteWarning(string text) /// Throw terminating error /// /// + [System.Diagnostics.CodeAnalysis.DoesNotReturn] internal void ThrowTerminatingError(Exception exception, string operation) { - ErrorRecord errorRecord = new ErrorRecord(exception, operation, ErrorCategory.InvalidOperation, this); + ErrorRecord errorRecord = new(exception, operation, ErrorCategory.InvalidOperation, this); cmdlet.ThrowTerminatingError(errorRecord); } #endregion /// - /// Constructor method. + /// Initializes a new instance of the class. /// public CmdletOperationBase(Cmdlet cmdlet) { ValidationHelper.ValidateNoNullArgument(cmdlet, "cmdlet"); this.cmdlet = cmdlet; } - }//End Class + } #region Class CmdletOperationRemoveCimInstance @@ -132,7 +145,7 @@ public CmdletOperationBase(Cmdlet cmdlet) internal class CmdletOperationRemoveCimInstance : CmdletOperationBase { /// - /// Constructor method + /// Initializes a new instance of the class. /// /// public CmdletOperationRemoveCimInstance(Cmdlet cmdlet, @@ -176,12 +189,12 @@ public override void WriteObject(object sendToPipeline, bool enumerateCollection #region private methods - private CimRemoveCimInstance removeCimInstance; + private readonly CimRemoveCimInstance removeCimInstance; private const string cimRemoveCimInstanceParameterName = @"cimRemoveCimInstance"; #endregion - }//End Class + } #endregion @@ -196,7 +209,7 @@ public override void WriteObject(object sendToPipeline, bool enumerateCollection internal class CmdletOperationSetCimInstance : CmdletOperationBase { /// - /// Constructor method + /// Initializes a new instance of the class. /// /// public CmdletOperationSetCimInstance(Cmdlet cmdlet, @@ -219,11 +232,10 @@ public override void WriteObject(object sendToPipeline, XOperationContextBase co if (sendToPipeline is CimInstance) { - CimSetCimInstanceContext setContext = context as CimSetCimInstanceContext; - if (setContext != null) + if (context is CimSetCimInstanceContext setContext) { - if ((String.Compare(setContext.ParameterSetName, CimBaseCommand.QueryComputerSet, StringComparison.OrdinalIgnoreCase) == 0) || - (String.Compare(setContext.ParameterSetName, CimBaseCommand.QuerySessionSet, StringComparison.OrdinalIgnoreCase) == 0)) + if (string.Equals(setContext.ParameterSetName, CimBaseCommand.QueryComputerSet, StringComparison.OrdinalIgnoreCase) || + string.Equals(setContext.ParameterSetName, CimBaseCommand.QuerySessionSet, StringComparison.OrdinalIgnoreCase)) { this.setCimInstance.SetCimInstance(sendToPipeline as CimInstance, setContext, this); return; @@ -238,6 +250,7 @@ public override void WriteObject(object sendToPipeline, XOperationContextBase co DebugHelper.WriteLog("Assert. CimSetCimInstance::SetCimInstance has NULL CimSetCimInstanceContext", 4); } } + base.WriteObject(sendToPipeline, context); } @@ -255,12 +268,12 @@ public override void WriteObject(object sendToPipeline, bool enumerateCollection #region private methods - private CimSetCimInstance setCimInstance; + private readonly CimSetCimInstance setCimInstance; private const string theCimSetCimInstanceParameterName = @"theCimSetCimInstance"; #endregion - }//End Class + } #endregion #region Class CmdletOperationInvokeCimMethod @@ -273,7 +286,7 @@ public override void WriteObject(object sendToPipeline, bool enumerateCollection internal class CmdletOperationInvokeCimMethod : CmdletOperationBase { /// - /// Constructor method + /// Initializes a new instance of the class. /// /// public CmdletOperationInvokeCimMethod(Cmdlet cmdlet, @@ -318,12 +331,12 @@ public override void WriteObject(object sendToPipeline, bool enumerateCollection #region private methods - private CimInvokeCimMethod cimInvokeCimMethod; + private readonly CimInvokeCimMethod cimInvokeCimMethod; private const string theCimInvokeCimMethodParameterName = @"theCimInvokeCimMethod"; #endregion - }//End Class + } #endregion @@ -338,7 +351,7 @@ public override void WriteObject(object sendToPipeline, bool enumerateCollection internal class CmdletOperationTestCimSession : CmdletOperationBase { /// - /// Constructor method + /// Initializes a new instance of the class. /// /// public CmdletOperationTestCimSession(Cmdlet cmdlet, @@ -379,12 +392,12 @@ public override void WriteObject(object sendToPipeline, XOperationContextBase co #region private methods - private CimNewSession cimNewSession; + private readonly CimNewSession cimNewSession; private const string theCimNewSessionParameterName = @"theCimNewSession"; #endregion - }//End Class + } #endregion -}//End namespace +} diff --git a/src/Microsoft.Management.Infrastructure.CimCmdlets/GetCimAssociatedInstanceCommand.cs b/src/Microsoft.Management.Infrastructure.CimCmdlets/GetCimAssociatedInstanceCommand.cs index 7afe7325230..6c4d0c94d2b 100644 --- a/src/Microsoft.Management.Infrastructure.CimCmdlets/GetCimAssociatedInstanceCommand.cs +++ b/src/Microsoft.Management.Infrastructure.CimCmdlets/GetCimAssociatedInstanceCommand.cs @@ -1,18 +1,15 @@ -/*============================================================================ - * Copyright (C) Microsoft Corporation, All rights reserved. - *============================================================================ - */ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. #region Using directives using System; +using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Management.Automation; -using System.Collections.Generic; #endregion - namespace Microsoft.Management.Infrastructure.CimCmdlets { /// @@ -25,6 +22,7 @@ namespace Microsoft.Management.Infrastructure.CimCmdlets /// Association parameter. /// /// + [Alias("gcai")] [Cmdlet(VerbsCommon.Get, GetCimAssociatedInstanceCommand.Noun, DefaultParameterSetName = CimBaseCommand.ComputerSetName, @@ -35,7 +33,7 @@ public class GetCimAssociatedInstanceCommand : CimBaseCommand #region constructor /// - /// constructor + /// Initializes a new instance of the class. /// public GetCimAssociatedInstanceCommand() : base(parameters, parameterSets) @@ -55,12 +53,7 @@ public GetCimAssociatedInstanceCommand() [Parameter( Position = 1, ValueFromPipelineByPropertyName = true)] - public String Association - { - get { return association; } - set { association = value; } - } - private String association; + public string Association { get; set; } /// /// The following is the definition of the input parameter "ResultClassName". @@ -68,37 +61,7 @@ public String Association /// the given instance. /// [Parameter] - public String ResultClassName - { - get { return resultClassName; } - set { resultClassName = value; } - } - private String resultClassName; - - /// - /// The following is the definition of the input parameter "AssociatorRole". - /// Specifies the name of the association role of the instances to be retrieved. - /// - //[Parameter(ValueFromPipelineByPropertyName = true)] - //public String AssociatorRole - //{ - // get { return associatorRole; } - // set { associatorRole = value; } - //} - //private String associatorRole; - - /// - /// The following is the definition of the input parameter "SourceRole". - /// Specifies the name of the association role of the source instance where the - /// association traversal should begin. - /// - //[Parameter(ValueFromPipelineByPropertyName = true)] - //public String SourceRole - //{ - // get { return sourcerole; } - // set { sourcerole = value; } - //} - //private String sourcerole; + public string ResultClassName { get; set; } /// /// @@ -113,22 +76,22 @@ public String ResultClassName [Alias(CimBaseCommand.AliasCimInstance)] public CimInstance InputObject { - get { return cimInstance; } + get + { + return CimInstance; + } + set { - cimInstance = value; + CimInstance = value; base.SetParameter(value, nameCimInstance); } } /// - /// Property for internal usage purpose + /// Property for internal usage purpose. /// - internal CimInstance CimInstance - { - get { return cimInstance; } - } - private CimInstance cimInstance; + internal CimInstance CimInstance { get; private set; } /// /// The following is the definition of the input parameter "Namespace". @@ -136,12 +99,7 @@ internal CimInstance CimInstance /// is registered. /// [Parameter(ValueFromPipelineByPropertyName = true)] - public String Namespace - { - get { return nameSpace; } - set { nameSpace = value; } - } - private String nameSpace; + public string Namespace { get; set; } /// /// The following is the definition of the input parameter "OperationTimeoutSec". @@ -152,12 +110,7 @@ public String Namespace /// [Alias(AliasOT)] [Parameter(ValueFromPipelineByPropertyName = true)] - public UInt32 OperationTimeoutSec - { - get { return operationTimeout; } - set { operationTimeout = value; } - } - private UInt32 operationTimeout; + public uint OperationTimeoutSec { get; set; } /// /// @@ -168,13 +121,18 @@ public UInt32 OperationTimeoutSec [Parameter] public Uri ResourceUri { - get { return resourceUri; } + get + { + return resourceUri; + } + set { this.resourceUri = value; base.SetParameter(value, nameResourceUri); } } + private Uri resourceUri; /// @@ -192,16 +150,21 @@ public Uri ResourceUri [Parameter( ParameterSetName = ComputerSetName)] [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] - public String[] ComputerName + public string[] ComputerName { - get { return computerName; } + get + { + return computerName; + } + set { computerName = value; base.SetParameter(value, nameComputerName); } } - private String[] computerName; + + private string[] computerName; /// /// The following is the definition of the input parameter "CimSession". @@ -214,13 +177,18 @@ public String[] ComputerName [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] public Microsoft.Management.Infrastructure.CimSession[] CimSession { - get { return cimSession; } + get + { + return cimSession; + } + set { cimSession = value; base.SetParameter(value, nameCimSession); } } + private Microsoft.Management.Infrastructure.CimSession[] cimSession; /// @@ -231,12 +199,7 @@ public Microsoft.Management.Infrastructure.CimSession[] CimSession /// /// [Parameter] - public SwitchParameter KeyOnly - { - get { return keyOnly; } - set { keyOnly = value; } - } - private SwitchParameter keyOnly; + public SwitchParameter KeyOnly { get; set; } #endregion @@ -249,7 +212,7 @@ protected override void BeginProcessing() { this.CmdletOperation = new CmdletOperationBase(this); this.AtBeginProcess = false; - }//End BeginProcessing() + } /// /// ProcessRecord method. @@ -257,14 +220,11 @@ protected override void BeginProcessing() protected override void ProcessRecord() { base.CheckParameterSet(); - CimGetAssociatedInstance operation = this.GetOperationAgent(); - if (operation == null) - { - operation = this.CreateOperationAgent(); - } + CimGetAssociatedInstance operation = this.GetOperationAgent() ?? this.CreateOperationAgent(); + operation.GetCimAssociatedInstance(this); operation.ProcessActions(this.CmdletOperation); - }//End ProcessRecord() + } /// /// EndProcessing method. @@ -272,9 +232,8 @@ protected override void ProcessRecord() protected override void EndProcessing() { CimGetAssociatedInstance operation = this.GetOperationAgent(); - if (operation != null) - operation.ProcessRemainActions(this.CmdletOperation); - }//End EndProcessing() + operation?.ProcessRemainActions(this.CmdletOperation); + } #endregion @@ -286,7 +245,7 @@ protected override void EndProcessing() /// used to delegate all Get-CimAssociatedInstance operations. /// /// - CimGetAssociatedInstance GetOperationAgent() + private CimGetAssociatedInstance GetOperationAgent() { return this.AsyncOperation as CimGetAssociatedInstance; } @@ -298,7 +257,7 @@ CimGetAssociatedInstance GetOperationAgent() /// /// /// - CimGetAssociatedInstance CreateOperationAgent() + private CimGetAssociatedInstance CreateOperationAgent() { this.AsyncOperation = new CimGetAssociatedInstance(); return GetOperationAgent(); @@ -309,7 +268,7 @@ CimGetAssociatedInstance CreateOperationAgent() #region internal const strings /// - /// Noun of current cmdlet + /// Noun of current cmdlet. /// internal const string Noun = @"CimAssociatedInstance"; @@ -318,20 +277,16 @@ CimGetAssociatedInstance CreateOperationAgent() #region private members #region const string of parameter names - // internal const string nameAssociation = "Association"; internal const string nameCimInstance = "InputObject"; - // internal const string nameNamespace = "Namespace"; - // internal const string nameOperationTimeoutSec = "OperationTimeoutSec"; internal const string nameComputerName = "ComputerName"; internal const string nameCimSession = "CimSession"; internal const string nameResourceUri = "ResourceUri"; - // internal const string nameKeyOnly = "KeyOnly"; #endregion /// - /// static parameter definition entries + /// Static parameter definition entries. /// - static Dictionary> parameters = new Dictionary> + private static readonly Dictionary> parameters = new() { { nameComputerName, new HashSet { @@ -358,13 +313,13 @@ CimGetAssociatedInstance CreateOperationAgent() }; /// - /// static parameter set entries + /// Static parameter set entries. /// - static Dictionary parameterSets = new Dictionary + private static readonly Dictionary parameterSets = new() { { CimBaseCommand.SessionSetName, new ParameterSetEntry(2, false) }, { CimBaseCommand.ComputerSetName, new ParameterSetEntry(1, true) }, }; #endregion - }//End Class -}//End namespace + } +} diff --git a/src/Microsoft.Management.Infrastructure.CimCmdlets/GetCimClassCommand.cs b/src/Microsoft.Management.Infrastructure.CimCmdlets/GetCimClassCommand.cs index 2201d64fdfd..1bededc485f 100644 --- a/src/Microsoft.Management.Infrastructure.CimCmdlets/GetCimClassCommand.cs +++ b/src/Microsoft.Management.Infrastructure.CimCmdlets/GetCimClassCommand.cs @@ -1,7 +1,5 @@ -/*============================================================================ - * Copyright (C) Microsoft Corporation, All rights reserved. - *============================================================================ - */ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. #region Using directives @@ -21,10 +19,11 @@ namespace Microsoft.Management.Infrastructure.CimCmdlets /// classes in the given namespace. /// /// - /// NOTES: The class instance contains the Namespace properties + /// NOTES: The class instance contains the Namespace properties /// Should the class remember what Session it came from? No. /// /// + [Alias("gcls")] [Cmdlet(VerbsCommon.Get, GetCimClassCommand.Noun, DefaultParameterSetName = ComputerSetName, HelpUri = "https://go.microsoft.com/fwlink/?LinkId=227959")] [OutputType(typeof(CimClass))] public class GetCimClassCommand : CimBaseCommand @@ -32,7 +31,7 @@ public class GetCimClassCommand : CimBaseCommand #region constructor /// - /// constructor + /// Initializes a new instance of the class. /// public GetCimClassCommand() : base(parameters, parameterSets) @@ -44,6 +43,12 @@ public GetCimClassCommand() #region parameters + /// + /// Gets or sets flag to retrieve a localized data for WMI class. + /// + [Parameter] + public SwitchParameter Amended { get; set; } + /// /// /// The following is the definition of the input parameter "ClassName". @@ -55,12 +60,7 @@ public GetCimClassCommand() [Parameter( Position = 0, ValueFromPipelineByPropertyName = true)] - public String ClassName - { - get { return className; } - set { className = value; } - } - private String className; + public string ClassName { get; set; } /// /// @@ -76,12 +76,7 @@ public String ClassName [Parameter( Position = 1, ValueFromPipelineByPropertyName = true)] - public String Namespace - { - get { return nameSpace; } - set { nameSpace = value; } - } - private String nameSpace; + public string Namespace { get; set; } /// /// The following is the definition of the input parameter "OperationTimeoutSec". @@ -90,12 +85,7 @@ public String Namespace /// [Alias(AliasOT)] [Parameter(ValueFromPipelineByPropertyName = true)] - public UInt32 OperationTimeoutSec - { - get { return operationTimeout;} - set { operationTimeout = value; } - } - private UInt32 operationTimeout; + public uint OperationTimeoutSec { get; set; } /// /// The following is the definition of the input parameter "Session". @@ -108,13 +98,18 @@ public UInt32 OperationTimeoutSec [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] public CimSession[] CimSession { - get { return cimSession;} + get + { + return cimSession; + } + set { cimSession = value; base.SetParameter(value, nameCimSession); } } + private CimSession[] cimSession; /// @@ -130,16 +125,21 @@ public CimSession[] CimSession ValueFromPipelineByPropertyName = true, ParameterSetName = ComputerSetName)] [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] - public String[] ComputerName + public string[] ComputerName { - get { return computerName; } + get + { + return computerName; + } + set { computerName = value; base.SetParameter(value, nameComputerName); } } - private String[] computerName; + + private string[] computerName; /// /// @@ -149,12 +149,7 @@ public String[] ComputerName /// /// [Parameter(ValueFromPipelineByPropertyName = true)] - public String MethodName - { - get { return methodName; } - set { methodName = value; } - } - private String methodName; + public string MethodName { get; set; } /// /// @@ -164,12 +159,7 @@ public String MethodName /// /// [Parameter(ValueFromPipelineByPropertyName = true)] - public String PropertyName - { - get { return propertyName; } - set { propertyName = value; } - } - private String propertyName; + public string PropertyName { get; set; } /// /// @@ -179,12 +169,7 @@ public String PropertyName /// /// [Parameter(ValueFromPipelineByPropertyName = true)] - public String QualifierName - { - get { return qualifierName; } - set { qualifierName = value; } - } - private String qualifierName; + public string QualifierName { get; set; } #endregion @@ -197,7 +182,7 @@ protected override void BeginProcessing() { this.CmdletOperation = new CmdletOperationBase(this); this.AtBeginProcess = false; - }//End BeginProcessing() + } /// /// ProcessRecord method. @@ -205,14 +190,11 @@ protected override void BeginProcessing() protected override void ProcessRecord() { base.CheckParameterSet(); - CimGetCimClass cimGetCimClass = this.GetOperationAgent(); - if (cimGetCimClass == null) - { - cimGetCimClass = CreateOperationAgent(); - } + CimGetCimClass cimGetCimClass = this.GetOperationAgent() ?? CreateOperationAgent(); + cimGetCimClass.GetCimClass(this); cimGetCimClass.ProcessActions(this.CmdletOperation); - }//End ProcessRecord() + } /// /// EndProcessing method. @@ -220,11 +202,8 @@ protected override void ProcessRecord() protected override void EndProcessing() { CimGetCimClass cimGetCimClass = this.GetOperationAgent(); - if (cimGetCimClass != null) - { - cimGetCimClass.ProcessRemainActions(this.CmdletOperation); - } - }//End EndProcessing() + cimGetCimClass?.ProcessRemainActions(this.CmdletOperation); + } #endregion @@ -236,9 +215,9 @@ protected override void EndProcessing() /// used to delegate all New-CimInstance operations. /// /// - CimGetCimClass GetOperationAgent() + private CimGetCimClass GetOperationAgent() { - return (this.AsyncOperation as CimGetCimClass); + return this.AsyncOperation as CimGetCimClass; } /// @@ -248,9 +227,9 @@ CimGetCimClass GetOperationAgent() /// /// /// - CimGetCimClass CreateOperationAgent() + private CimGetCimClass CreateOperationAgent() { - CimGetCimClass cimGetCimClass = new CimGetCimClass(); + CimGetCimClass cimGetCimClass = new(); this.AsyncOperation = cimGetCimClass; return cimGetCimClass; } @@ -260,7 +239,7 @@ CimGetCimClass CreateOperationAgent() #region internal const strings /// - /// Noun of current cmdlet + /// Noun of current cmdlet. /// internal const string Noun = @"CimClass"; @@ -274,9 +253,9 @@ CimGetCimClass CreateOperationAgent() #endregion /// - /// static parameter definition entries + /// Static parameter definition entries. /// - static Dictionary> parameters = new Dictionary> + private static readonly Dictionary> parameters = new() { { nameCimSession, new HashSet { @@ -292,13 +271,13 @@ CimGetCimClass CreateOperationAgent() }; /// - /// static parameter set entries + /// Static parameter set entries. /// - static Dictionary parameterSets = new Dictionary + private static readonly Dictionary parameterSets = new() { { CimBaseCommand.SessionSetName, new ParameterSetEntry(1) }, { CimBaseCommand.ComputerSetName, new ParameterSetEntry(0, true) }, }; #endregion - }//End Class -}//End namespace + } +} diff --git a/src/Microsoft.Management.Infrastructure.CimCmdlets/GetCimInstanceCommand.cs b/src/Microsoft.Management.Infrastructure.CimCmdlets/GetCimInstanceCommand.cs index 248e716c51f..65eeae8e450 100644 --- a/src/Microsoft.Management.Infrastructure.CimCmdlets/GetCimInstanceCommand.cs +++ b/src/Microsoft.Management.Infrastructure.CimCmdlets/GetCimInstanceCommand.cs @@ -1,19 +1,15 @@ -/*============================================================================ - * Copyright (C) Microsoft Corporation, All rights reserved. - *============================================================================ - */ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. #region Using directives using System; using System.Collections.Generic; -using System.Management.Automation; -using System.Collections; using System.Diagnostics.CodeAnalysis; +using System.Management.Automation; #endregion - namespace Microsoft.Management.Infrastructure.CimCmdlets { /// @@ -21,6 +17,7 @@ namespace Microsoft.Management.Infrastructure.CimCmdlets /// specified in the Property parameter, KeysOnly parameter or the Select clause /// of the Query parameter. /// + [Alias("gcim")] [Cmdlet(VerbsCommon.Get, "CimInstance", DefaultParameterSetName = CimBaseCommand.ClassNameComputerSet, HelpUri = "https://go.microsoft.com/fwlink/?LinkId=227961")] [OutputType(typeof(CimInstance))] public class GetCimInstanceCommand : CimBaseCommand @@ -28,7 +25,8 @@ public class GetCimInstanceCommand : CimBaseCommand #region constructor /// - /// constructor + /// Initializes a new instance of the class. + /// Constructor. /// public GetCimInstanceCommand() : base(parameters, parameterSets) @@ -65,13 +63,18 @@ public GetCimInstanceCommand() [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] public CimSession[] CimSession { - get { return cimSession; } + get + { + return cimSession; + } + set { cimSession = value; base.SetParameter(value, nameCimSession); } } + private CimSession[] cimSession; /// @@ -88,16 +91,21 @@ public CimSession[] CimSession Position = 0, ValueFromPipelineByPropertyName = true, ParameterSetName = CimBaseCommand.ClassNameComputerSet)] - public String ClassName + public string ClassName { - get { return className; } + get + { + return className; + } + set { this.className = value; base.SetParameter(value, nameClassName); } } - private String className; + + private string className; /// /// @@ -123,13 +131,18 @@ public String ClassName ParameterSetName = CimBaseCommand.QuerySessionSet)] public Uri ResourceUri { - get { return resourceUri; } + get + { + return resourceUri; + } + set { this.resourceUri = value; base.SetParameter(value, nameResourceUri); } } + private Uri resourceUri; /// @@ -155,16 +168,21 @@ public Uri ResourceUri [Parameter( ParameterSetName = CimBaseCommand.CimInstanceComputerSet)] [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] - public String[] ComputerName + public string[] ComputerName { - get { return computerName; } + get + { + return computerName; + } + set { computerName = value; base.SetParameter(value, nameComputerName); } } - private String[] computerName; + + private string[] computerName; /// /// @@ -179,13 +197,18 @@ public String[] ComputerName [Parameter(ParameterSetName = CimBaseCommand.ResourceUriSessionSet)] public SwitchParameter KeyOnly { - get { return keyOnly; } + get + { + return keyOnly; + } + set { keyOnly = value; base.SetParameter(value, nameKeyOnly); } } + private SwitchParameter keyOnly; /// @@ -210,16 +233,21 @@ public SwitchParameter KeyOnly ParameterSetName = CimBaseCommand.QueryComputerSet)] [Parameter(ValueFromPipelineByPropertyName = true, ParameterSetName = CimBaseCommand.QuerySessionSet)] - public String Namespace + public string Namespace { - get { return nameSpace; } + get + { + return nameSpace; + } + set { nameSpace = value; base.SetParameter(value, nameNamespace); } } - private String nameSpace; + + private string nameSpace; /// /// @@ -232,12 +260,7 @@ public String Namespace /// [Alias(AliasOT)] [Parameter] - public UInt32 OperationTimeoutSec - { - get { return operationTimeout; } - set { operationTimeout = value; } - } - private UInt32 operationTimeout; + public uint OperationTimeoutSec { get; set; } /// /// The following is the definition of the input parameter "InputObject". @@ -265,22 +288,22 @@ public UInt32 OperationTimeoutSec [Alias(CimBaseCommand.AliasCimInstance)] public CimInstance InputObject { - get { return cimInstance; } + get + { + return CimInstance; + } + set { - cimInstance = value; + CimInstance = value; base.SetParameter(value, nameCimInstance); } } /// - /// Property for internal usage purpose + /// Property for internal usage purpose. /// - internal CimInstance CimInstance - { - get { return cimInstance; } - } - private CimInstance cimInstance; + internal CimInstance CimInstance { get; private set; } /// /// The following is the definition of the input parameter "Query". @@ -293,16 +316,21 @@ internal CimInstance CimInstance [Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true, ParameterSetName = CimBaseCommand.QuerySessionSet)] - public String Query + public string Query { - get { return query; } + get + { + return query; + } + set { query = value; base.SetParameter(value, nameQuery); } } - private String query; + + private string query; /// /// @@ -320,16 +348,21 @@ public String Query [Parameter(ValueFromPipelineByPropertyName = true, ParameterSetName = CimBaseCommand.ClassNameComputerSet)] - public String QueryDialect + public string QueryDialect { - get { return queryDialect; } + get + { + return queryDialect; + } + set { queryDialect = value; base.SetParameter(value, nameQueryDialect); } } - private String queryDialect; + + private string queryDialect; /// /// @@ -348,13 +381,18 @@ public String QueryDialect [Parameter(ParameterSetName = CimBaseCommand.QuerySessionSet)] public SwitchParameter Shallow { - get { return shallow; } + get + { + return shallow; + } + set { shallow = value; base.SetParameter(value, nameShallow); } } + private SwitchParameter shallow; /// @@ -371,16 +409,21 @@ public SwitchParameter Shallow ParameterSetName = CimBaseCommand.ResourceUriSessionSet)] [Parameter(ValueFromPipelineByPropertyName = true, ParameterSetName = CimBaseCommand.ResourceUriComputerSet)] - public String Filter + public string Filter { - get { return filter; } + get + { + return filter; + } + set { filter = value; base.SetParameter(value, nameFilter); } } - private String filter; + + private string filter; /// /// @@ -398,23 +441,23 @@ public String Filter ParameterSetName = CimBaseCommand.ResourceUriComputerSet)] [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] [Alias("SelectProperties")] - public String[] Property + public string[] Property { - get { return property; } + get + { + return SelectProperties; + } + set { - property = value; + SelectProperties = value; base.SetParameter(value, nameSelectProperties); } } /// - /// Property for internal usage + /// Property for internal usage. /// - internal String[] SelectProperties - { - get { return property; } - } - private String[] property; + internal string[] SelectProperties { get; private set; } #endregion @@ -427,7 +470,7 @@ protected override void BeginProcessing() { this.CmdletOperation = new CmdletOperationBase(this); this.AtBeginProcess = false; - }//End BeginProcessing() + } /// /// ProcessRecord method. @@ -436,14 +479,11 @@ protected override void ProcessRecord() { base.CheckParameterSet(); this.CheckArgument(); - CimGetInstance cimGetInstance = this.GetOperationAgent(); - if (cimGetInstance == null) - { - cimGetInstance = CreateOperationAgent(); - } + CimGetInstance cimGetInstance = this.GetOperationAgent() ?? CreateOperationAgent(); + cimGetInstance.GetCimInstance(this); cimGetInstance.ProcessActions(this.CmdletOperation); - }//End ProcessRecord() + } /// /// EndProcessing method. @@ -451,11 +491,8 @@ protected override void ProcessRecord() protected override void EndProcessing() { CimGetInstance cimGetInstance = this.GetOperationAgent(); - if (cimGetInstance != null) - { - cimGetInstance.ProcessRemainActions(this.CmdletOperation); - } - }//End EndProcessing() + cimGetInstance?.ProcessRemainActions(this.CmdletOperation); + } #endregion @@ -468,9 +505,9 @@ protected override void EndProcessing() /// as enumerate instances, get instance, query instance. /// /// - CimGetInstance GetOperationAgent() + private CimGetInstance GetOperationAgent() { - return (this.AsyncOperation as CimGetInstance); + return this.AsyncOperation as CimGetInstance; } /// @@ -481,15 +518,15 @@ CimGetInstance GetOperationAgent() /// /// /// - CimGetInstance CreateOperationAgent() + private CimGetInstance CreateOperationAgent() { - CimGetInstance cimGetInstance = new CimGetInstance(); + CimGetInstance cimGetInstance = new(); this.AsyncOperation = cimGetInstance; return cimGetInstance; } /// - /// check argument value + /// Check argument value. /// private void CheckArgument() { @@ -499,7 +536,7 @@ private void CheckArgument() case CimBaseCommand.ClassNameSessionSet: // validate the classname & property this.className = ValidationHelper.ValidateArgumentIsValidName(nameClassName, this.className); - this.property = ValidationHelper.ValidateArgumentIsValidName(nameSelectProperties, this.property); + this.SelectProperties = ValidationHelper.ValidateArgumentIsValidName(nameSelectProperties, this.SelectProperties); break; default: break; @@ -526,9 +563,9 @@ private void CheckArgument() #endregion /// - /// static parameter definition entries + /// Static parameter definition entries. /// - static Dictionary> parameters = new Dictionary> + private static readonly Dictionary> parameters = new() { { nameCimSession, new HashSet { @@ -598,7 +635,6 @@ private void CheckArgument() new ParameterDefinitionEntry(CimBaseCommand.QueryComputerSet, false), new ParameterDefinitionEntry(CimBaseCommand.ClassNameSessionSet, false), new ParameterDefinitionEntry(CimBaseCommand.ClassNameComputerSet, false), - } }, { @@ -630,9 +666,9 @@ private void CheckArgument() }; /// - /// static parameter set entries + /// Static parameter set entries. /// - static Dictionary parameterSets = new Dictionary + private static readonly Dictionary parameterSets = new() { { CimBaseCommand.CimInstanceComputerSet, new ParameterSetEntry(1) }, { CimBaseCommand.CimInstanceSessionSet, new ParameterSetEntry(2) }, @@ -644,5 +680,5 @@ private void CheckArgument() { CimBaseCommand.QuerySessionSet, new ParameterSetEntry(2) } }; #endregion - }//End Class -}//End namespace + } +} diff --git a/src/Microsoft.Management.Infrastructure.CimCmdlets/GetCimSessionCommand.cs b/src/Microsoft.Management.Infrastructure.CimCmdlets/GetCimSessionCommand.cs index 86b26890b7f..3289b6c86c0 100644 --- a/src/Microsoft.Management.Infrastructure.CimCmdlets/GetCimSessionCommand.cs +++ b/src/Microsoft.Management.Infrastructure.CimCmdlets/GetCimSessionCommand.cs @@ -1,7 +1,5 @@ -/*============================================================================ - * Copyright (C) Microsoft Corporation, All rights reserved. - *============================================================================ - */ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. #region Using directives using System; @@ -10,14 +8,13 @@ using System.Management.Automation; #endregion - namespace Microsoft.Management.Infrastructure.CimCmdlets { /// /// The command returns zero, one or more CimSession objects that represent /// connections with remote computers established from the current PS Session. /// - + [Alias("gcms")] [Cmdlet(VerbsCommon.Get, "CimSession", DefaultParameterSetName = ComputerNameSet, HelpUri = "https://go.microsoft.com/fwlink/?LinkId=227966")] [OutputType(typeof(CimSession))] public sealed class GetCimSessionCommand : CimBaseCommand @@ -25,7 +22,7 @@ public sealed class GetCimSessionCommand : CimBaseCommand #region constructor /// - /// constructor + /// Initializes a new instance of the class. /// public GetCimSessionCommand() : base(parameters, parameterSets) @@ -59,16 +56,21 @@ public GetCimSessionCommand() ValueFromPipelineByPropertyName = true, ParameterSetName = ComputerNameSet)] [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] - public String[] ComputerName + public string[] ComputerName { - get { return computername;} + get + { + return computername; + } + set { computername = value; base.SetParameter(value, nameComputerName); } } - private String[] computername; + + private string[] computername; /// /// The following is the definition of the input parameter "Id". @@ -79,20 +81,25 @@ public String[] ComputerName ValueFromPipelineByPropertyName = true, ParameterSetName = SessionIdSet)] [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] - public UInt32[] Id + public uint[] Id { - get { return id;} + get + { + return id; + } + set { id = value; base.SetParameter(value, nameId); } } - private UInt32[] id; + + private uint[] id; /// /// The following is the definition of the input parameter "InstanceID". - /// Specifies one or Session Instance IDs + /// Specifies one or Session Instance IDs. /// [Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true, @@ -100,13 +107,18 @@ public UInt32[] Id [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] public Guid[] InstanceId { - get { return instanceid;} + get + { + return instanceid; + } + set { instanceid = value; base.SetParameter(value, nameInstanceId); } } + private Guid[] instanceid; /// @@ -118,16 +130,21 @@ public Guid[] InstanceId ValueFromPipelineByPropertyName = true, ParameterSetName = NameSet)] [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] - public String[] Name + public string[] Name { - get { return name;} + get + { + return name; + } + set { name = value; base.SetParameter(value, nameName); } } - private String[] name; + + private string[] name; #endregion @@ -139,7 +156,7 @@ protected override void BeginProcessing() { cimGetSession = new CimGetSession(); this.AtBeginProcess = false; - }//End BeginProcessing() + } /// /// ProcessRecord method. @@ -148,13 +165,13 @@ protected override void ProcessRecord() { base.CheckParameterSet(); cimGetSession.GetCimSession(this); - }//End ProcessRecord() + } #endregion #region private members /// - /// object used to search CimSession from cache + /// object used to search CimSession from cache. /// private CimGetSession cimGetSession; @@ -166,9 +183,9 @@ protected override void ProcessRecord() #endregion /// - /// static parameter definition entries + /// Static parameter definition entries. /// - static Dictionary> parameters = new Dictionary> + private static readonly Dictionary> parameters = new() { { nameComputerName, new HashSet { @@ -193,9 +210,9 @@ protected override void ProcessRecord() }; /// - /// static parameter set entries + /// Static parameter set entries. /// - static Dictionary parameterSets = new Dictionary + private static readonly Dictionary parameterSets = new() { { CimBaseCommand.ComputerNameSet, new ParameterSetEntry(0, true) }, { CimBaseCommand.SessionIdSet, new ParameterSetEntry(1) }, @@ -203,5 +220,5 @@ protected override void ProcessRecord() { CimBaseCommand.NameSet, new ParameterSetEntry(1) }, }; #endregion - }//End Class -}//End namespace + } +} diff --git a/src/Microsoft.Management.Infrastructure.CimCmdlets/InvokeCimMethodCommand.cs b/src/Microsoft.Management.Infrastructure.CimCmdlets/InvokeCimMethodCommand.cs index eb434f16b66..e3bc6f293b6 100644 --- a/src/Microsoft.Management.Infrastructure.CimCmdlets/InvokeCimMethodCommand.cs +++ b/src/Microsoft.Management.Infrastructure.CimCmdlets/InvokeCimMethodCommand.cs @@ -1,7 +1,5 @@ -/*============================================================================ - * Copyright (C) Microsoft Corporation, All rights reserved. - *============================================================================ - */ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. #region Using directives using System; @@ -11,14 +9,13 @@ using System.Management.Automation; #endregion - namespace Microsoft.Management.Infrastructure.CimCmdlets { /// /// This cmdlet enables the user to invoke a static method on a CIM class using /// the arguments passed as a list of name value pair dictionary. /// - + [Alias("icim")] [Cmdlet( "Invoke", "CimMethod", @@ -30,7 +27,7 @@ public class InvokeCimMethodCommand : CimBaseCommand #region constructor /// - /// constructor + /// Initializes a new instance of the class. /// public InvokeCimMethodCommand() : base(parameters, parameterSets) @@ -55,16 +52,21 @@ public InvokeCimMethodCommand() ValueFromPipelineByPropertyName = true, ParameterSetName = CimBaseCommand.ClassNameSessionSet)] [Alias("Class")] - public String ClassName + public string ClassName { - get { return className; } + get + { + return className; + } + set { className = value; base.SetParameter(value, nameClassName); } } - private String className; + + private string className; /// /// @@ -84,13 +86,18 @@ public String ClassName ParameterSetName = CimBaseCommand.ResourceUriSessionSet)] public Uri ResourceUri { - get { return resourceUri; } + get + { + return resourceUri; + } + set { this.resourceUri = value; base.SetParameter(value, nameResourceUri); } } + private Uri resourceUri; /// @@ -107,13 +114,18 @@ public Uri ResourceUri ParameterSetName = CimClassSessionSet)] public CimClass CimClass { - get { return cimClass; } + get + { + return cimClass; + } + set { cimClass = value; base.SetParameter(value, nameCimClass); } } + private CimClass cimClass; /// @@ -128,13 +140,18 @@ public CimClass CimClass ParameterSetName = CimBaseCommand.QuerySessionSet)] public string Query { - get { return query; } + get + { + return query; + } + set { query = value; base.SetParameter(value, nameQuery); } } + private string query; /// @@ -148,21 +165,26 @@ public string Query ParameterSetName = CimBaseCommand.QueryComputerSet)] [Parameter(ValueFromPipelineByPropertyName = true, ParameterSetName = CimBaseCommand.QuerySessionSet)] - public String QueryDialect + public string QueryDialect { - get { return queryDialect; } + get + { + return queryDialect; + } + set { queryDialect = value; base.SetParameter(value, nameQueryDialect); } } - private String queryDialect; + + private string queryDialect; /// /// The following is the definition of the input parameter "InputObject". /// Takes a CimInstance object retrieved by a Get-CimInstance call. - /// Invoke the method against the given instance + /// Invoke the method against the given instance. /// [Parameter(Mandatory = true, Position = 0, @@ -175,22 +197,22 @@ public String QueryDialect [Alias(CimBaseCommand.AliasCimInstance)] public CimInstance InputObject { - get { return cimInstance; } + get + { + return CimInstance; + } + set { - cimInstance = value; + CimInstance = value; base.SetParameter(value, nameCimInstance); } } /// - /// Property for internal usage purpose + /// Property for internal usage purpose. /// - internal CimInstance CimInstance - { - get { return cimInstance; } - } - private CimInstance cimInstance; + internal CimInstance CimInstance { get; private set; } /// /// The following is the definition of the input parameter "ComputerName". @@ -218,9 +240,13 @@ internal CimInstance CimInstance ValueFromPipelineByPropertyName = true, ParameterSetName = CimBaseCommand.ResourceUriComputerSet)] [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] - public String[] ComputerName + public string[] ComputerName { - get { return computerName; } + get + { + return computerName; + } + set { DebugHelper.WriteLogEx(); @@ -228,7 +254,8 @@ public String[] ComputerName base.SetParameter(value, nameComputerName); } } - private String[] computerName; + + private string[] computerName; /// /// @@ -258,13 +285,18 @@ public String[] ComputerName [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] public CimSession[] CimSession { - get { return cimSession; } + get + { + return cimSession; + } + set { cimSession = value; base.SetParameter(value, nameCimSession); } } + private CimSession[] cimSession; /// @@ -274,12 +306,7 @@ public CimSession[] CimSession /// [Parameter(Position = 1, ValueFromPipelineByPropertyName = true)] [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")] - public IDictionary Arguments - { - get { return arguments; } - set { arguments = value; } - } - private IDictionary arguments; + public IDictionary Arguments { get; set; } /// /// The following is the definition of the input parameter "MethodName". @@ -289,16 +316,21 @@ public IDictionary Arguments Position = 2, ValueFromPipelineByPropertyName = true)] [Alias("Name")] - public String MethodName + public string MethodName { - get { return methodName; } + get + { + return methodName; + } + set { methodName = value; base.SetParameter(value, nameMethodName); } } - private String methodName; + + private string methodName; /// /// The following is the definition of the input parameter "Namespace". @@ -318,16 +350,21 @@ public String MethodName [Parameter( ValueFromPipelineByPropertyName = true, ParameterSetName = CimBaseCommand.ResourceUriSessionSet)] - public String Namespace + public string Namespace { - get { return nameSpace; } + get + { + return nameSpace; + } + set { nameSpace = value; base.SetParameter(value, nameNamespace); } } - private String nameSpace; + + private string nameSpace; /// /// The following is the definition of the input parameter "OperationTimeoutSec". @@ -336,12 +373,7 @@ public String Namespace /// [Alias(AliasOT)] [Parameter] - public UInt32 OperationTimeoutSec - { - get { return operationTimeout; } - set { operationTimeout = value; } - } - private UInt32 operationTimeout; + public uint OperationTimeoutSec { get; set; } #endregion @@ -352,14 +384,11 @@ public UInt32 OperationTimeoutSec /// protected override void BeginProcessing() { - CimInvokeCimMethod cimInvokeMethod = this.GetOperationAgent(); - if (cimInvokeMethod == null) - { - cimInvokeMethod = CreateOperationAgent(); - } + CimInvokeCimMethod cimInvokeMethod = this.GetOperationAgent() ?? CreateOperationAgent(); + this.CmdletOperation = new CmdletOperationInvokeCimMethod(this, cimInvokeMethod); this.AtBeginProcess = false; - }//End BeginProcessing() + } /// /// ProcessRecord method. @@ -371,7 +400,7 @@ protected override void ProcessRecord() CimInvokeCimMethod cimInvokeMethod = this.GetOperationAgent(); cimInvokeMethod.InvokeCimMethod(this); cimInvokeMethod.ProcessActions(this.CmdletOperation); - }//End ProcessRecord() + } /// /// EndProcessing method. @@ -379,11 +408,8 @@ protected override void ProcessRecord() protected override void EndProcessing() { CimInvokeCimMethod cimInvokeMethod = this.GetOperationAgent(); - if (cimInvokeMethod != null) - { - cimInvokeMethod.ProcessRemainActions(this.CmdletOperation); - } - }//End EndProcessing() + cimInvokeMethod?.ProcessRemainActions(this.CmdletOperation); + } #endregion @@ -395,7 +421,7 @@ protected override void EndProcessing() /// used to delegate all Invoke-CimMethod operations. /// /// - CimInvokeCimMethod GetOperationAgent() + private CimInvokeCimMethod GetOperationAgent() { return this.AsyncOperation as CimInvokeCimMethod; } @@ -407,15 +433,15 @@ CimInvokeCimMethod GetOperationAgent() /// /// /// - CimInvokeCimMethod CreateOperationAgent() + private CimInvokeCimMethod CreateOperationAgent() { - CimInvokeCimMethod cimInvokeMethod = new CimInvokeCimMethod(); + CimInvokeCimMethod cimInvokeMethod = new(); this.AsyncOperation = cimInvokeMethod; return cimInvokeMethod; } /// - /// check argument value + /// Check argument value. /// private void CheckArgument() { @@ -449,9 +475,9 @@ private void CheckArgument() #endregion /// - /// static parameter definition entries + /// Static parameter definition entries. /// - static Dictionary> parameters = new Dictionary> + private static readonly Dictionary> parameters = new() { { nameClassName, new HashSet { @@ -536,9 +562,9 @@ private void CheckArgument() }; /// - /// static parameter set entries + /// Static parameter set entries. /// - static Dictionary parameterSets = new Dictionary + private static readonly Dictionary parameterSets = new() { { CimBaseCommand.ClassNameComputerSet, new ParameterSetEntry(2, true) }, { CimBaseCommand.ResourceUriSessionSet, new ParameterSetEntry(3) }, @@ -552,5 +578,5 @@ private void CheckArgument() { CimBaseCommand.CimClassSessionSet, new ParameterSetEntry(3) }, }; #endregion - }//End Class -}//End namespace + } +} diff --git a/src/Microsoft.Management.Infrastructure.CimCmdlets/Microsoft.Management.Infrastructure.CimCmdlets.csproj b/src/Microsoft.Management.Infrastructure.CimCmdlets/Microsoft.Management.Infrastructure.CimCmdlets.csproj index bba384de1cb..582858a592b 100644 --- a/src/Microsoft.Management.Infrastructure.CimCmdlets/Microsoft.Management.Infrastructure.CimCmdlets.csproj +++ b/src/Microsoft.Management.Infrastructure.CimCmdlets/Microsoft.Management.Infrastructure.CimCmdlets.csproj @@ -1,9 +1,7 @@ - - - - + + - PowerShell Core's Microsoft.Management.Infrastructure.CimCmdlets project + PowerShell's Microsoft.Management.Infrastructure.CimCmdlets project $(NoWarn);CS1570;CS1572;CS1573;CS1574;CS1584;CS1587;CS1591 Microsoft.Management.Infrastructure.CimCmdlets @@ -12,9 +10,4 @@ - - $(DefineConstants);CORECLR - portable - - diff --git a/src/Microsoft.Management.Infrastructure.CimCmdlets/NewCimInstanceCommand.cs b/src/Microsoft.Management.Infrastructure.CimCmdlets/NewCimInstanceCommand.cs index b1a5986a0f5..5843f25a26b 100644 --- a/src/Microsoft.Management.Infrastructure.CimCmdlets/NewCimInstanceCommand.cs +++ b/src/Microsoft.Management.Infrastructure.CimCmdlets/NewCimInstanceCommand.cs @@ -1,7 +1,5 @@ -/*============================================================================ - * Copyright (C) Microsoft Corporation, All rights reserved. - *============================================================================ - */ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. #region Using directives using System; @@ -11,7 +9,6 @@ using System.Management.Automation; #endregion - namespace Microsoft.Management.Infrastructure.CimCmdlets { /// @@ -24,6 +21,7 @@ namespace Microsoft.Management.Infrastructure.CimCmdlets /// on the server, otherwise just create client in-memory instance /// /// + [Alias("ncim")] [Cmdlet(VerbsCommon.New, "CimInstance", DefaultParameterSetName = CimBaseCommand.ClassNameComputerSet, SupportsShouldProcess = true, HelpUri = "https://go.microsoft.com/fwlink/?LinkId=227963")] [OutputType(typeof(CimInstance))] public class NewCimInstanceCommand : CimBaseCommand @@ -31,7 +29,7 @@ public class NewCimInstanceCommand : CimBaseCommand #region constructor /// - /// constructor + /// Initializes a new instance of the class. /// public NewCimInstanceCommand() : base(parameters, parameterSets) @@ -57,16 +55,21 @@ public NewCimInstanceCommand() Position = 0, ValueFromPipelineByPropertyName = true, ParameterSetName = CimBaseCommand.ClassNameComputerSet)] - public String ClassName + public string ClassName { - get { return className; } + get + { + return className; + } + set { className = value; base.SetParameter(value, nameClassName); } } - private String className; + + private string className; /// /// @@ -82,13 +85,18 @@ public String ClassName ParameterSetName = CimBaseCommand.ResourceUriComputerSet)] public Uri ResourceUri { - get { return resourceUri; } + get + { + return resourceUri; + } + set { this.resourceUri = value; base.SetParameter(value, nameResourceUri); } } + private Uri resourceUri; /// @@ -113,16 +121,21 @@ public Uri ResourceUri ValueFromPipelineByPropertyName = true, ParameterSetName = CimBaseCommand.ResourceUriComputerSet)] [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] - public String[] Key + public string[] Key { - get { return key; } + get + { + return key; + } + set { key = value; base.SetParameter(value, nameKey); } } - private String[] key; + + private string[] key; /// /// The following is the definition of the input parameter "CimClass". @@ -140,13 +153,18 @@ public String[] Key ParameterSetName = CimClassComputerSet)] public CimClass CimClass { - get { return cimClass; } + get + { + return cimClass; + } + set { cimClass = value; base.SetParameter(value, nameCimClass); } } + private CimClass cimClass; /// @@ -163,12 +181,7 @@ public CimClass CimClass ValueFromPipelineByPropertyName = true)] [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")] [Alias("Arguments")] - public IDictionary Property - { - get { return property; } - set { property = value; } - } - private IDictionary property; + public IDictionary Property { get; set; } /// /// The following is the definition of the input parameter "Namespace". @@ -187,16 +200,21 @@ public IDictionary Property [Parameter( ValueFromPipelineByPropertyName = true, ParameterSetName = CimBaseCommand.ResourceUriComputerSet)] - public String Namespace + public string Namespace { - get { return nameSpace; } + get + { + return nameSpace; + } + set { nameSpace = value; base.SetParameter(value, nameNamespace); } } - private String nameSpace; + + private string nameSpace; /// /// The following is the definition of the input parameter "OperationTimeoutSec". @@ -205,12 +223,7 @@ public String Namespace /// [Alias(AliasOT)] [Parameter] - public UInt32 OperationTimeoutSec - { - get { return operationTimeout; } - set { operationTimeout = value; } - } - private UInt32 operationTimeout; + public uint OperationTimeoutSec { get; set; } /// /// @@ -233,13 +246,18 @@ public UInt32 OperationTimeoutSec [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] public CimSession[] CimSession { - get { return cimSession; } + get + { + return cimSession; + } + set { cimSession = value; base.SetParameter(value, nameCimSession); } } + private CimSession[] cimSession; /// @@ -261,16 +279,21 @@ public CimSession[] CimSession ValueFromPipelineByPropertyName = true, ParameterSetName = CimClassComputerSet)] [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] - public String[] ComputerName + public string[] ComputerName { - get { return computerName; } + get + { + return computerName; + } + set { computerName = value; base.SetParameter(value, nameComputerName); } } - private String[] computerName; + + private string[] computerName; /// /// @@ -289,12 +312,18 @@ public String[] ComputerName ParameterSetName = CimBaseCommand.CimClassSessionSet)] public SwitchParameter ClientOnly { - get { return clientOnly; } - set { + get + { + return clientOnly; + } + + set + { clientOnly = value; base.SetParameter(value, nameClientOnly); - } + } } + private SwitchParameter clientOnly; #endregion @@ -308,7 +337,7 @@ protected override void BeginProcessing() { this.CmdletOperation = new CmdletOperationBase(this); this.AtBeginProcess = false; - }//End BeginProcessing() + } /// /// ProcessRecord method. @@ -320,29 +349,27 @@ protected override void ProcessRecord() if (this.ClientOnly) { string conflictParameterName = null; - if (null != this.ComputerName) + if (this.ComputerName != null) { conflictParameterName = @"ComputerName"; } - else if (null != this.CimSession) + else if (this.CimSession != null) { conflictParameterName = @"CimSession"; } - if (null != conflictParameterName) + + if (conflictParameterName != null) { ThrowConflictParameterWasSet(@"New-CimInstance", conflictParameterName, @"ClientOnly"); return; } } - CimNewCimInstance cimNewCimInstance = this.GetOperationAgent(); - if (cimNewCimInstance == null) - { - cimNewCimInstance = CreateOperationAgent(); - } + CimNewCimInstance cimNewCimInstance = this.GetOperationAgent() ?? CreateOperationAgent(); + cimNewCimInstance.NewCimInstance(this); cimNewCimInstance.ProcessActions(this.CmdletOperation); - }//End ProcessRecord() + } /// /// EndProcessing method. @@ -350,11 +377,8 @@ protected override void ProcessRecord() protected override void EndProcessing() { CimNewCimInstance cimNewCimInstance = this.GetOperationAgent(); - if (cimNewCimInstance != null) - { - cimNewCimInstance.ProcessRemainActions(this.CmdletOperation); - } - }//End EndProcessing() + cimNewCimInstance?.ProcessRemainActions(this.CmdletOperation); + } #endregion @@ -366,9 +390,9 @@ protected override void EndProcessing() /// used to delegate all New-CimInstance operations. /// /// - CimNewCimInstance GetOperationAgent() + private CimNewCimInstance GetOperationAgent() { - return (this.AsyncOperation as CimNewCimInstance); + return this.AsyncOperation as CimNewCimInstance; } /// @@ -378,15 +402,15 @@ CimNewCimInstance GetOperationAgent() /// /// /// - CimNewCimInstance CreateOperationAgent() + private CimNewCimInstance CreateOperationAgent() { - CimNewCimInstance cimNewCimInstance = new CimNewCimInstance(); + CimNewCimInstance cimNewCimInstance = new(); this.AsyncOperation = cimNewCimInstance; return cimNewCimInstance; } /// - /// check argument value + /// Check argument value. /// private void CheckArgument() { @@ -418,9 +442,9 @@ private void CheckArgument() #endregion /// - /// static parameter definition entries + /// Static parameter definition entries. /// - static Dictionary> parameters = new Dictionary> + private static readonly Dictionary> parameters = new() { { nameClassName, new HashSet { @@ -481,9 +505,9 @@ private void CheckArgument() }; /// - /// static parameter set entries + /// Static parameter set entries. /// - static Dictionary parameterSets = new Dictionary + private static readonly Dictionary parameterSets = new() { { CimBaseCommand.ClassNameSessionSet, new ParameterSetEntry(2) }, { CimBaseCommand.ClassNameComputerSet, new ParameterSetEntry(1, true) }, @@ -493,5 +517,5 @@ private void CheckArgument() { CimBaseCommand.ResourceUriComputerSet, new ParameterSetEntry(1) }, }; #endregion - }//End Class -}//End namespace + } +} diff --git a/src/Microsoft.Management.Infrastructure.CimCmdlets/NewCimSessionCommand.cs b/src/Microsoft.Management.Infrastructure.CimCmdlets/NewCimSessionCommand.cs index f1083747e80..8b8e36cf829 100644 --- a/src/Microsoft.Management.Infrastructure.CimCmdlets/NewCimSessionCommand.cs +++ b/src/Microsoft.Management.Infrastructure.CimCmdlets/NewCimSessionCommand.cs @@ -1,17 +1,14 @@ -/*============================================================================ - * Copyright (C) Microsoft Corporation, All rights reserved. - *============================================================================ - */ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. #region Using directives using System; -using System.Management.Automation; using System.Diagnostics.CodeAnalysis; +using System.Management.Automation; using Microsoft.Management.Infrastructure.Options; #endregion - namespace Microsoft.Management.Infrastructure.CimCmdlets { /// @@ -21,6 +18,7 @@ namespace Microsoft.Management.Infrastructure.CimCmdlets /// The CimSession object returned by the Cmdlet is used by all other CIM /// cmdlets. /// + [Alias("ncms")] [Cmdlet(VerbsCommon.New, "CimSession", DefaultParameterSetName = CredentialParameterSet, HelpUri = "https://go.microsoft.com/fwlink/?LinkId=227967")] [OutputType(typeof(CimSession))] public sealed class NewCimSessionCommand : CimBaseCommand @@ -35,13 +33,18 @@ public sealed class NewCimSessionCommand : CimBaseCommand ParameterSetName = CredentialParameterSet)] public PasswordAuthenticationMechanism Authentication { - get { return authentication;} + get + { + return authentication; + } + set { authentication = value; authenticationSet = true; } } + private PasswordAuthenticationMechanism authentication; private bool authenticationSet = false; @@ -51,13 +54,8 @@ public PasswordAuthenticationMechanism Authentication /// The default is the current user. /// [Parameter(Position = 1, ParameterSetName = CredentialParameterSet)] - [Credential()] - public PSCredential Credential - { - get { return credential; } - set { credential = value; } - } - private PSCredential credential; + [Credential] + public PSCredential Credential { get; set; } /// /// The following is the definition of the input parameter "CertificateThumbprint". @@ -65,12 +63,7 @@ public PSCredential Credential /// [Parameter(ValueFromPipelineByPropertyName = true, ParameterSetName = CertificateParameterSet)] - public String CertificateThumbprint - { - get { return certificatethumbprint; } - set { certificatethumbprint = value; } - } - private String certificatethumbprint; + public string CertificateThumbprint { get; set; } /// /// The following is the definition of the input parameter "ComputerName". @@ -83,12 +76,7 @@ public String CertificateThumbprint ValueFromPipelineByPropertyName = true)] [ValidateNotNullOrEmpty] [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] - public String[] ComputerName - { - get { return computername;} - set { computername = value; } - } - private String[] computername; + public string[] ComputerName { get; set; } /// /// @@ -102,12 +90,7 @@ public String[] ComputerName /// /// [Parameter(ValueFromPipelineByPropertyName = true)] - public String Name - { - get { return name;} - set { name = value; } - } - private String name; + public string Name { get; set; } /// /// @@ -117,20 +100,25 @@ public String Name /// /// /// The unit is Second. - /// + /// /// [Alias(AliasOT)] [Parameter(ValueFromPipelineByPropertyName = true)] - public UInt32 OperationTimeoutSec + public uint OperationTimeoutSec { - get { return operationTimeout; } + get + { + return operationTimeout; + } + set { operationTimeout = value; operationTimeoutSet = true; } } - private UInt32 operationTimeout; + + private uint operationTimeout; internal bool operationTimeoutSet = false; /// @@ -140,15 +128,7 @@ public UInt32 OperationTimeoutSec /// /// [Parameter(ValueFromPipelineByPropertyName = true)] - public SwitchParameter SkipTestConnection - { - get { return skipTestConnection; } - set - { - skipTestConnection = value; - } - } - private SwitchParameter skipTestConnection; + public SwitchParameter SkipTestConnection { get; set; } /// /// The following is the definition of the input parameter "Port". @@ -156,16 +136,21 @@ public SwitchParameter SkipTestConnection /// This is specificly for wsman protocol. /// [Parameter(ValueFromPipelineByPropertyName = true)] - public UInt32 Port + public uint Port { - get { return port; } + get + { + return port; + } + set { port = value; portSet = true; } } - private UInt32 port; + + private uint port; private bool portSet = false; /// @@ -177,17 +162,14 @@ public UInt32 Port /// If the argument is not given, a default SessionOption will be created for /// the session in .NET API layer. /// + /// /// If a object is passed, then /// connection is made using DCOM. If a /// object is passed, then connection is made using WsMan. + /// /// [Parameter(ValueFromPipelineByPropertyName = true)] - public Microsoft.Management.Infrastructure.Options.CimSessionOptions SessionOption - { - get { return sessionOption; } - set { sessionOption = value; } - } - private Microsoft.Management.Infrastructure.Options.CimSessionOptions sessionOption; + public Microsoft.Management.Infrastructure.Options.CimSessionOptions SessionOption { get; set; } #endregion @@ -201,7 +183,7 @@ protected override void BeginProcessing() cimNewSession = new CimNewSession(); this.CmdletOperation = new CmdletOperationTestCimSession(this, this.cimNewSession); this.AtBeginProcess = false; - }//End BeginProcessing() + } /// /// ProcessRecord method. @@ -213,7 +195,7 @@ protected override void ProcessRecord() BuildSessionOptions(out outputOptions, out outputCredential); cimNewSession.NewCimSession(this, outputOptions, outputCredential); cimNewSession.ProcessActions(this.CmdletOperation); - }//End ProcessRecord() + } /// /// EndProcessing method. @@ -221,15 +203,15 @@ protected override void ProcessRecord() protected override void EndProcessing() { cimNewSession.ProcessRemainActions(this.CmdletOperation); - }//End EndProcessing() + } #endregion #region helper methods /// - /// Build a CimSessionOptions, used to create CimSession + /// Build a CimSessionOptions, used to create CimSession. /// - /// Null means no prefer CimSessionOptions + /// Null means no prefer CimSessionOptions. internal void BuildSessionOptions(out CimSessionOptions outputOptions, out CimCredential outputCredential) { DebugHelper.WriteLogEx(); @@ -240,19 +222,19 @@ internal void BuildSessionOptions(out CimSessionOptions outputOptions, out CimCr // clone the sessionOption object if (this.SessionOption is WSManSessionOptions) { - options = new WSManSessionOptions(this.sessionOption as WSManSessionOptions); + options = new WSManSessionOptions(this.SessionOption as WSManSessionOptions); } else { - options = new DComSessionOptions(this.sessionOption as DComSessionOptions); + options = new DComSessionOptions(this.SessionOption as DComSessionOptions); } } + outputOptions = null; outputCredential = null; if (options != null) { - DComSessionOptions dcomOptions = (options as DComSessionOptions); - if (dcomOptions != null) + if (options is DComSessionOptions dcomOptions) { bool conflict = false; string parameterName = string.Empty; @@ -261,11 +243,13 @@ internal void BuildSessionOptions(out CimSessionOptions outputOptions, out CimCr conflict = true; parameterName = @"CertificateThumbprint"; } + if (portSet) { conflict = true; parameterName = @"Port"; } + if (conflict) { ThrowConflictParameterWasSet(@"New-CimSession", parameterName, @"DComSessionOptions"); @@ -273,6 +257,7 @@ internal void BuildSessionOptions(out CimSessionOptions outputOptions, out CimCr } } } + if (portSet || (this.CertificateThumbprint != null)) { WSManSessionOptions wsmanOptions = (options == null) ? new WSManSessionOptions() : options as WSManSessionOptions; @@ -281,13 +266,16 @@ internal void BuildSessionOptions(out CimSessionOptions outputOptions, out CimCr wsmanOptions.DestinationPort = this.Port; portSet = false; } + if (this.CertificateThumbprint != null) { - CimCredential credentials = new CimCredential(CertificateAuthenticationMechanism.Default, this.CertificateThumbprint); + CimCredential credentials = new(CertificateAuthenticationMechanism.Default, this.CertificateThumbprint); wsmanOptions.AddDestinationCredentials(credentials); } + options = wsmanOptions; } + if (this.operationTimeoutSet) { if (options != null) @@ -295,13 +283,15 @@ internal void BuildSessionOptions(out CimSessionOptions outputOptions, out CimCr options.Timeout = TimeSpan.FromSeconds((double)this.OperationTimeoutSec); } } - if (this.authenticationSet || (this.credential != null)) + + if (this.authenticationSet || (this.Credential != null)) { PasswordAuthenticationMechanism authentication = this.authenticationSet ? this.Authentication : PasswordAuthenticationMechanism.Default; if (this.authenticationSet) { this.authenticationSet = false; } + CimCredential credentials = CreateCimCredentials(this.Credential, authentication, @"New-CimSession", @"Authentication"); if (credentials == null) { @@ -316,6 +306,7 @@ internal void BuildSessionOptions(out CimSessionOptions outputOptions, out CimCr options.AddDestinationCredentials(credentials); } } + DebugHelper.WriteLogEx("Set outputOptions: {0}", 1, outputOptions); outputOptions = options; } @@ -335,18 +326,15 @@ internal void BuildSessionOptions(out CimSessionOptions outputOptions, out CimCr #region IDisposable /// - /// Clean up resources + /// Clean up resources. /// protected override void DisposeInternal() { base.DisposeInternal(); // Dispose managed resources. - if (this.cimNewSession != null) - { - this.cimNewSession.Dispose(); - } + this.cimNewSession?.Dispose(); } #endregion - }//End Class -}//End namespace + } +} diff --git a/src/Microsoft.Management.Infrastructure.CimCmdlets/NewCimSessionOptionCommand.cs b/src/Microsoft.Management.Infrastructure.CimCmdlets/NewCimSessionOptionCommand.cs index 9e12e007f3a..54956a9805a 100644 --- a/src/Microsoft.Management.Infrastructure.CimCmdlets/NewCimSessionOptionCommand.cs +++ b/src/Microsoft.Management.Infrastructure.CimCmdlets/NewCimSessionOptionCommand.cs @@ -1,7 +1,5 @@ -/*============================================================================ - * Copyright (C) Microsoft Corporation, All rights reserved. - *============================================================================ - */ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. #region Using directives using System; @@ -12,7 +10,6 @@ using Microsoft.Management.Infrastructure.Options; #endregion - namespace Microsoft.Management.Infrastructure.CimCmdlets { /// @@ -21,11 +18,13 @@ namespace Microsoft.Management.Infrastructure.CimCmdlets public enum ProtocolType { Default, + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] Dcom, + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] Wsman - }; + } /// /// The Cmdlet allows the IT Pro to create a CimSessionOptions object that she/he @@ -38,6 +37,7 @@ public enum ProtocolType /// DComSessionOptions or WSManSessionOptions, which derive from /// CimSessionOptions. /// + [Alias("ncso")] [Cmdlet(VerbsCommon.New, "CimSessionOption", DefaultParameterSetName = ProtocolNameParameterSet, HelpUri = "https://go.microsoft.com/fwlink/?LinkId=227969")] [OutputType(typeof(CimSessionOptions))] public sealed class NewCimSessionOptionCommand : CimBaseCommand @@ -45,7 +45,7 @@ public sealed class NewCimSessionOptionCommand : CimBaseCommand #region constructor /// - /// constructor + /// Initializes a new instance of the class. /// public NewCimSessionOptionCommand() : base(parameters, parameterSets) @@ -64,7 +64,11 @@ public NewCimSessionOptionCommand() [Parameter(ParameterSetName = WSManParameterSet)] public SwitchParameter NoEncryption { - get { return noEncryption; } + get + { + return noEncryption; + } + set { noEncryption = value; @@ -72,18 +76,23 @@ public SwitchParameter NoEncryption base.SetParameter(value, nameNoEncryption); } } + private SwitchParameter noEncryption; private bool noEncryptionSet = false; /// /// The following is the definition of the input parameter "CertificateCACheck". - /// Switch indicating if Certificate Authority should be validated + /// Switch indicating if Certificate Authority should be validated. /// [Parameter(ValueFromPipelineByPropertyName = true, ParameterSetName = WSManParameterSet)] public SwitchParameter SkipCACheck { - get { return skipCACheck; } + get + { + return skipCACheck; + } + set { skipCACheck = value; @@ -91,18 +100,23 @@ public SwitchParameter SkipCACheck base.SetParameter(value, nameSkipCACheck); } } + private SwitchParameter skipCACheck; private bool skipCACheckSet = false; /// /// The following is the definition of the input parameter "CertificateCNCheck". - /// Switch indicating if Certificate Name should be validated + /// Switch indicating if Certificate Name should be validated. /// [Parameter(ValueFromPipelineByPropertyName = true, ParameterSetName = WSManParameterSet)] public SwitchParameter SkipCNCheck { - get { return skipCNCheck; } + get + { + return skipCNCheck; + } + set { skipCNCheck = value; @@ -110,6 +124,7 @@ public SwitchParameter SkipCNCheck base.SetParameter(value, nameSkipCNCheck); } } + private SwitchParameter skipCNCheck; private bool skipCNCheckSet = false; @@ -121,7 +136,11 @@ public SwitchParameter SkipCNCheck ParameterSetName = WSManParameterSet)] public SwitchParameter SkipRevocationCheck { - get { return skipRevocationCheck; } + get + { + return skipRevocationCheck; + } + set { skipRevocationCheck = value; @@ -129,18 +148,23 @@ public SwitchParameter SkipRevocationCheck base.SetParameter(value, nameSkipRevocationCheck); } } + private SwitchParameter skipRevocationCheck; private bool skipRevocationCheckSet = false; /// /// The following is the definition of the input parameter "EncodePortInServicePrincipalName". - /// Switch indicating if to encode Port In Service Principal Name + /// Switch indicating if to encode Port In Service Principal Name. /// [Parameter(ValueFromPipelineByPropertyName = true, ParameterSetName = WSManParameterSet)] public SwitchParameter EncodePortInServicePrincipalName { - get { return encodeportinserviceprincipalname; } + get + { + return encodeportinserviceprincipalname; + } + set { encodeportinserviceprincipalname = value; @@ -148,6 +172,7 @@ public SwitchParameter EncodePortInServicePrincipalName base.SetParameter(value, nameEncodePortInServicePrincipalName); } } + private SwitchParameter encodeportinserviceprincipalname; private bool encodeportinserviceprincipalnameSet = false; @@ -161,7 +186,11 @@ public SwitchParameter EncodePortInServicePrincipalName ParameterSetName = WSManParameterSet)] public PacketEncoding Encoding { - get { return encoding; } + get + { + return encoding; + } + set { encoding = value; @@ -169,6 +198,7 @@ public PacketEncoding Encoding base.SetParameter(value, nameEncoding); } } + private PacketEncoding encoding; private bool encodingSet = false; @@ -181,13 +211,18 @@ public PacketEncoding Encoding ParameterSetName = WSManParameterSet)] public Uri HttpPrefix { - get { return httpprefix; } + get + { + return httpprefix; + } + set { httpprefix = value; base.SetParameter(value, nameHttpPrefix); } } + private Uri httpprefix; /// @@ -196,9 +231,13 @@ public Uri HttpPrefix /// [Parameter(ValueFromPipelineByPropertyName = true, ParameterSetName = WSManParameterSet)] - public UInt32 MaxEnvelopeSizeKB + public uint MaxEnvelopeSizeKB { - get { return maxenvelopesizekb; } + get + { + return maxenvelopesizekb; + } + set { maxenvelopesizekb = value; @@ -206,7 +245,8 @@ public UInt32 MaxEnvelopeSizeKB base.SetParameter(value, nameMaxEnvelopeSizeKB); } } - private UInt32 maxenvelopesizekb; + + private uint maxenvelopesizekb; private bool maxenvelopesizekbSet = false; /// @@ -217,7 +257,11 @@ public UInt32 MaxEnvelopeSizeKB ParameterSetName = WSManParameterSet)] public PasswordAuthenticationMechanism ProxyAuthentication { - get { return proxyAuthentication; } + get + { + return proxyAuthentication; + } + set { proxyAuthentication = value; @@ -225,6 +269,7 @@ public PasswordAuthenticationMechanism ProxyAuthentication base.SetParameter(value, nameProxyAuthentication); } } + private PasswordAuthenticationMechanism proxyAuthentication; private bool proxyauthenticationSet = false; @@ -233,32 +278,42 @@ public PasswordAuthenticationMechanism ProxyAuthentication /// [Parameter(ValueFromPipelineByPropertyName = true, ParameterSetName = WSManParameterSet)] - public String ProxyCertificateThumbprint + public string ProxyCertificateThumbprint { - get { return proxycertificatethumbprint; } + get + { + return proxycertificatethumbprint; + } + set { proxycertificatethumbprint = value; base.SetParameter(value, nameProxyCertificateThumbprint); } } - private String proxycertificatethumbprint; + + private string proxycertificatethumbprint; /// /// The following is the definition of the input parameter "ProxyCredential". /// Ps Credential used by the proxy server when required by the server. /// [Parameter(ParameterSetName = WSManParameterSet)] - [Credential()] + [Credential] public PSCredential ProxyCredential { - get { return proxycredential; } + get + { + return proxycredential; + } + set { proxycredential = value; base.SetParameter(value, nameProxyCredential); } } + private PSCredential proxycredential; /// @@ -270,7 +325,11 @@ public PSCredential ProxyCredential ParameterSetName = WSManParameterSet)] public ProxyType ProxyType { - get { return proxytype; } + get + { + return proxytype; + } + set { proxytype = value; @@ -278,6 +337,7 @@ public ProxyType ProxyType base.SetParameter(value, nameProxyType); } } + private ProxyType proxytype; private bool proxytypeSet = false; @@ -289,7 +349,11 @@ public ProxyType ProxyType ParameterSetName = WSManParameterSet)] public SwitchParameter UseSsl { - get { return usessl; } + get + { + return usessl; + } + set { usessl = value; @@ -297,6 +361,7 @@ public SwitchParameter UseSsl base.SetParameter(value, nameUseSsl); } } + private SwitchParameter usessl; private bool usesslSet = false; @@ -308,7 +373,11 @@ public SwitchParameter UseSsl [Parameter(ParameterSetName = DcomParameterSet)] public ImpersonationType Impersonation { - get { return impersonation; } + get + { + return impersonation; + } + set { impersonation = value; @@ -316,6 +385,7 @@ public ImpersonationType Impersonation base.SetParameter(value, nameImpersonation); } } + private ImpersonationType impersonation; private bool impersonationSet = false; @@ -327,7 +397,11 @@ public ImpersonationType Impersonation [Parameter(ParameterSetName = DcomParameterSet)] public SwitchParameter PacketIntegrity { - get { return packetintegrity; } + get + { + return packetintegrity; + } + set { packetintegrity = value; @@ -335,6 +409,7 @@ public SwitchParameter PacketIntegrity base.SetParameter(value, namePacketIntegrity); } } + private SwitchParameter packetintegrity; private bool packetintegritySet = false; @@ -346,7 +421,11 @@ public SwitchParameter PacketIntegrity [Parameter(ParameterSetName = DcomParameterSet)] public SwitchParameter PacketPrivacy { - get { return packetprivacy; } + get + { + return packetprivacy; + } + set { packetprivacy = value; @@ -354,12 +433,13 @@ public SwitchParameter PacketPrivacy base.SetParameter(value, namePacketPrivacy); } } + private SwitchParameter packetprivacy; private bool packetprivacySet = false; /// /// The following is the definition of the input parameter "Protocol". - /// Switch indicating if to encode Port In Service Principal Name + /// Switch indicating if to encode Port In Service Principal Name. /// [Parameter( Mandatory = true, @@ -368,38 +448,33 @@ public SwitchParameter PacketPrivacy ParameterSetName = ProtocolNameParameterSet)] public ProtocolType Protocol { - get { return protocol; } + get + { + return protocol; + } + set { protocol = value; base.SetParameter(value, nameProtocol); } } + private ProtocolType protocol; /// /// The following is the definition of the input parameter "UICulture". - /// Specifies the UI Culture to use. i.e. en-us, ar-sa + /// Specifies the UI Culture to use. i.e. en-us, ar-sa. /// [Parameter(ValueFromPipelineByPropertyName = true)] - public CultureInfo UICulture - { - get { return uiculture; } - set { uiculture = value; } - } - private CultureInfo uiculture; + public CultureInfo UICulture { get; set; } /// /// The following is the definition of the input parameter "Culture". - /// Specifies the culture to use. i.e. en-us, ar-sa + /// Specifies the culture to use. i.e. en-us, ar-sa. /// [Parameter(ValueFromPipelineByPropertyName = true)] - public CultureInfo Culture - { - get { return culture; } - set { culture = value; } - } - private CultureInfo culture; + public CultureInfo Culture { get; set; } #endregion @@ -412,7 +487,7 @@ protected override void BeginProcessing() { this.CmdletOperation = new CmdletOperationBase(this); this.AtBeginProcess = false; - }//End BeginProcessing() + } /// /// ProcessRecord method. @@ -427,11 +502,13 @@ protected override void ProcessRecord() { options = CreateWSMANSessionOptions(); } + break; case DcomParameterSet: { options = CreateDComSessionOptions(); } + break; case ProtocolNameParameterSet: switch (Protocol) @@ -444,41 +521,45 @@ protected override void ProcessRecord() options = CreateWSMANSessionOptions(); break; } + break; default: return; } + if (options != null) { if (this.Culture != null) { options.Culture = this.Culture; } + if (this.UICulture != null) { options.UICulture = this.UICulture; } + this.WriteObject(options); } - }//End ProcessRecord() + } /// /// EndProcessing method. /// protected override void EndProcessing() { - }//End EndProcessing() + } #endregion #region helper functions /// - /// Create DComSessionOptions + /// Create DComSessionOptions. /// /// internal DComSessionOptions CreateDComSessionOptions() { - DComSessionOptions dcomoptions = new DComSessionOptions(); + DComSessionOptions dcomoptions = new(); if (this.impersonationSet) { dcomoptions.Impersonation = this.Impersonation; @@ -488,6 +569,7 @@ internal DComSessionOptions CreateDComSessionOptions() { dcomoptions.Impersonation = ImpersonationType.Impersonate; } + if (this.packetintegritySet) { dcomoptions.PacketIntegrity = this.packetintegrity; @@ -497,6 +579,7 @@ internal DComSessionOptions CreateDComSessionOptions() { dcomoptions.PacketIntegrity = true; } + if (this.packetprivacySet) { dcomoptions.PacketPrivacy = this.PacketPrivacy; @@ -506,16 +589,17 @@ internal DComSessionOptions CreateDComSessionOptions() { dcomoptions.PacketPrivacy = true; } + return dcomoptions; } /// - /// Create WSMANSessionOptions + /// Create WSMANSessionOptions. /// /// internal WSManSessionOptions CreateWSMANSessionOptions() { - WSManSessionOptions wsmanoptions = new WSManSessionOptions(); + WSManSessionOptions wsmanoptions = new(); if (this.noEncryptionSet) { wsmanoptions.NoEncryption = true; @@ -525,6 +609,7 @@ internal WSManSessionOptions CreateWSMANSessionOptions() { wsmanoptions.NoEncryption = false; } + if (this.skipCACheckSet) { wsmanoptions.CertCACheck = false; @@ -534,6 +619,7 @@ internal WSManSessionOptions CreateWSMANSessionOptions() { wsmanoptions.CertCACheck = true; } + if (this.skipCNCheckSet) { wsmanoptions.CertCNCheck = false; @@ -543,6 +629,7 @@ internal WSManSessionOptions CreateWSMANSessionOptions() { wsmanoptions.CertCNCheck = true; } + if (this.skipRevocationCheckSet) { wsmanoptions.CertRevocationCheck = false; @@ -552,6 +639,7 @@ internal WSManSessionOptions CreateWSMANSessionOptions() { wsmanoptions.CertRevocationCheck = true; } + if (this.encodeportinserviceprincipalnameSet) { wsmanoptions.EncodePortInServicePrincipalName = this.EncodePortInServicePrincipalName; @@ -561,6 +649,7 @@ internal WSManSessionOptions CreateWSMANSessionOptions() { wsmanoptions.EncodePortInServicePrincipalName = false; } + if (this.encodingSet) { wsmanoptions.PacketEncoding = this.Encoding; @@ -569,10 +658,12 @@ internal WSManSessionOptions CreateWSMANSessionOptions() { wsmanoptions.PacketEncoding = PacketEncoding.Utf8; } + if (this.HttpPrefix != null) { wsmanoptions.HttpUrlPrefix = this.HttpPrefix; } + if (this.maxenvelopesizekbSet) { wsmanoptions.MaxEnvelopeSize = this.MaxEnvelopeSizeKB; @@ -581,11 +672,13 @@ internal WSManSessionOptions CreateWSMANSessionOptions() { wsmanoptions.MaxEnvelopeSize = 0; } - if (!String.IsNullOrWhiteSpace(this.ProxyCertificateThumbprint)) + + if (!string.IsNullOrWhiteSpace(this.ProxyCertificateThumbprint)) { - CimCredential credentials = new CimCredential(CertificateAuthenticationMechanism.Default, this.ProxyCertificateThumbprint); + CimCredential credentials = new(CertificateAuthenticationMechanism.Default, this.ProxyCertificateThumbprint); wsmanoptions.AddProxyCredentials(credentials); } + if (this.proxyauthenticationSet) { this.proxyauthenticationSet = false; @@ -601,10 +694,11 @@ internal WSManSessionOptions CreateWSMANSessionOptions() catch (Exception ex) { DebugHelper.WriteLogEx(ex.ToString(), 1); - throw ex; + throw; } } } + if (this.proxytypeSet) { wsmanoptions.ProxyType = this.ProxyType; @@ -614,6 +708,7 @@ internal WSManSessionOptions CreateWSMANSessionOptions() { wsmanoptions.ProxyType = Options.ProxyType.WinHttp; } + if (this.usesslSet) { wsmanoptions.UseSsl = this.UseSsl; @@ -623,6 +718,7 @@ internal WSManSessionOptions CreateWSMANSessionOptions() { wsmanoptions.UseSsl = false; } + wsmanoptions.DestinationPort = 0; return wsmanoptions; } @@ -651,9 +747,9 @@ internal WSManSessionOptions CreateWSMANSessionOptions() #endregion /// - /// static parameter definition entries + /// Static parameter definition entries. /// - static Dictionary> parameters = new Dictionary> + private static readonly Dictionary> parameters = new() { { nameNoEncryption, new HashSet { @@ -752,14 +848,14 @@ internal WSManSessionOptions CreateWSMANSessionOptions() }; /// - /// static parameter set entries + /// Static parameter set entries. /// - static Dictionary parameterSets = new Dictionary + private static readonly Dictionary parameterSets = new() { { CimBaseCommand.ProtocolNameParameterSet, new ParameterSetEntry(1, true) }, { CimBaseCommand.DcomParameterSet, new ParameterSetEntry(0) }, { CimBaseCommand.WSManParameterSet, new ParameterSetEntry(0) }, }; #endregion - }//End Class -}//End namespace + } +} diff --git a/src/Microsoft.Management.Infrastructure.CimCmdlets/RegisterCimIndicationCommand.cs b/src/Microsoft.Management.Infrastructure.CimCmdlets/RegisterCimIndicationCommand.cs index 66213850619..b314691e41f 100644 --- a/src/Microsoft.Management.Infrastructure.CimCmdlets/RegisterCimIndicationCommand.cs +++ b/src/Microsoft.Management.Infrastructure.CimCmdlets/RegisterCimIndicationCommand.cs @@ -1,7 +1,5 @@ -/*============================================================================ - * Copyright (C) Microsoft Corporation, All rights reserved. - *============================================================================ - */ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. #region Using directives using System; @@ -11,7 +9,6 @@ using Microsoft.PowerShell.Commands; #endregion - namespace Microsoft.Management.Infrastructure.CimCmdlets { /// @@ -22,6 +19,7 @@ namespace Microsoft.Management.Infrastructure.CimCmdlets /// cancel the subscription /// Should we have the second parameter set with a -Query? /// + [Alias("rcie")] [Cmdlet(VerbsLifecycle.Register, "CimIndicationEvent", DefaultParameterSetName = CimBaseCommand.ClassNameComputerSet, HelpUri = "https://go.microsoft.com/fwlink/?LinkId=227960")] public class RegisterCimIndicationCommand : ObjectEventRegistrationBase { @@ -37,12 +35,7 @@ public class RegisterCimIndicationCommand : ObjectEventRegistrationBase /// /// [Parameter] - public String Namespace - { - get { return nameSpace; } - set { nameSpace = value; } - } - private String nameSpace; + public string Namespace { get; set; } /// /// The following is the definition of the input parameter "ClassName". @@ -55,16 +48,21 @@ public String Namespace [Parameter(Mandatory = true, Position = 0, ParameterSetName = CimBaseCommand.ClassNameComputerSet)] - public String ClassName + public string ClassName { - get { return className; } + get + { + return className; + } + set { className = value; this.SetParameter(value, nameClassName); } } - private String className; + + private string className; /// /// The following is the definition of the input parameter "Query". @@ -78,16 +76,21 @@ public String ClassName Mandatory = true, Position = 0, ParameterSetName = CimBaseCommand.QueryExpressionComputerSet)] - public String Query + public string Query { - get { return query; } + get + { + return query; + } + set { query = value; this.SetParameter(value, nameQuery); } } - private String query; + + private string query; /// /// @@ -98,16 +101,21 @@ public String Query /// [Parameter(ParameterSetName = CimBaseCommand.QueryExpressionComputerSet)] [Parameter(ParameterSetName = CimBaseCommand.QueryExpressionSessionSet)] - public String QueryDialect + public string QueryDialect { - get { return queryDialect; } + get + { + return queryDialect; + } + set { queryDialect = value; this.SetParameter(value, nameQueryDialect); } } - private String queryDialect; + + private string queryDialect; /// /// The following is the definition of the input parameter "OperationTimeoutSec". @@ -116,12 +124,7 @@ public String QueryDialect /// [Alias(CimBaseCommand.AliasOT)] [Parameter] - public UInt32 OperationTimeoutSec - { - get { return operationTimeout; } - set { operationTimeout = value; } - } - private UInt32 operationTimeout; + public uint OperationTimeoutSec { get; set; } /// /// The following is the definition of the input parameter "Session". @@ -135,13 +138,18 @@ public UInt32 OperationTimeoutSec ParameterSetName = CimBaseCommand.ClassNameSessionSet)] public CimSession CimSession { - get { return cimSession; } + get + { + return cimSession; + } + set { cimSession = value; this.SetParameter(value, nameCimSession); } } + private CimSession cimSession; /// @@ -152,23 +160,28 @@ public CimSession CimSession [Alias(CimBaseCommand.AliasCN, CimBaseCommand.AliasServerName)] [Parameter(ParameterSetName = CimBaseCommand.QueryExpressionComputerSet)] [Parameter(ParameterSetName = CimBaseCommand.ClassNameComputerSet)] - public String ComputerName + public string ComputerName { - get { return computername; } + get + { + return computername; + } + set { computername = value; this.SetParameter(value, nameComputerName); } } - private String computername; + + private string computername; #endregion /// - /// Returns the object that generates events to be monitored + /// Returns the object that generates events to be monitored. /// - protected override Object GetSourceObject() + protected override object GetSourceObject() { CimIndicationWatcher watcher = null; string parameterSetName = null; @@ -180,6 +193,7 @@ protected override Object GetSourceObject() { this.parameterBinder.reset(); } + string tempQueryExpression = string.Empty; switch (parameterSetName) { @@ -191,9 +205,10 @@ protected override Object GetSourceObject() case CimBaseCommand.ClassNameComputerSet: // validate the classname this.CheckArgument(); - tempQueryExpression = String.Format(CultureInfo.CurrentCulture, "Select * from {0}", this.ClassName); + tempQueryExpression = string.Create(CultureInfo.CurrentCulture, $"Select * from {this.ClassName}"); break; } + switch (parameterSetName) { case CimBaseCommand.QueryExpressionSessionSet: @@ -201,25 +216,26 @@ protected override Object GetSourceObject() { watcher = new CimIndicationWatcher(this.CimSession, this.Namespace, this.QueryDialect, tempQueryExpression, this.OperationTimeoutSec); } + break; case CimBaseCommand.QueryExpressionComputerSet: case CimBaseCommand.ClassNameComputerSet: { watcher = new CimIndicationWatcher(this.ComputerName, this.Namespace, this.QueryDialect, tempQueryExpression, this.OperationTimeoutSec); } + break; } - if (watcher != null) - { - watcher.SetCmdlet(this); - } + + watcher?.SetCmdlet(this); + return watcher; } /// - /// Returns the event name to be monitored on the input object + /// Returns the event name to be monitored on the input object. /// - protected override String GetSourceObjectEventName() + protected override string GetSourceObjectEventName() { return "CimIndicationArrived"; } @@ -239,9 +255,9 @@ protected override void EndProcessing() if (newSubscriber != null) { DebugHelper.WriteLog("RegisterCimIndicationCommand::EndProcessing subscribe to Unsubscribed event", 4); - newSubscriber.Unsubscribed += new PSEventUnsubscribedEventHandler(newSubscriber_Unsubscribed); + newSubscriber.Unsubscribed += newSubscriber_Unsubscribed; } - }//End EndProcessing() + } /// /// @@ -256,15 +272,12 @@ private static void newSubscriber_Unsubscribed( DebugHelper.WriteLogEx(); CimIndicationWatcher watcher = sender as CimIndicationWatcher; - if (watcher != null) - { - watcher.Stop(); - } + watcher?.Stop(); } #region private members /// - /// check argument value + /// Check argument value. /// private void CheckArgument() { @@ -272,13 +285,13 @@ private void CheckArgument() } /// - /// Parameter binder used to resolve parameter set name + /// Parameter binder used to resolve parameter set name. /// - private ParameterBinder parameterBinder = new ParameterBinder( + private readonly ParameterBinder parameterBinder = new( parameters, parameterSets); /// - /// Set the parameter + /// Set the parameter. /// /// private void SetParameter(object value, string parameterName) @@ -287,6 +300,7 @@ private void SetParameter(object value, string parameterName) { return; } + this.parameterBinder.SetParameter(parameterName, true); } @@ -299,9 +313,9 @@ private void SetParameter(object value, string parameterName) #endregion /// - /// static parameter definition entries + /// Static parameter definition entries. /// - static Dictionary> parameters = new Dictionary> + private static readonly Dictionary> parameters = new() { { nameClassName, new HashSet { @@ -336,9 +350,9 @@ private void SetParameter(object value, string parameterName) }; /// - /// static parameter set entries + /// Static parameter set entries. /// - static Dictionary parameterSets = new Dictionary + private static readonly Dictionary parameterSets = new() { { CimBaseCommand.QueryExpressionSessionSet, new ParameterSetEntry(2) }, { CimBaseCommand.QueryExpressionComputerSet, new ParameterSetEntry(1) }, @@ -347,5 +361,5 @@ private void SetParameter(object value, string parameterName) }; #endregion - }//End Class -}//End namespace + } +} diff --git a/src/Microsoft.Management.Infrastructure.CimCmdlets/RemoveCimInstanceCommand.cs b/src/Microsoft.Management.Infrastructure.CimCmdlets/RemoveCimInstanceCommand.cs index 3e6e4166a4f..5ac8d129367 100644 --- a/src/Microsoft.Management.Infrastructure.CimCmdlets/RemoveCimInstanceCommand.cs +++ b/src/Microsoft.Management.Infrastructure.CimCmdlets/RemoveCimInstanceCommand.cs @@ -1,7 +1,5 @@ -/*============================================================================ - * Copyright (C) Microsoft Corporation, All rights reserved. - *============================================================================ - */ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. #region Using directives @@ -12,16 +10,16 @@ #endregion - namespace Microsoft.Management.Infrastructure.CimCmdlets { /// /// Enables the user to remove a CimInstance. /// + [Alias("rcim")] [Cmdlet( VerbsCommon.Remove, "CimInstance", - SupportsShouldProcess=true, + SupportsShouldProcess = true, DefaultParameterSetName = CimBaseCommand.CimInstanceComputerSet, HelpUri = "https://go.microsoft.com/fwlink/?LinkId=227964")] public class RemoveCimInstanceCommand : CimBaseCommand @@ -29,7 +27,7 @@ public class RemoveCimInstanceCommand : CimBaseCommand #region constructor /// - /// constructor + /// Initializes a new instance of the class. /// public RemoveCimInstanceCommand() : base(parameters, parameterSets) @@ -55,15 +53,19 @@ public RemoveCimInstanceCommand() [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] public CimSession[] CimSession { - get { return cimSession; } + get + { + return cimSession; + } + set { cimSession = value; base.SetParameter(value, nameCimSession); } } - private CimSession[] cimSession; + private CimSession[] cimSession; /// /// @@ -77,13 +79,18 @@ public CimSession[] CimSession ParameterSetName = CimBaseCommand.CimInstanceSessionSet)] public Uri ResourceUri { - get { return resourceUri; } + get + { + return resourceUri; + } + set { this.resourceUri = value; base.SetParameter(value, nameResourceUri); } } + private Uri resourceUri; /// @@ -96,16 +103,21 @@ public Uri ResourceUri [Parameter( ParameterSetName = CimBaseCommand.CimInstanceComputerSet)] [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] - public String[] ComputerName + public string[] ComputerName { - get { return computername; } + get + { + return computername; + } + set { computername = value; base.SetParameter(value, nameComputerName); } } - private String[] computername; + + private string[] computername; /// /// The following is the definition of the input parameter "Namespace". @@ -119,16 +131,21 @@ public String[] ComputerName Position = 1, ValueFromPipelineByPropertyName = true, ParameterSetName = CimBaseCommand.QueryComputerSet)] - public String Namespace + public string Namespace { - get { return nameSpace; } + get + { + return nameSpace; + } + set { nameSpace = value; base.SetParameter(value, nameNamespace); } } - private String nameSpace; + + private string nameSpace; /// /// The following is the definition of the input parameter "OperationTimeoutSec". @@ -137,12 +154,7 @@ public String Namespace /// [Alias(AliasOT)] [Parameter] - public UInt32 OperationTimeoutSec - { - get { return operationTimeout;} - set { operationTimeout = value; } - } - private UInt32 operationTimeout; + public uint OperationTimeoutSec { get; set; } /// /// The following is the definition of the input parameter "InputObject". @@ -161,22 +173,22 @@ public UInt32 OperationTimeoutSec [Alias(CimBaseCommand.AliasCimInstance)] public CimInstance InputObject { - get { return cimInstance; } + get + { + return CimInstance; + } + set { - cimInstance = value; + CimInstance = value; base.SetParameter(value, nameCimInstance); } } /// - /// Property for internal usage purpose + /// Property for internal usage purpose. /// - internal CimInstance CimInstance - { - get { return cimInstance; } - } - private CimInstance cimInstance; + internal CimInstance CimInstance { get; private set; } /// /// The following is the definition of the input parameter "Query". @@ -191,16 +203,21 @@ internal CimInstance CimInstance Position = 0, ValueFromPipelineByPropertyName = true, ParameterSetName = CimBaseCommand.QuerySessionSet)] - public String Query + public string Query { - get { return query;} + get + { + return query; + } + set { query = value; base.SetParameter(value, nameQuery); } } - private String query; + + private string query; /// /// The following is the definition of the input parameter "QueryDialect". @@ -211,16 +228,21 @@ public String Query ParameterSetName = CimBaseCommand.QuerySessionSet)] [Parameter(ValueFromPipelineByPropertyName = true, ParameterSetName = CimBaseCommand.QueryComputerSet)] - public String QueryDialect + public string QueryDialect { - get { return querydialect;} + get + { + return querydialect; + } + set { querydialect = value; base.SetParameter(value, nameQueryDialect); } } - private String querydialect; + + private string querydialect; #endregion @@ -231,14 +253,11 @@ public String QueryDialect /// protected override void BeginProcessing() { - CimRemoveCimInstance cimRemoveInstance = this.GetOperationAgent(); - if (cimRemoveInstance == null) - { - cimRemoveInstance = CreateOperationAgent(); - } + CimRemoveCimInstance cimRemoveInstance = this.GetOperationAgent() ?? CreateOperationAgent(); + this.CmdletOperation = new CmdletOperationRemoveCimInstance(this, cimRemoveInstance); this.AtBeginProcess = false; - }//End BeginProcessing() + } /// /// ProcessRecord method. @@ -249,7 +268,7 @@ protected override void ProcessRecord() CimRemoveCimInstance cimRemoveInstance = this.GetOperationAgent(); cimRemoveInstance.RemoveCimInstance(this); cimRemoveInstance.ProcessActions(this.CmdletOperation); - }//End ProcessRecord() + } /// /// EndProcessing method. @@ -257,11 +276,8 @@ protected override void ProcessRecord() protected override void EndProcessing() { CimRemoveCimInstance cimRemoveInstance = this.GetOperationAgent(); - if (cimRemoveInstance != null) - { - cimRemoveInstance.ProcessRemainActions(this.CmdletOperation); - } - }//End EndProcessing() + cimRemoveInstance?.ProcessRemainActions(this.CmdletOperation); + } #endregion @@ -273,9 +289,9 @@ protected override void EndProcessing() /// used to delegate all Remove-CimInstance operations. /// /// - CimRemoveCimInstance GetOperationAgent() + private CimRemoveCimInstance GetOperationAgent() { - return (this.AsyncOperation as CimRemoveCimInstance); + return this.AsyncOperation as CimRemoveCimInstance; } /// @@ -285,9 +301,9 @@ CimRemoveCimInstance GetOperationAgent() /// /// /// - CimRemoveCimInstance CreateOperationAgent() + private CimRemoveCimInstance CreateOperationAgent() { - CimRemoveCimInstance cimRemoveInstance = new CimRemoveCimInstance(); + CimRemoveCimInstance cimRemoveInstance = new(); this.AsyncOperation = cimRemoveInstance; return cimRemoveInstance; } @@ -307,9 +323,9 @@ CimRemoveCimInstance CreateOperationAgent() #endregion /// - /// static parameter definition entries + /// Static parameter definition entries. /// - static Dictionary> parameters = new Dictionary> + private static readonly Dictionary> parameters = new() { { nameCimSession, new HashSet { @@ -356,9 +372,9 @@ CimRemoveCimInstance CreateOperationAgent() }; /// - /// static parameter set entries + /// Static parameter set entries. /// - static Dictionary parameterSets = new Dictionary + private static readonly Dictionary parameterSets = new() { { CimBaseCommand.CimInstanceComputerSet, new ParameterSetEntry(1, true) }, { CimBaseCommand.CimInstanceSessionSet, new ParameterSetEntry(2) }, @@ -366,5 +382,5 @@ CimRemoveCimInstance CreateOperationAgent() { CimBaseCommand.QuerySessionSet, new ParameterSetEntry(2) }, }; #endregion - }//End Class -}//End namespace + } +} diff --git a/src/Microsoft.Management.Infrastructure.CimCmdlets/RemoveCimSessionCommand.cs b/src/Microsoft.Management.Infrastructure.CimCmdlets/RemoveCimSessionCommand.cs index f02f1598593..2f1a5ad026e 100644 --- a/src/Microsoft.Management.Infrastructure.CimCmdlets/RemoveCimSessionCommand.cs +++ b/src/Microsoft.Management.Infrastructure.CimCmdlets/RemoveCimSessionCommand.cs @@ -1,6 +1,6 @@ -// -// Copyright (C) Microsoft. All rights reserved. -// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + #region Using directives using System; @@ -19,7 +19,7 @@ namespace Microsoft.Management.Infrastructure.CimCmdlets /// /// This Cmdlet allows the to remove, or terminate, one or more CimSession(s). /// - + [Alias("rcms")] [Cmdlet(VerbsCommon.Remove, "CimSession", SupportsShouldProcess = true, DefaultParameterSetName = CimSessionSet, @@ -29,7 +29,7 @@ public sealed class RemoveCimSessionCommand : CimBaseCommand #region constructor /// - /// constructor + /// Initializes a new instance of the class. /// public RemoveCimSessionCommand() : base(parameters, parameterSets) @@ -54,13 +54,18 @@ public RemoveCimSessionCommand() [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] public CimSession[] CimSession { - get { return cimsession;} + get + { + return cimsession; + } + set { cimsession = value; base.SetParameter(value, nameCimSession); } } + private CimSession[] cimsession; /// @@ -77,16 +82,21 @@ public CimSession[] CimSession ValueFromPipelineByPropertyName = true, ParameterSetName = ComputerNameSet)] [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] - public String[] ComputerName + public string[] ComputerName { - get { return computername; } + get + { + return computername; + } + set { computername = value; base.SetParameter(value, nameComputerName); } } - private String[] computername; + + private string[] computername; /// /// The following is the definition of the input parameter "Id". @@ -98,16 +108,21 @@ public String[] ComputerName ValueFromPipelineByPropertyName = true, ParameterSetName = SessionIdSet)] [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] - public UInt32[] Id + public uint[] Id { - get { return id;} + get + { + return id; + } + set { id = value; base.SetParameter(value, nameId); } } - private UInt32[] id; + + private uint[] id; /// /// The following is the definition of the input parameter "InstanceId". @@ -121,13 +136,18 @@ public UInt32[] Id [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] public Guid[] InstanceId { - get { return instanceid;} + get + { + return instanceid; + } + set { instanceid = value; base.SetParameter(value, nameInstanceId); } } + private Guid[] instanceid; /// @@ -140,16 +160,21 @@ public Guid[] InstanceId ValueFromPipelineByPropertyName = true, ParameterSetName = NameSet)] [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] - public String[] Name + public string[] Name { - get { return name;} + get + { + return name; + } + set { name = value; base.SetParameter(value, nameName); } } - private String[] name; + + private string[] name; #endregion @@ -160,7 +185,7 @@ protected override void BeginProcessing() { this.cimRemoveSession = new CimRemoveSession(); this.AtBeginProcess = false; - }//End BeginProcessing() + } /// /// ProcessRecord method. @@ -169,12 +194,12 @@ protected override void ProcessRecord() { base.CheckParameterSet(); this.cimRemoveSession.RemoveCimSession(this); - }//End ProcessRecord() + } #region private members /// /// object used to remove the session from - /// session cache + /// session cache. /// private CimRemoveSession cimRemoveSession; @@ -187,9 +212,9 @@ protected override void ProcessRecord() #endregion /// - /// static parameter definition entries + /// Static parameter definition entries. /// - static Dictionary> parameters = new Dictionary> + private static readonly Dictionary> parameters = new() { { nameCimSession, new HashSet { @@ -219,9 +244,9 @@ protected override void ProcessRecord() }; /// - /// static parameter set entries + /// Static parameter set entries. /// - static Dictionary parameterSets = new Dictionary + private static readonly Dictionary parameterSets = new() { { CimBaseCommand.CimSessionSet, new ParameterSetEntry(1, true) }, { CimBaseCommand.ComputerNameSet, new ParameterSetEntry(1) }, @@ -230,5 +255,5 @@ protected override void ProcessRecord() { CimBaseCommand.NameSet, new ParameterSetEntry(1) }, }; #endregion - }//End Class -}//End namespace + } +} diff --git a/src/Microsoft.Management.Infrastructure.CimCmdlets/SetCimInstanceCommand.cs b/src/Microsoft.Management.Infrastructure.CimCmdlets/SetCimInstanceCommand.cs index 717d3f805bd..d190e5fafba 100644 --- a/src/Microsoft.Management.Infrastructure.CimCmdlets/SetCimInstanceCommand.cs +++ b/src/Microsoft.Management.Infrastructure.CimCmdlets/SetCimInstanceCommand.cs @@ -1,7 +1,5 @@ -/*============================================================================ - * Copyright (C) Microsoft Corporation, All rights reserved. - *============================================================================ - */ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. #region Using directives using System; @@ -19,6 +17,7 @@ namespace Microsoft.Management.Infrastructure.CimCmdlets /// CimInstance must have values of all [KEY] properties. /// /// + [Alias("scim")] [Cmdlet( VerbsCommon.Set, "CimInstance", @@ -30,7 +29,7 @@ public class SetCimInstanceCommand : CimBaseCommand #region constructor /// - /// constructor + /// Initializes a new instance of the class. /// public SetCimInstanceCommand() : base(parameters, parameterSets) @@ -55,13 +54,18 @@ public SetCimInstanceCommand() [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] public CimSession[] CimSession { - get { return cimSession; } + get + { + return cimSession; + } + set { cimSession = value; base.SetParameter(value, nameCimSession); } } + private CimSession[] cimSession; /// @@ -74,16 +78,21 @@ public CimSession[] CimSession [Parameter( ParameterSetName = CimBaseCommand.CimInstanceComputerSet)] [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] - public String[] ComputerName + public string[] ComputerName { - get { return computername; } + get + { + return computername; + } + set { computername = value; base.SetParameter(value, nameComputerName); } } - private String[] computername; + + private string[] computername; /// /// @@ -97,13 +106,18 @@ public String[] ComputerName ParameterSetName = CimBaseCommand.CimInstanceSessionSet)] public Uri ResourceUri { - get { return resourceUri; } + get + { + return resourceUri; + } + set { this.resourceUri = value; base.SetParameter(value, nameResourceUri); } } + private Uri resourceUri; /// @@ -114,16 +128,21 @@ public Uri ResourceUri ParameterSetName = CimBaseCommand.QuerySessionSet)] [Parameter(ValueFromPipelineByPropertyName = true, ParameterSetName = CimBaseCommand.QueryComputerSet)] - public String Namespace + public string Namespace { - get { return nameSpace; } + get + { + return nameSpace; + } + set { nameSpace = value; base.SetParameter(value, nameNamespace); } } - private String nameSpace; + + private string nameSpace; /// /// The following is the definition of the input parameter "OperationTimeoutSec". @@ -132,12 +151,7 @@ public String Namespace /// [Alias(AliasOT)] [Parameter] - public UInt32 OperationTimeoutSec - { - get { return operationTimeout; } - set { operationTimeout = value; } - } - private UInt32 operationTimeout; + public uint OperationTimeoutSec { get; set; } /// /// The following is the definition of the input parameter "InputObject". @@ -156,22 +170,22 @@ public UInt32 OperationTimeoutSec [Alias(CimBaseCommand.AliasCimInstance)] public CimInstance InputObject { - get { return cimInstance; } + get + { + return CimInstance; + } + set { - cimInstance = value; + CimInstance = value; base.SetParameter(value, nameCimInstance); } } /// - /// Property for internal usage purpose + /// Property for internal usage purpose. /// - internal CimInstance CimInstance - { - get { return cimInstance; } - } - private CimInstance cimInstance; + internal CimInstance CimInstance { get; private set; } /// /// The following is the definition of the input parameter "Query". @@ -186,16 +200,21 @@ internal CimInstance CimInstance Position = 0, ValueFromPipelineByPropertyName = true, ParameterSetName = CimBaseCommand.QuerySessionSet)] - public String Query + public string Query { - get { return query; } + get + { + return query; + } + set { query = value; base.SetParameter(value, nameQuery); } } - private String query; + + private string query; /// /// The following is the definition of the input parameter "QueryDialect". @@ -206,16 +225,21 @@ public String Query ParameterSetName = CimBaseCommand.QuerySessionSet)] [Parameter(ValueFromPipelineByPropertyName = true, ParameterSetName = CimBaseCommand.QueryComputerSet)] - public String QueryDialect + public string QueryDialect { - get { return querydialect; } + get + { + return querydialect; + } + set { querydialect = value; base.SetParameter(value, nameQueryDialect); } } - private String querydialect; + + private string querydialect; /// /// @@ -241,18 +265,25 @@ public String QueryDialect [Alias("Arguments")] public IDictionary Property { - get { return property; } + get + { + return property; + } + set { property = value; base.SetParameter(value, nameProperty); } } + private IDictionary property; /// + /// /// The following is the definition of the input parameter "PassThru", /// indicate whether Set-CimInstance should output modified result instance or not. + /// /// /// True indicates output the result instance, otherwise output nothing as by default /// behavior. @@ -260,18 +291,7 @@ public IDictionary Property /// [Parameter] [ValidateNotNull] - public SwitchParameter PassThru - { - set - { - this.passThru = value; - } - get - { - return this.passThru; - } - } - private SwitchParameter passThru; + public SwitchParameter PassThru { get; set; } #endregion @@ -282,14 +302,11 @@ public SwitchParameter PassThru /// protected override void BeginProcessing() { - CimSetCimInstance cimSetCimInstance = this.GetOperationAgent(); - if (cimSetCimInstance == null) - { - cimSetCimInstance = CreateOperationAgent(); - } + CimSetCimInstance cimSetCimInstance = this.GetOperationAgent() ?? CreateOperationAgent(); + this.CmdletOperation = new CmdletOperationSetCimInstance(this, cimSetCimInstance); this.AtBeginProcess = false; - }//End BeginProcessing() + } /// /// ProcessRecord method. @@ -300,7 +317,7 @@ protected override void ProcessRecord() CimSetCimInstance cimSetCimInstance = this.GetOperationAgent(); cimSetCimInstance.SetCimInstance(this); cimSetCimInstance.ProcessActions(this.CmdletOperation); - }//End ProcessRecord() + } /// /// EndProcessing method. @@ -308,11 +325,8 @@ protected override void ProcessRecord() protected override void EndProcessing() { CimSetCimInstance cimSetCimInstance = this.GetOperationAgent(); - if (cimSetCimInstance != null) - { - cimSetCimInstance.ProcessRemainActions(this.CmdletOperation); - } - }//End EndProcessing() + cimSetCimInstance?.ProcessRemainActions(this.CmdletOperation); + } #endregion @@ -324,9 +338,9 @@ protected override void EndProcessing() /// used to delegate all Set-CimInstance operations. /// /// - CimSetCimInstance GetOperationAgent() + private CimSetCimInstance GetOperationAgent() { - return (this.AsyncOperation as CimSetCimInstance); + return this.AsyncOperation as CimSetCimInstance; } /// @@ -336,9 +350,9 @@ CimSetCimInstance GetOperationAgent() /// /// /// - CimSetCimInstance CreateOperationAgent() + private CimSetCimInstance CreateOperationAgent() { - CimSetCimInstance cimSetCimInstance = new CimSetCimInstance(); + CimSetCimInstance cimSetCimInstance = new(); this.AsyncOperation = cimSetCimInstance; return cimSetCimInstance; } @@ -359,9 +373,9 @@ CimSetCimInstance CreateOperationAgent() #endregion /// - /// static parameter definition entries + /// Static parameter definition entries. /// - static Dictionary> parameters = new Dictionary> + private static readonly Dictionary> parameters = new() { { nameCimSession, new HashSet { @@ -416,9 +430,9 @@ CimSetCimInstance CreateOperationAgent() }; /// - /// static parameter set entries + /// Static parameter set entries. /// - static Dictionary parameterSets = new Dictionary + private static readonly Dictionary parameterSets = new() { { CimBaseCommand.QuerySessionSet, new ParameterSetEntry(3) }, { CimBaseCommand.QueryComputerSet, new ParameterSetEntry(2) }, @@ -426,5 +440,5 @@ CimSetCimInstance CreateOperationAgent() { CimBaseCommand.CimInstanceComputerSet, new ParameterSetEntry(1, true) }, }; #endregion - }//End Class -}//End namespace + } +} diff --git a/src/Microsoft.Management.Infrastructure.CimCmdlets/Utils.cs b/src/Microsoft.Management.Infrastructure.CimCmdlets/Utils.cs index c5389942069..adcab254231 100644 --- a/src/Microsoft.Management.Infrastructure.CimCmdlets/Utils.cs +++ b/src/Microsoft.Management.Infrastructure.CimCmdlets/Utils.cs @@ -1,19 +1,15 @@ -/*============================================================================ - * Copyright (C) Microsoft Corporation, All rights reserved. - *============================================================================ - */ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. // #define LOGENABLE // uncomment this line to enable the log, - // create c:\temp\cim.log before invoking cimcmdlets +// create c:\temp\cim.log before invoking cimcmdlets using System; using System.Collections.Generic; using System.Diagnostics; using System.Globalization; using System.IO; -using System.Management.Automation; using System.Text.RegularExpressions; -using System.Threading; namespace Microsoft.Management.Infrastructure.CimCmdlets { @@ -29,47 +25,47 @@ internal static class ConstValue /// Default computername /// /// - internal static string[] DefaultSessionName = {@"*"}; + internal static readonly string[] DefaultSessionName = { @"*" }; /// /// /// Empty computername, which will create DCOM session /// /// - internal static string NullComputerName = null; + internal static readonly string NullComputerName = null; /// /// /// Empty computername array, which will create DCOM session /// /// - internal static string[] NullComputerNames = { NullComputerName }; + internal static readonly string[] NullComputerNames = { NullComputerName }; /// /// /// localhost computername, which will create WSMAN session /// /// - internal static string LocalhostComputerName = @"localhost"; + internal static readonly string LocalhostComputerName = @"localhost"; /// /// /// Default namespace /// /// - internal static string DefaultNameSpace = @"root\cimv2"; + internal static readonly string DefaultNameSpace = @"root\cimv2"; /// /// /// Default namespace /// /// - internal static string DefaultQueryDialect = @"WQL"; + internal static readonly string DefaultQueryDialect = @"WQL"; /// - /// Name of the note property that controls if "PSComputerName" column is shown + /// Name of the note property that controls if "PSComputerName" column is shown. /// - internal static string ShowComputerNameNoteProperty = "PSShowComputerName"; + internal static readonly string ShowComputerNameNoteProperty = "PSShowComputerName"; /// /// @@ -80,7 +76,7 @@ internal static class ConstValue /// internal static bool IsDefaultComputerName(string computerName) { - return String.IsNullOrEmpty(computerName); + return string.IsNullOrEmpty(computerName); } /// @@ -92,11 +88,11 @@ internal static bool IsDefaultComputerName(string computerName) /// internal static IEnumerable GetComputerNames(IEnumerable computerNames) { - return (computerNames == null) ? NullComputerNames : computerNames; + return computerNames ?? NullComputerNames; } /// - /// Get computer name, if it is null then return default one + /// Get computer name, if it is null then return default one. /// /// /// @@ -114,7 +110,7 @@ internal static string GetComputerName(string computerName) /// internal static string GetNamespace(string nameSpace) { - return (nameSpace == null) ? DefaultNameSpace : nameSpace; + return nameSpace ?? DefaultNameSpace; } /// @@ -126,7 +122,7 @@ internal static string GetNamespace(string nameSpace) /// internal static string GetQueryDialectWithDefault(string queryDialect) { - return (queryDialect == null) ? DefaultQueryDialect : queryDialect; + return queryDialect ?? DefaultQueryDialect; } } @@ -142,42 +138,29 @@ internal static class DebugHelper /// /// Flag used to control generating log message into file. /// - private static bool generateLog = true; - internal static bool GenerateLog - { - get { return generateLog; } - set { generateLog = value; } - } + internal static bool GenerateLog { get; set; } = true; /// - /// Whether the log been initialized + /// Whether the log been initialized. /// private static bool logInitialized = false; - /// - /// Flag used to control generating message into powershell - /// - private static bool generateVerboseMessage = true; - internal static bool GenerateVerboseMessage - { - get { return generateVerboseMessage; } - set { generateVerboseMessage = value; } - } + internal static bool GenerateVerboseMessage { get; set; } = true; /// - /// Flag used to control generating message into powershell + /// Flag used to control generating message into powershell. /// - internal static string logFile = @"c:\temp\Cim.log"; + internal static readonly string logFile = @"c:\temp\Cim.log"; /// - /// Indent space string + /// Indent space string. /// - internal static string space = @" "; + internal static readonly string space = @" "; /// - /// Indent space strings array + /// Indent space strings array. /// - internal static string[] spaces = { + internal static readonly string[] spaces = { string.Empty, space, space + space, @@ -187,51 +170,42 @@ internal static bool GenerateVerboseMessage }; /// - /// Lock the log file + /// Lock the log file. /// - internal static object logLock = new object(); + internal static readonly object logLock = new(); #endregion #region internal strings - internal static string runspaceStateChanged = "Runspace {0} state changed to {1}"; - internal static string classDumpInfo = @"Class type is {0}"; - internal static string propertyDumpInfo = @"Property name {0} of type {1}, its value is {2}"; - internal static string defaultPropertyType = @"It is a default property, default value is {0}"; - internal static string propertyValueSet = @"This property value is set by user {0}"; - internal static string addParameterSetName = @"Add parameter set {0} name to cache"; - internal static string removeParameterSetName = @"Remove parameter set {0} name from cache"; - internal static string currentParameterSetNameCount = @"Cache have {0} parameter set names"; - internal static string currentParameterSetNameInCache = @"Cache have parameter set {0} valid {1}"; - internal static string currentnonMandatoryParameterSetInCache = @"Cache have optional parameter set {0} valid {1}"; - internal static string optionalParameterSetNameCount = @"Cache have {0} optional parameter set names"; - internal static string finalParameterSetName = @"------Final parameter set name of the cmdlet is {0}"; - internal static string addToOptionalParameterSet = @"Add to optional ParameterSetNames {0}"; - internal static string startToResolveParameterSet = @"------Resolve ParameterSet Name"; - internal static string reservedString = @"------"; + internal static readonly string runspaceStateChanged = "Runspace {0} state changed to {1}"; + internal static readonly string classDumpInfo = @"Class type is {0}"; + internal static readonly string propertyDumpInfo = @"Property name {0} of type {1}, its value is {2}"; + internal static readonly string defaultPropertyType = @"It is a default property, default value is {0}"; + internal static readonly string propertyValueSet = @"This property value is set by user {0}"; + internal static readonly string addParameterSetName = @"Add parameter set {0} name to cache"; + internal static readonly string removeParameterSetName = @"Remove parameter set {0} name from cache"; + internal static readonly string currentParameterSetNameCount = @"Cache have {0} parameter set names"; + internal static readonly string currentParameterSetNameInCache = @"Cache have parameter set {0} valid {1}"; + internal static readonly string currentnonMandatoryParameterSetInCache = @"Cache have optional parameter set {0} valid {1}"; + internal static readonly string optionalParameterSetNameCount = @"Cache have {0} optional parameter set names"; + internal static readonly string finalParameterSetName = @"------Final parameter set name of the cmdlet is {0}"; + internal static readonly string addToOptionalParameterSet = @"Add to optional ParameterSetNames {0}"; + internal static readonly string startToResolveParameterSet = @"------Resolve ParameterSet Name"; + internal static readonly string reservedString = @"------"; #endregion #region runtime methods internal static string GetSourceCodeInformation(bool withFileName, int depth) { - StackTrace trace = new StackTrace(); + StackTrace trace = new(); StackFrame frame = trace.GetFrame(depth); - //if (withFileName) - //{ - // return string.Format(CultureInfo.CurrentUICulture, "{0}#{1}:{2}:", frame.GetFileName()., frame.GetFileLineNumber(), frame.GetMethod().Name); - //} - //else - //{ - // return string.Format(CultureInfo.CurrentUICulture, "{0}:", frame.GetMethod()); - //} - return string.Format(CultureInfo.CurrentUICulture, "{0}::{1} ", - frame.GetMethod().DeclaringType.Name, - frame.GetMethod().Name); + + return string.Create(CultureInfo.CurrentUICulture, $"{frame.GetMethod().DeclaringType.Name}::{frame.GetMethod().Name} "); } #endregion /// - /// Write message to log file named @logFile + /// Write message to log file named @logFile. /// /// internal static void WriteLog(string message) @@ -240,7 +214,7 @@ internal static void WriteLog(string message) } /// - /// Write blank line to log file named @logFile + /// Write blank line to log file named @logFile. /// /// internal static void WriteEmptyLine() @@ -249,18 +223,18 @@ internal static void WriteEmptyLine() } /// - /// Write message to log file named @logFile with args + /// Write message to log file named @logFile with args. /// /// internal static void WriteLog(string message, int indent, params object[] args) { - String outMessage = String.Empty; + string outMessage = string.Empty; FormatLogMessage(ref outMessage, message, args); WriteLog(outMessage, indent); } /// - /// Write message to log file w/o arguments + /// Write message to log file w/o arguments. /// /// /// @@ -270,19 +244,19 @@ internal static void WriteLog(string message, int indent) } /// - /// Write message to log file named @logFile with args + /// Write message to log file named @logFile with args. /// /// internal static void WriteLogEx(string message, int indent, params object[] args) { - String outMessage = String.Empty; + string outMessage = string.Empty; WriteLogInternal(string.Empty, 0, -1); FormatLogMessage(ref outMessage, message, args); WriteLogInternal(outMessage, indent, 3); } /// - /// Write message to log file w/o arguments + /// Write message to log file w/o arguments. /// /// /// @@ -293,7 +267,7 @@ internal static void WriteLogEx(string message, int indent) } /// - /// Write message to log file w/o arguments + /// Write message to log file w/o arguments. /// /// /// @@ -304,15 +278,15 @@ internal static void WriteLogEx() } /// - /// Format the message + /// Format the message. /// /// /// /// [Conditional("LOGENABLE")] - private static void FormatLogMessage(ref String outMessage, string message, params object[] args) + private static void FormatLogMessage(ref string outMessage, string message, params object[] args) { - outMessage = String.Format(CultureInfo.CurrentCulture, message, args); + outMessage = string.Format(CultureInfo.CurrentCulture, message, args); } /// @@ -336,36 +310,38 @@ private static void WriteLogInternal(string message, int indent, int depth) } } - if (generateLog) + if (GenerateLog) { if (indent < 0) { indent = 0; } + if (indent > 5) { indent = 5; } + string sourceInformation = string.Empty; if (depth != -1) { sourceInformation = string.Format( CultureInfo.InvariantCulture, "Thread {0}#{1}:{2}:{3} {4}", - Thread.CurrentThread.ManagedThreadId, + Environment.CurrentManagedThreadId, DateTime.Now.Hour, DateTime.Now.Minute, DateTime.Now.Second, GetSourceCodeInformation(true, depth)); } + lock (logLock) { - using (FileStream fs = new FileStream(logFile,FileMode.OpenOrCreate)) - using (StreamWriter writer = new StreamWriter(fs)) + using (FileStream fs = new(logFile, FileMode.OpenOrCreate)) + using (StreamWriter writer = new(fs)) { writer.WriteLineAsync(spaces[indent] + sourceInformation + @" " + message); } - } } } @@ -379,26 +355,23 @@ private static void WriteLogInternal(string message, int indent, int depth) internal static class ValidationHelper { /// - /// Validate the argument is not null + /// Validate the argument is not null. /// /// /// public static void ValidateNoNullArgument(object obj, string argumentName) { - if (obj == null) - { - throw new ArgumentNullException(argumentName); - } + ArgumentNullException.ThrowIfNull(obj, argumentName); } /// - /// Validate the argument is not null and not whitespace + /// Validate the argument is not null and not whitespace. /// /// /// public static void ValidateNoNullorWhiteSpaceArgument(string obj, string argumentName) { - if (String.IsNullOrWhiteSpace(obj)) + if (string.IsNullOrWhiteSpace(obj)) { throw new ArgumentException(argumentName); } @@ -406,12 +379,12 @@ public static void ValidateNoNullorWhiteSpaceArgument(string obj, string argumen /// /// Validate that given classname/propertyname is a valid name compliance with DMTF standard. - /// Only for verifying ClassName and PropertyName argument + /// Only for verifying ClassName and PropertyName argument. /// /// /// /// - /// Throw if the given value is not a valid name (class name or property name) + /// Throw if the given value is not a valid name (class name or property name). public static string ValidateArgumentIsValidName(string parameterName, string value) { DebugHelper.WriteLogEx(); @@ -420,15 +393,16 @@ public static string ValidateArgumentIsValidName(string parameterName, string va string trimed = value.Trim(); // The first character should be contained in set: [A-Za-z_] // Inner characters should be contained in set: [A-Za-z0-9_] - Regex regex = new Regex(@"^[a-zA-Z_][a-zA-Z0-9_]*\z"); + Regex regex = new(@"^[a-zA-Z_][a-zA-Z0-9_]*\z"); if (regex.IsMatch(trimed)) { DebugHelper.WriteLogEx("A valid name: {0}={1}", 0, parameterName, value); return trimed; } } + DebugHelper.WriteLogEx("An invalid name: {0}={1}", 0, parameterName, value); - throw new ArgumentException(String.Format(CultureInfo.CurrentUICulture, Strings.InvalidParameterValue, value, parameterName)); + throw new ArgumentException(string.Format(CultureInfo.CurrentUICulture, CimCmdletStrings.InvalidParameterValue, value, parameterName)); } /// @@ -438,21 +412,23 @@ public static string ValidateArgumentIsValidName(string parameterName, string va /// /// /// - /// Throw if the given value contains any invalid name (class name or property name) - public static String[] ValidateArgumentIsValidName(string parameterName, String[] value) + /// Throw if the given value contains any invalid name (class name or property name). + public static string[] ValidateArgumentIsValidName(string parameterName, string[] value) { if (value != null) { foreach (string propertyName in value) { // * is wild char supported in select properties - if ((propertyName != null) && (String.Compare(propertyName.Trim(), "*", StringComparison.OrdinalIgnoreCase) == 0)) + if ((propertyName != null) && string.Equals(propertyName.Trim(), "*", StringComparison.OrdinalIgnoreCase)) { continue; } + ValidationHelper.ValidateArgumentIsValidName(parameterName, propertyName); } } + return value; } } diff --git a/src/Microsoft.Management.Infrastructure.CimCmdlets/resources/CimCmdletStrings.resx b/src/Microsoft.Management.Infrastructure.CimCmdlets/resources/CimCmdletStrings.resx new file mode 100644 index 00000000000..d479d28b5bf --- /dev/null +++ b/src/Microsoft.Management.Infrastructure.CimCmdlets/resources/CimCmdletStrings.resx @@ -0,0 +1,228 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Operation '{0}' complete. + {0} is a placeholder for operation name. (i.e, GetCimInstance) + + + Create CimInstance + + + Delete CimInstance + + + Enumerate Associated CimInstances + + + Enumerate CimClasses + + + Enumerate CimInstances + + + Get CimClass + + + Get CimInstance + + + Invoke CimMethod + + + Modify CimInstance + + + Query CimInstances + + + Subscribe CimIndication + + + Perform operation '{0}' with following parameters, '{1}'. + {0} is a placeholder for operation name; {1} is a placeholder for parameters value + + + Parameter '{0}' cannot be used with the parameter '{1}'. + {0} is a placeholder for parameter name; {1} is a placeholder for another parameter name; + + + Could not find CimSession with the given {0} = {1} + {0} is a placeholder for property name; {1} is a placeholder for property value. + + + Could not find the following properties from the given class {0}: {1}. + {0} is a placeholder for class name; {1} is a placeholder for list of property names. + + + Could not modify readonly property '{0}' of object '{1}'. + {0} is a placeholder for propertyname; {1} is a placeholder for object string. + + + Default status description. + N/A + + + Cannot perform operation because the wildcard path {0} did not resolve to a file. + {0} is a placeholder for a path + + + Authentication type '{0}' is invalid without credential. Only following authentication type are allowed without credential, '{1}', '{2}', '{3}', or '{4}'. + {0} is a placeholder for authentication type. {1}-{4} are placeholders for authentication types. + + + Can not find method '{0}' in class '{1}'. + {0} is a placeholder for method name. {1} is a placeholders for class name. + + + Can not find Parameter '{0}' in method '{1}' of class '{2}'. + {0} is a placeholder for parameter name; {1} is a placeholder for method name; {2} is a placeholder for class name. + + + Invalid operation. Current cmdlet already have operation created. + N/A + + + Argument '{0}' contains characters that are not allowed in parameter '{1}'. Supply an argument that is valid and then try the command again. + {0} stand for argument value, {1} stand for parameter name. + + + Cannot perform operation because the path resolved to more than one file. This command cannot operate on multiple files. + + + Argument '{0}' can not be null. + N/A + + + CimSession proxy object already have operation in progress. + N/A + + + Cannot open file because the current provider ({0}) cannot open a file. + {0} is a placeholder for PowerShell filesystem-like provider name (i.e. registry provider) + + + Unable to add property '{0}' to input object '{1}'. The class schema does not contain the property. + {0} stand for property name, {1} stand for cim instance path. + + + Unable to resolve the parameter set name. + N/A + + diff --git a/src/Microsoft.Management.Infrastructure.CimCmdlets/resources/Microsoft.Management.Infrastructure.CimCmdlets.Strings.resx b/src/Microsoft.Management.Infrastructure.CimCmdlets/resources/Microsoft.Management.Infrastructure.CimCmdlets.Strings.resx deleted file mode 100644 index 5015ddad221..00000000000 --- a/src/Microsoft.Management.Infrastructure.CimCmdlets/resources/Microsoft.Management.Infrastructure.CimCmdlets.Strings.resx +++ /dev/null @@ -1,228 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - Operation '{0}' complete. - {0} is a placeholder for operation name. (i.e, GetCimInstance) - - - Create CimInstance - - - Delete CimInstance - - - Enumerate Associated CimInstances - - - Enumerate CimClasses - - - Enumerate CimInstances - - - Get CimClass - - - Get CimInstance - - - Invoke CimMethod - - - Modify CimInstance - - - Query CimInstances - - - Subscribe CimIndication - - - Perform operation '{0}' with following parameters, '{1}'. - {0} is a placeholder for operation name; {1} is a placeholder for parameters value - - - Parameter '{0}' cannot be used with the parameter '{1}'. - {0} is a placeholder for parameter name; {1} is a placeholder for another parameter name; - - - Could not find CimSession with the given {0} = {1} - {0} is a placeholder for property name; {1} is a placeholder for property value. - - - Could not find the following properties from the given class {0}: {1}. - {0} is a placeholder for class name; {1} is a placeholder for list of property names. - - - Could not modify readonly property '{0}' of object '{1}'. - {0} is a placeholder for propertyname; {1} is a placeholder for object string. - - - Default status description. - N/A - - - Cannot perform operation because the wildcard path {0} did not resolve to a file. - {0} is a placeholder for a path - - - Authentication type '{0}' is invalid without credential. Only following authentication type are allowed without credential, '{1}', '{2}', '{3}', or '{4}'. - {0} is a placeholder for authentication type. {1}-{4} are placeholders for authentication types. - - - Can not find method '{0}' in class '{1}'. - {0} is a placeholder for method name. {1} is a placeholders for class name. - - - Can not find Parameter '{0}' in method '{1}' of class '{2}'. - {0} is a placeholder for parameter name; {1} is a placeholder for method name; {2} is a placeholder for class name. - - - Invalid operation. Current cmdlet already have operation created. - N/A - - - Argument '{0}' contains characters that are not allowed in parameter '{1}'. Supply an argument that is valid and then try the command again. - {0} stand for argument value, {1} stand for parameter name. - - - Cannot perform operation because the path resolved to more than one file. This command cannot operate on multiple files. - - - Argument '{0}' can not be null. - N/A - - - CimSession proxy object already have operation in progress. - N/A - - - Cannot open file because the current provider ({0}) cannot open a file. - {0} is a placeholder for PowerShell filesystem-like provider name (i.e. registry provider) - - - Unable to add property '{0}' to input object '{1}'. The class schema does not contain the property. - {0} stand for property name, {1} stand for cim instance path. - - - Unable to resolve the parameter set name. - N/A - - diff --git a/src/Microsoft.Management.UI.Internal/CommonHelper.cs b/src/Microsoft.Management.UI.Internal/CommonHelper.cs new file mode 100644 index 00000000000..c9492c701d6 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/CommonHelper.cs @@ -0,0 +1,71 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.Windows; + +// Specifies the location in which theme dictionaries are stored for types in an assembly. +[assembly: ThemeInfo(ResourceDictionaryLocation.None, ResourceDictionaryLocation.SourceAssembly)] + +namespace Microsoft.Management.UI +{ + /// + /// Utilities in common in this assembly. + /// + internal static class CommonHelper + { + /// + /// Restore the values from the settings to the actual window position, size and state. + /// + /// The window we are setting position and size of. + /// The value for top from the user settings. + /// The value for left from the user settings. + /// The value for width from the user settings. + /// The value for height from the user settings. + /// The with used if is not valid. + /// The height used if is not valid. + /// True if the window is maximized in the user setting. + internal static void SetStartingPositionAndSize(Window target, double userSettingTop, double userSettingLeft, double userSettingWidth, double userSettingHeight, double defaultWidth, double defaultHeight, bool userSettingMaximized) + { + bool leftInvalid = userSettingLeft < System.Windows.SystemParameters.VirtualScreenLeft || + userSettingWidth > System.Windows.SystemParameters.VirtualScreenLeft + + System.Windows.SystemParameters.VirtualScreenWidth; + + bool topInvalid = userSettingTop < System.Windows.SystemParameters.VirtualScreenTop || + userSettingTop > System.Windows.SystemParameters.VirtualScreenTop + + System.Windows.SystemParameters.VirtualScreenHeight; + + bool widthInvalid = userSettingWidth < 0 || + userSettingWidth > System.Windows.SystemParameters.VirtualScreenWidth; + + bool heightInvalid = userSettingHeight < 0 || + userSettingHeight > System.Windows.SystemParameters.VirtualScreenHeight; + + if (leftInvalid || topInvalid) + { + target.WindowStartupLocation = System.Windows.WindowStartupLocation.CenterScreen; + } + else + { + target.Left = userSettingLeft; + target.Top = userSettingTop; + } + + // If any saved coordinate is invalid, we set the window to the default position + if (widthInvalid || heightInvalid) + { + target.Width = defaultWidth; + target.Height = defaultHeight; + } + else + { + target.Width = userSettingWidth; + target.Height = userSettingHeight; + } + + if (userSettingMaximized) + { + target.WindowState = WindowState.Maximized; + } + } + } +} diff --git a/src/Microsoft.Management.UI.Internal/HelpWindow/HelpParagraphBuilder.cs b/src/Microsoft.Management.UI.Internal/HelpWindow/HelpParagraphBuilder.cs new file mode 100644 index 00000000000..fdb81a6d416 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/HelpWindow/HelpParagraphBuilder.cs @@ -0,0 +1,994 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Diagnostics; +using System.Globalization; +using System.Management.Automation; +using System.Text; +using System.Windows.Documents; + +namespace Microsoft.Management.UI.Internal +{ + /// + /// Builds a help paragraph for a cmdlet. + /// + internal class HelpParagraphBuilder : ParagraphBuilder + { + /// + /// Indentation size. + /// + internal const int IndentSize = 4; + + /// + /// new line separators. + /// + private static readonly string[] Separators = new[] { "\r\n", "\n" }; + + /// + /// Object with the cmdelt. + /// + private readonly PSObject psObj; + + /// + /// Initializes a new instance of the HelpParagraphBuilder class. + /// + /// Paragraph being built. + /// Object with help information. + internal HelpParagraphBuilder(Paragraph paragraph, PSObject psObj) + : base(paragraph) + { + this.psObj = psObj; + this.AddTextToParagraphBuilder(); + } + + /// + /// Enum for category of Help. + /// + private enum HelpCategory + { + Default, + DscResource, + Class + } + + /// + /// Gets the string value of a property or null if it could not be retrieved. + /// + /// Object with the property. + /// Property name. + /// The string value of a property or null if it could not be retrieved. + internal static string GetPropertyString(PSObject psObj, string propertyName) + { + Debug.Assert(psObj != null, "ensured by caller"); + object value = GetPropertyObject(psObj, propertyName); + + if (value == null) + { + return null; + } + + return value.ToString(); + } + + /// + /// Adds the help text to the paragraph. + /// + internal void AddTextToParagraphBuilder() + { + this.ResetAllText(); + + string strCategory = HelpParagraphBuilder.GetProperty(this.psObj, "Category").Value.ToString(); + + HelpCategory category = HelpCategory.Default; + + if (string.Equals(strCategory, "DscResource", StringComparison.OrdinalIgnoreCase)) + { + category = HelpCategory.DscResource; + } + else if (string.Equals(strCategory, "Class", StringComparison.OrdinalIgnoreCase)) + { + category = HelpCategory.Class; + } + + if (HelpParagraphBuilder.GetProperty(this.psObj, "Syntax") == null) + { + if (category == HelpCategory.Default) + { + // if there is no syntax, this is not the standard help + // it might be an about page + this.AddText(this.psObj.ToString(), false); + return; + } + } + + switch (category) + { + case HelpCategory.Class: + this.AddDescription(HelpWindowSettings.Default.HelpSynopsysDisplayed, HelpWindowResources.SynopsisTitle, "Introduction"); + this.AddMembers(HelpWindowSettings.Default.HelpParametersDisplayed, HelpWindowResources.PropertiesTitle); + this.AddMembers(HelpWindowSettings.Default.HelpParametersDisplayed, HelpWindowResources.MethodsTitle); + break; + case HelpCategory.DscResource: + this.AddStringSection(HelpWindowSettings.Default.HelpSynopsysDisplayed, "Synopsis", HelpWindowResources.SynopsisTitle); + this.AddDescription(HelpWindowSettings.Default.HelpDescriptionDisplayed, HelpWindowResources.DescriptionTitle, "Description"); + this.AddParameters(HelpWindowSettings.Default.HelpParametersDisplayed, HelpWindowResources.PropertiesTitle, "Properties", HelpCategory.DscResource); + break; + default: + this.AddStringSection(HelpWindowSettings.Default.HelpSynopsysDisplayed, "Synopsis", HelpWindowResources.SynopsisTitle); + this.AddDescription(HelpWindowSettings.Default.HelpDescriptionDisplayed, HelpWindowResources.DescriptionTitle, "Description"); + this.AddParameters(HelpWindowSettings.Default.HelpParametersDisplayed, HelpWindowResources.ParametersTitle, "Parameters", HelpCategory.Default); + this.AddSyntax(HelpWindowSettings.Default.HelpSyntaxDisplayed, HelpWindowResources.SyntaxTitle); + break; + } + + this.AddInputOrOutputEntries(HelpWindowSettings.Default.HelpInputsDisplayed, HelpWindowResources.InputsTitle, "inputTypes", "inputType"); + this.AddInputOrOutputEntries(HelpWindowSettings.Default.HelpOutputsDisplayed, HelpWindowResources.OutputsTitle, "returnValues", "returnValue"); + this.AddNotes(HelpWindowSettings.Default.HelpNotesDisplayed, HelpWindowResources.NotesTitle); + this.AddExamples(HelpWindowSettings.Default.HelpExamplesDisplayed, HelpWindowResources.ExamplesTitle); + this.AddNavigationLink(HelpWindowSettings.Default.HelpRelatedLinksDisplayed, HelpWindowResources.RelatedLinksTitle); + this.AddStringSection(HelpWindowSettings.Default.HelpRemarksDisplayed, "Remarks", HelpWindowResources.RemarksTitle); + } + + /// + /// Gets the object property or null if it could not be retrieved. + /// + /// Object with the property. + /// Property name. + /// The object property or null if it could not be retrieved. + private static PSPropertyInfo GetProperty(PSObject psObj, string propertyName) + { + Debug.Assert(psObj != null, "ensured by caller"); + return psObj.Properties[propertyName]; + } + + /// + /// Gets a PSObject and then a value from it or null if the value could not be retrieved. + /// + /// PSObject that contains another PSObject as a property. + /// Property name that contains the PSObject. + /// Property name in the inner PSObject. + /// The string from the inner psObject property or null if it could not be retrieved. + private static string GetInnerPSObjectPropertyString(PSObject psObj, string psObjectName, string propertyName) + { + Debug.Assert(psObj != null, "ensured by caller"); + PSObject innerPsObj = GetPropertyObject(psObj, psObjectName) as PSObject; + + if (innerPsObj == null) + { + return null; + } + + object value = GetPropertyObject(innerPsObj, propertyName); + + if (value == null) + { + return null; + } + + return value.ToString(); + } + + /// + /// Gets the value of a property or null if the value could not be retrieved. + /// + /// Object with the property. + /// Property name. + /// The value of a property or null if the value could not be retrieved. + private static object GetPropertyObject(PSObject psObj, string propertyName) + { + Debug.Assert(psObj != null, "ensured by caller"); + PSPropertyInfo property = HelpParagraphBuilder.GetProperty(psObj, propertyName); + if (property == null) + { + return null; + } + + object value = null; + try + { + value = property.Value; + } + catch (ExtendedTypeSystemException) + { + // ignore this exception + } + + return value; + } + + /// + /// Gets the text from a property of type PSObject[] where the first object has a text property. + /// + /// Objhect to get text from. + /// Property with PSObject[] containing text. + /// The text from a property of type PSObject[] where the first object has a text property. + private static string GetTextFromArray(PSObject psObj, string propertyText) + { + PSObject[] introductionObjects = HelpParagraphBuilder.GetPropertyObject(psObj, propertyText) as PSObject[]; + if (introductionObjects != null && introductionObjects.Length > 0) + { + return GetPropertyString(introductionObjects[0], "text"); + } + + return null; + } + + /// + /// Returns the largest size of a group of strings. + /// + /// Strings to evaluate the largest size from. + /// The largest size of a group of strings. + private static int LargestSize(params string[] strs) + { + int returnValue = 0; + + foreach (string str in strs) + { + if (str != null && str.Length > returnValue) + { + returnValue = str.Length; + } + } + + return returnValue; + } + + /// + /// Splits the string adding indentation before each line. + /// + /// String to add indentation to. + /// The string indented. + private static string AddIndent(string str) + { + return HelpParagraphBuilder.AddIndent(str, 1); + } + + /// + /// Splits the string adding indentation before each line. + /// + /// String to add indentation to. + /// Number of indentations. + /// The string indented. + private static string AddIndent(string str, int numberOfIdents) + { + StringBuilder indent = new StringBuilder(); + indent.Append(' ', numberOfIdents * HelpParagraphBuilder.IndentSize); + return HelpParagraphBuilder.AddIndent(str, indent.ToString()); + } + + /// + /// Splits the string adding indentation before each line. + /// + /// String to add indentation to. + /// Indentation string. + /// The string indented. + private static string AddIndent(string str, string indentString) + { + if (str == null) + { + return string.Empty; + } + + string[] lines = str.Split(Separators, StringSplitOptions.None); + + StringBuilder returnValue = new StringBuilder(); + foreach (string line in lines) + { + // Indentation is not localized + returnValue.Append($"{indentString}{line}\r\n"); + } + + if (returnValue.Length > 2) + { + // remove the last \r\n + returnValue.Remove(returnValue.Length - 2, 2); + } + + return returnValue.ToString(); + } + + /// + /// Get the object array value of a property. + /// + /// Object containing the property. + /// Property with the array value. + /// The object array value of a property. + private static object[] GetPropertyObjectArray(PSObject obj, string propertyName) + { + object innerObject; + if ((innerObject = HelpParagraphBuilder.GetPropertyObject(obj, propertyName)) == null) + { + return null; + } + + if (innerObject is PSObject) + { + return new[] { innerObject }; + } + + object[] innerObjectArray = innerObject as object[]; + return innerObjectArray; + } + + /// + /// Adds a section that contains only a string. + /// + /// True if it should add the segment. + /// Name of the section to add. + /// Title of the section. + private void AddStringSection(bool setting, string sectionName, string sectionTitle) + { + string propertyValue; + if (!setting || (propertyValue = HelpParagraphBuilder.GetPropertyString(this.psObj, sectionName)) == null) + { + return; + } + + this.AddText(sectionTitle, true); + this.AddText("\r\n", false); + this.AddText(HelpParagraphBuilder.AddIndent(propertyValue), false); + this.AddText("\r\n\r\n", false); + } + + /// + /// Adds the help syntax segment. + /// + /// True if it should add the segment. + /// Title of the section. + private void AddSyntax(bool setting, string sectionTitle) + { + PSObject syntaxObject; + if (!setting || (syntaxObject = HelpParagraphBuilder.GetPropertyObject(this.psObj, "Syntax") as PSObject) == null) + { + return; + } + + object[] syntaxItemsObj = HelpParagraphBuilder.GetPropertyObjectArray(syntaxObject, "syntaxItem"); + if (syntaxItemsObj == null || syntaxItemsObj.Length == 0) + { + return; + } + + this.AddText(sectionTitle, true); + this.AddText("\r\n", false); + + foreach (object syntaxItemObj in syntaxItemsObj) + { + PSObject syntaxItem = syntaxItemObj as PSObject; + if (syntaxItem == null) + { + continue; + } + + string commandName = GetPropertyString(syntaxItem, "name"); + + object[] parameterObjs = HelpParagraphBuilder.GetPropertyObjectArray(syntaxItem, "parameter"); + if (commandName == null || parameterObjs == null || parameterObjs.Length == 0) + { + continue; + } + + string commandStart = string.Create(CultureInfo.CurrentCulture, $"{commandName} "); + this.AddText(HelpParagraphBuilder.AddIndent(commandStart), false); + + foreach (object parameterObj in parameterObjs) + { + PSObject parameter = parameterObj as PSObject; + if (parameter == null) + { + continue; + } + + string parameterValue = GetPropertyString(parameter, "parameterValue"); + string position = GetPropertyString(parameter, "position"); + string required = GetPropertyString(parameter, "required"); + string parameterName = GetPropertyString(parameter, "name"); + if (position == null || required == null || parameterName == null) + { + continue; + } + + string parameterType = parameterValue == null ? string.Empty : string.Create(CultureInfo.CurrentCulture, $"<{parameterValue}>"); + + string parameterOptionalOpenBrace, parameterOptionalCloseBrace; + + if (string.Equals(required, "true", StringComparison.OrdinalIgnoreCase)) + { + parameterOptionalOpenBrace = string.Empty; + parameterOptionalCloseBrace = string.Empty; + } + else + { + parameterOptionalOpenBrace = "["; + parameterOptionalCloseBrace = "]"; + } + + string parameterNameOptionalOpenBrace, parameterNameOptionalCloseBrace; + + if (string.Equals(position, "named", StringComparison.OrdinalIgnoreCase)) + { + parameterNameOptionalOpenBrace = parameterNameOptionalCloseBrace = string.Empty; + } + else + { + parameterNameOptionalOpenBrace = "["; + parameterNameOptionalCloseBrace = "]"; + } + + string paramterPrefix = string.Format( + CultureInfo.CurrentCulture, + "{0}{1}-", + parameterOptionalOpenBrace, + parameterNameOptionalOpenBrace); + + this.AddText(paramterPrefix, false); + this.AddText(parameterName, true); + + string paramterSuffix = string.Format( + CultureInfo.CurrentCulture, + "{0} {1}{2} ", + parameterNameOptionalCloseBrace, + parameterType, + parameterOptionalCloseBrace); + this.AddText(paramterSuffix, false); + } + + string commonParametersText = string.Format( + CultureInfo.CurrentCulture, + "[<{0}>]\r\n\r\n", + HelpWindowResources.CommonParameters); + + this.AddText(commonParametersText, false); + } + + this.AddText("\r\n", false); + } + + /// + /// Adds the help description segment. + /// + /// True if it should add the segment. + /// Title of the section. + /// PropertyName that has description. + private void AddDescription(bool setting, string sectionTitle, string propertyName) + { + PSObject[] descriptionObjects; + if (!setting || + (descriptionObjects = HelpParagraphBuilder.GetPropertyObject(this.psObj, propertyName) as PSObject[]) == null || + descriptionObjects.Length == 0) + { + return; + } + + this.AddText(sectionTitle, true); + this.AddText("\r\n", false); + + foreach (PSObject description in descriptionObjects) + { + string descriptionText = GetPropertyString(description, "text"); + this.AddText(HelpParagraphBuilder.AddIndent(descriptionText), false); + this.AddText("\r\n", false); + } + + this.AddText("\r\n\r\n", false); + } + + /// + /// Adds the help examples segment. + /// + /// True if it should add the segment. + /// Title of the section. + private void AddExamples(bool setting, string sectionTitle) + { + if (!setting) + { + return; + } + + PSObject exampleRootObject = HelpParagraphBuilder.GetPropertyObject(this.psObj, "Examples") as PSObject; + if (exampleRootObject == null) + { + return; + } + + object[] exampleObjects = HelpParagraphBuilder.GetPropertyObjectArray(exampleRootObject, "example"); + if (exampleObjects == null || exampleObjects.Length == 0) + { + return; + } + + this.AddText(sectionTitle, true); + this.AddText("\r\n", false); + + foreach (object exampleObj in exampleObjects) + { + PSObject example = exampleObj as PSObject; + if (example == null) + { + continue; + } + + string introductionText = null; + introductionText = GetTextFromArray(example, "introduction"); + + string codeText = GetPropertyString(example, "code"); + string title = GetPropertyString(example, "title"); + + if (codeText == null) + { + continue; + } + + if (title != null) + { + this.AddText(HelpParagraphBuilder.AddIndent(title), false); + this.AddText("\r\n", false); + } + + string codeLine = string.Format( + CultureInfo.CurrentCulture, + "{0}{1}\r\n\r\n", + introductionText, + codeText); + + this.AddText(HelpParagraphBuilder.AddIndent(codeLine), false); + + PSObject[] remarks = HelpParagraphBuilder.GetPropertyObject(example, "remarks") as PSObject[]; + if (remarks == null) + { + continue; + } + + foreach (PSObject remark in remarks) + { + string remarkText = GetPropertyString(remark, "text"); + if (remarkText == null) + { + continue; + } + + this.AddText(remarkText, false); + this.AddText("\r\n", false); + } + } + + this.AddText("\r\n\r\n", false); + } + + private void AddMembers(bool setting, string sectionTitle) + { + if (!setting || string.IsNullOrEmpty(sectionTitle)) + { + return; + } + + PSObject memberRootObject = HelpParagraphBuilder.GetPropertyObject(this.psObj, "Members") as PSObject; + if (memberRootObject == null) + { + return; + } + + object[] memberObjects = HelpParagraphBuilder.GetPropertyObjectArray(memberRootObject, "member"); + + if (memberObjects == null) + { + return; + } + + this.AddText(sectionTitle, true); + this.AddText("\r\n", false); + + foreach (object memberObj in memberObjects) + { + string description = null; + string memberText = null; + + PSObject member = memberObj as PSObject; + if (member == null) + { + continue; + } + + string name = GetPropertyString(member, "title"); + string type = GetPropertyString(member, "type"); + string propertyType = null; + + if (string.Equals("field", type, StringComparison.OrdinalIgnoreCase)) + { + PSObject fieldData = HelpParagraphBuilder.GetPropertyObject(member, "fieldData") as PSObject; + + if (fieldData != null) + { + PSObject propertyTypeObject = HelpParagraphBuilder.GetPropertyObject(fieldData, "type") as PSObject; + if (propertyTypeObject != null) + { + propertyType = GetPropertyString(propertyTypeObject, "name"); + description = GetPropertyString(propertyTypeObject, "description"); + } + + memberText = string.Create(CultureInfo.CurrentCulture, $" [{propertyType}] {name}\r\n"); + } + } + else if (string.Equals("method", type, StringComparison.OrdinalIgnoreCase)) + { + FormatMethodData(member, name, out memberText, out description); + } + + if (!string.IsNullOrEmpty(memberText)) + { + this.AddText(HelpParagraphBuilder.AddIndent(string.Empty), false); + this.AddText(memberText, true); + + if (description != null) + { + this.AddText(HelpParagraphBuilder.AddIndent(description, 2), false); + this.AddText("\r\n", false); + } + + this.AddText("\r\n", false); + } + } + } + + private static void FormatMethodData(PSObject member, string name, out string memberText, out string description) + { + memberText = null; + description = null; + + if (member == null || string.IsNullOrEmpty(name)) + { + return; + } + + string returnType = null; + StringBuilder parameterText = new StringBuilder(); + + // Get method return type + PSObject returnTypeObject = HelpParagraphBuilder.GetPropertyObject(member, "returnValue") as PSObject; + if (returnTypeObject != null) + { + PSObject returnTypeData = HelpParagraphBuilder.GetPropertyObject(returnTypeObject, "type") as PSObject; + if (returnTypeData != null) + { + returnType = GetPropertyString(returnTypeData, "name"); + } + } + + // Get method description. + PSObject[] methodDescriptions = HelpParagraphBuilder.GetPropertyObject(member, "introduction") as PSObject[]; + if (methodDescriptions != null) + { + foreach (var methodDescription in methodDescriptions) + { + description = GetPropertyString(methodDescription, "Text"); + + // If we get an text we do not need to iterate more. + if (!string.IsNullOrEmpty(description)) + { + break; + } + } + } + + // Get method parameters. + PSObject parametersObject = HelpParagraphBuilder.GetPropertyObject(member, "parameters") as PSObject; + if (parametersObject != null) + { + PSObject[] paramObject = HelpParagraphBuilder.GetPropertyObject(parametersObject, "parameter") as PSObject[]; + + if (paramObject != null) + { + foreach (var param in paramObject) + { + string parameterName = GetPropertyString(param, "name"); + string parameterType = null; + + PSObject parameterTypeData = HelpParagraphBuilder.GetPropertyObject(param, "type") as PSObject; + + if (parameterTypeData != null) + { + parameterType = GetPropertyString(parameterTypeData, "name"); + + // If there is no type for the parameter, we expect it is System.Object + if (string.IsNullOrEmpty(parameterType)) + { + parameterType = "object"; + } + } + + string paramString = string.Create(CultureInfo.CurrentCulture, $"[{parameterType}] ${parameterName},"); + + parameterText.Append(paramString); + } + + if (string.Equals(parameterText[parameterText.Length - 1].ToString(), ",", StringComparison.OrdinalIgnoreCase)) + { + parameterText = parameterText.Remove(parameterText.Length - 1, 1); + } + } + } + + memberText = string.Create(CultureInfo.CurrentCulture, $" [{returnType}] {name}({parameterText})\r\n"); + } + + /// + /// Adds the help parameters segment. + /// + /// True if it should add the segment. + /// Title of the section. + /// Name of the property which has properties. + /// Category of help. + private void AddParameters(bool setting, string sectionTitle, string paramPropertyName, HelpCategory helpCategory) + { + if (!setting) + { + return; + } + + PSObject parameterRootObject = HelpParagraphBuilder.GetPropertyObject(this.psObj, paramPropertyName) as PSObject; + if (parameterRootObject == null) + { + return; + } + + object[] parameterObjects = null; + + // Root object for Class has members not parameters. + if (helpCategory != HelpCategory.Class) + { + parameterObjects = HelpParagraphBuilder.GetPropertyObjectArray(parameterRootObject, "parameter"); + } + + if (parameterObjects == null || parameterObjects.Length == 0) + { + return; + } + + this.AddText(sectionTitle, true); + this.AddText("\r\n", false); + + foreach (object parameterObj in parameterObjects) + { + PSObject parameter = parameterObj as PSObject; + if (parameter == null) + { + continue; + } + + string parameterValue = GetPropertyString(parameter, "parameterValue"); + string name = GetPropertyString(parameter, "name"); + string description = GetTextFromArray(parameter, "description"); + string required = GetPropertyString(parameter, "required"); + string position = GetPropertyString(parameter, "position"); + string pipelineinput = GetPropertyString(parameter, "pipelineInput"); + string defaultValue = GetPropertyString(parameter, "defaultValue"); + string acceptWildcard = GetPropertyString(parameter, "globbing"); + + if (string.IsNullOrEmpty(name)) + { + continue; + } + + if (helpCategory == HelpCategory.DscResource) + { + this.AddText(HelpParagraphBuilder.AddIndent(string.Empty), false); + } + else + { + this.AddText(HelpParagraphBuilder.AddIndent("-"), false); + } + + this.AddText(name, true); + string parameterText = string.Format( + CultureInfo.CurrentCulture, + " <{0}>\r\n", + parameterValue); + + this.AddText(parameterText, false); + + if (description != null) + { + this.AddText(HelpParagraphBuilder.AddIndent(description, 2), false); + this.AddText("\r\n", false); + } + + this.AddText("\r\n", false); + + int largestSize = HelpParagraphBuilder.LargestSize( + HelpWindowResources.ParameterRequired, + HelpWindowResources.ParameterPosition, + HelpWindowResources.ParameterDefaultValue, + HelpWindowResources.ParameterPipelineInput, + HelpWindowResources.ParameterAcceptWildcard); + + // justification of parameter values is not localized + string formatString = string.Format( + CultureInfo.CurrentCulture, + "{{0,-{0}}}{{1}}", + largestSize + 2); + + string tableLine; + + tableLine = string.Format( + CultureInfo.CurrentCulture, + formatString, + HelpWindowResources.ParameterRequired, + required); + this.AddText(HelpParagraphBuilder.AddIndent(tableLine, 2), false); + this.AddText("\r\n", false); + + // these are not applicable for Dsc Resource help + if (helpCategory != HelpCategory.DscResource) + { + tableLine = string.Format( + CultureInfo.CurrentCulture, + formatString, + HelpWindowResources.ParameterPosition, + position); + this.AddText(HelpParagraphBuilder.AddIndent(tableLine, 2), false); + this.AddText("\r\n", false); + + tableLine = string.Format( + CultureInfo.CurrentCulture, + formatString, + HelpWindowResources.ParameterDefaultValue, + defaultValue); + this.AddText(HelpParagraphBuilder.AddIndent(tableLine, 2), false); + this.AddText("\r\n", false); + + tableLine = string.Format( + CultureInfo.CurrentCulture, + formatString, + HelpWindowResources.ParameterPipelineInput, + pipelineinput); + this.AddText(HelpParagraphBuilder.AddIndent(tableLine, 2), false); + this.AddText("\r\n", false); + + tableLine = string.Format( + CultureInfo.CurrentCulture, + formatString, + HelpWindowResources.ParameterAcceptWildcard, + acceptWildcard); + this.AddText(HelpParagraphBuilder.AddIndent(tableLine, 2), false); + } + + this.AddText("\r\n\r\n", false); + } + + this.AddText("\r\n\r\n", false); + } + + /// + /// Adds the help navigation links segment. + /// + /// True if it should add the segment. + /// Title of the section. + private void AddNavigationLink(bool setting, string sectionTitle) + { + if (!setting) + { + return; + } + + PSObject linkRootObject = HelpParagraphBuilder.GetPropertyObject(this.psObj, "RelatedLinks") as PSObject; + if (linkRootObject == null) + { + return; + } + + PSObject[] linkObjects; + + if ((linkObjects = HelpParagraphBuilder.GetPropertyObject(linkRootObject, "navigationLink") as PSObject[]) == null || + linkObjects.Length == 0) + { + return; + } + + this.AddText(sectionTitle, true); + this.AddText("\r\n", false); + + foreach (PSObject linkObject in linkObjects) + { + string text = GetPropertyString(linkObject, "linkText"); + string uri = GetPropertyString(linkObject, "uri"); + + string linkLine = string.IsNullOrEmpty(uri) ? text : string.Format( + CultureInfo.CurrentCulture, + HelpWindowResources.LinkTextFormat, + text, + uri); + + this.AddText(HelpParagraphBuilder.AddIndent(linkLine), false); + this.AddText("\r\n", false); + } + + this.AddText("\r\n\r\n", false); + } + + /// + /// Adds the help input or output segment. + /// + /// True if it should add the segment. + /// Title of the section. + /// Property with the outter object. + /// Property with the inner object. + private void AddInputOrOutputEntries(bool setting, string sectionTitle, string inputOrOutputProperty, string inputOrOutputInnerProperty) + { + if (!setting) + { + return; + } + + PSObject rootObject = HelpParagraphBuilder.GetPropertyObject(this.psObj, inputOrOutputProperty) as PSObject; + if (rootObject == null) + { + return; + } + + object[] inputOrOutputObjs; + inputOrOutputObjs = HelpParagraphBuilder.GetPropertyObjectArray(rootObject, inputOrOutputInnerProperty); + + if (inputOrOutputObjs == null || inputOrOutputObjs.Length == 0) + { + return; + } + + this.AddText(sectionTitle, true); + this.AddText("\r\n", false); + + foreach (object inputOrOutputObj in inputOrOutputObjs) + { + PSObject inputOrOutput = inputOrOutputObj as PSObject; + if (inputOrOutput == null) + { + continue; + } + + string type = HelpParagraphBuilder.GetInnerPSObjectPropertyString(inputOrOutput, "type", "name"); + string description = GetTextFromArray(inputOrOutput, "description"); + + this.AddText(HelpParagraphBuilder.AddIndent(type), false); + this.AddText("\r\n", false); + if (description != null) + { + this.AddText(HelpParagraphBuilder.AddIndent(description), false); + this.AddText("\r\n", false); + } + } + + this.AddText("\r\n", false); + } + + /// + /// Adds the help notes segment. + /// + /// True if it should add the segment. + /// Title of the section. + private void AddNotes(bool setting, string sectionTitle) + { + if (!setting) + { + return; + } + + PSObject rootObject = HelpParagraphBuilder.GetPropertyObject(this.psObj, "alertSet") as PSObject; + if (rootObject == null) + { + return; + } + + string note = GetTextFromArray(rootObject, "alert"); + + if (note == null) + { + return; + } + + this.AddText(sectionTitle, true); + this.AddText("\r\n", false); + this.AddText(HelpParagraphBuilder.AddIndent(note), false); + this.AddText("\r\n\r\n", false); + } + } +} diff --git a/src/Microsoft.Management.UI.Internal/HelpWindow/HelpViewModel.cs b/src/Microsoft.Management.UI.Internal/HelpWindow/HelpViewModel.cs new file mode 100644 index 00000000000..7ab8681fef4 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/HelpWindow/HelpViewModel.cs @@ -0,0 +1,283 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.ComponentModel; +using System.Diagnostics; +using System.Globalization; +using System.Management.Automation; +using System.Windows.Documents; + +namespace Microsoft.Management.UI.Internal +{ + /// + /// ViewModel for the Help Dialog used to: + /// build the help document + /// search the help document + /// offer text for labels. + /// + internal class HelpViewModel : INotifyPropertyChanged + { + /// + /// The builder for the help FlowDocument Paragraph used in a RichEditText control. + /// + private readonly HelpParagraphBuilder helpBuilder; + + /// + /// Searcher for selecting current matches in paragraph text. + /// + private readonly ParagraphSearcher searcher; + + /// + /// Title of the help window. + /// + private readonly string helpTitle; + + /// + /// the zoom bound to the zoom slider value. + /// + private double zoom = 100; + + /// + /// Text to be found. This is bound to the find TextBox. + /// + private string findText; + + /// + /// text for the number of matches found. + /// + private string matchesLabel; + + /// + /// Initializes a new instance of the HelpViewModel class. + /// + /// Object containing help. + /// Paragraph in which help text is built/searched. + internal HelpViewModel(PSObject psObj, Paragraph documentParagraph) + { + Debug.Assert(psObj != null, "ensured by caller"); + Debug.Assert(documentParagraph != null, "ensured by caller"); + + this.helpBuilder = new HelpParagraphBuilder(documentParagraph, psObj); + this.helpBuilder.BuildParagraph(); + this.searcher = new ParagraphSearcher(); + this.helpBuilder.PropertyChanged += this.HelpBuilder_PropertyChanged; + this.helpTitle = string.Format( + CultureInfo.CurrentCulture, + HelpWindowResources.HelpTitleFormat, + HelpParagraphBuilder.GetPropertyString(psObj, "name")); + } + + #region INotifyPropertyChanged Members + /// + /// Used to notify of property changes. + /// + public event PropertyChangedEventHandler PropertyChanged; + #endregion + + /// + /// Gets or sets the Zoom bound to the zoom slider value. + /// + public double Zoom + { + get + { + return this.zoom; + } + + set + { + this.zoom = value; + this.OnNotifyPropertyChanged("Zoom"); + this.OnNotifyPropertyChanged("ZoomLabel"); + this.OnNotifyPropertyChanged("ZoomLevel"); + } + } + + /// + /// Gets the value bound to the RichTextEdit zoom, which is calculated based on the zoom. + /// + public double ZoomLevel + { + get + { + return this.zoom / 100.0; + } + } + + /// + /// Gets the label to be displayed for the zoom. + /// + public string ZoomLabel + { + get + { + return string.Format(CultureInfo.CurrentCulture, HelpWindowResources.ZoomLabelTextFormat, this.zoom); + } + } + + /// + /// Gets or sets the text to be found. + /// + public string FindText + { + get + { + return this.findText; + } + + set + { + this.findText = value; + this.Search(); + this.SetMatchesLabel(); + } + } + + /// + /// Gets the title of the window. + /// + public string HelpTitle + { + get + { + return this.helpTitle; + } + } + + /// + /// Gets or sets the label for current matches. + /// + public string MatchesLabel + { + get + { + return this.matchesLabel; + } + + set + { + this.matchesLabel = value; + this.OnNotifyPropertyChanged("MatchesLabel"); + } + } + + /// + /// Gets a value indicating whether there are matches to go to. + /// + public bool CanGoToNextOrPrevious + { + get + { + return this.HelpBuilder.HighlightCount != 0; + } + } + + /// + /// Gets the searcher for selecting current matches in paragraph text. + /// + internal ParagraphSearcher Searcher + { + get { return this.searcher; } + } + + /// + /// Gets the paragraph builder used to write help content. + /// + internal HelpParagraphBuilder HelpBuilder + { + get { return this.helpBuilder; } + } + + /// + /// Highlights all matches to this.findText + /// Called when findText changes or whenever the search has to be refreshed + /// + internal void Search() + { + this.HelpBuilder.HighlightAllInstancesOf(this.findText, HelpWindowSettings.Default.HelpSearchMatchCase, HelpWindowSettings.Default.HelpSearchWholeWord); + this.searcher.ResetSearch(); + } + + /// + /// Increases Zoom if not above maximum. + /// + internal void ZoomIn() + { + if (this.Zoom + HelpWindow.ZoomInterval <= HelpWindow.MaximumZoom) + { + this.Zoom += HelpWindow.ZoomInterval; + } + } + + /// + /// Decreases Zoom if not below minimum. + /// + internal void ZoomOut() + { + if (this.Zoom - HelpWindow.ZoomInterval >= HelpWindow.MinimumZoom) + { + this.Zoom -= HelpWindow.ZoomInterval; + } + } + + /// + /// Called to update the matches label. + /// + /// Event sender. + /// Event arguments. + private void HelpBuilder_PropertyChanged(object sender, PropertyChangedEventArgs e) + { + if (e.PropertyName == "HighlightCount") + { + this.SetMatchesLabel(); + this.OnNotifyPropertyChanged("CanGoToNextOrPrevious"); + } + } + + /// + /// Sets the current matches label. + /// + private void SetMatchesLabel() + { + if (this.findText == null || this.findText.Trim().Length == 0) + { + this.MatchesLabel = string.Empty; + } + else + { + if (this.HelpBuilder.HighlightCount == 0) + { + this.MatchesLabel = HelpWindowResources.NoMatches; + } + else + { + if (this.HelpBuilder.HighlightCount == 1) + { + this.MatchesLabel = HelpWindowResources.OneMatch; + } + else + { + this.MatchesLabel = string.Format( + CultureInfo.CurrentCulture, + HelpWindowResources.SomeMatchesFormat, + this.HelpBuilder.HighlightCount); + } + } + } + } + + /// + /// Called internally to notify when a property changed. + /// + /// Property name. + private void OnNotifyPropertyChanged(string propertyName) + { + PropertyChangedEventHandler handler = this.PropertyChanged; + if (handler != null) + { + handler(this, new PropertyChangedEventArgs(propertyName)); + } + } + } +} diff --git a/src/Microsoft.Management.UI.Internal/HelpWindow/HelpWindow.xaml b/src/Microsoft.Management.UI.Internal/HelpWindow/HelpWindow.xaml new file mode 100644 index 00000000000..2b2a8fe60b1 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/HelpWindow/HelpWindow.xaml @@ -0,0 +1,76 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Microsoft.Management.UI.Internal/HelpWindow/HelpWindow.xaml.cs b/src/Microsoft.Management.UI.Internal/HelpWindow/HelpWindow.xaml.cs new file mode 100644 index 00000000000..6793806503a --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/HelpWindow/HelpWindow.xaml.cs @@ -0,0 +1,318 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.Globalization; +using System.Management.Automation; +using System.Windows; +using System.Windows.Documents; +using System.Windows.Input; + +using Microsoft.Management.UI.Internal; + +namespace Microsoft.Management.UI +{ + /// + /// A window displaying help content and allowing search. + /// + public partial class HelpWindow : Window + { + /// + /// Minimum zoom in the slider. + /// + public static double MinimumZoom + { + get + { + return 20; + } + } + + /// + /// Maximum zoom in the slider. + /// + public static double MaximumZoom + { + get + { + return 300; + } + } + + /// + /// Zoom interval. + /// + public static double ZoomInterval + { + get + { + return 10; + } + } + + /// + /// The ViewModel for the dialog. + /// + private readonly HelpViewModel viewModel; + + /// + /// Initializes a new instance of the HelpWindow class. + /// + /// The object with help information. + public HelpWindow(PSObject helpObject) + { + InitializeComponent(); + this.viewModel = new HelpViewModel(helpObject, this.DocumentParagraph); + CommonHelper.SetStartingPositionAndSize( + this, + HelpWindowSettings.Default.HelpWindowTop, + HelpWindowSettings.Default.HelpWindowLeft, + HelpWindowSettings.Default.HelpWindowWidth, + HelpWindowSettings.Default.HelpWindowHeight, + double.Parse((string)HelpWindowSettings.Default.Properties["HelpWindowWidth"].DefaultValue, CultureInfo.InvariantCulture.NumberFormat), + double.Parse((string)HelpWindowSettings.Default.Properties["HelpWindowHeight"].DefaultValue, CultureInfo.InvariantCulture.NumberFormat), + HelpWindowSettings.Default.HelpWindowMaximized); + + this.ReadZoomUserSetting(); + + this.viewModel.PropertyChanged += this.ViewModel_PropertyChanged; + this.DataContext = this.viewModel; + + this.Loaded += this.HelpDialog_Loaded; + this.Closed += this.HelpDialog_Closed; + } + + /// + /// Handles the mouse wheel to zoom in/out. + /// + /// Event arguments. + protected override void OnPreviewMouseWheel(MouseWheelEventArgs e) + { + if (Keyboard.Modifiers != ModifierKeys.Control) + { + return; + } + + if (e.Delta > 0) + { + this.viewModel.ZoomIn(); + e.Handled = true; + } + else + { + this.viewModel.ZoomOut(); + e.Handled = true; + } + } + + /// + /// Handles key down to fix the Page/Douwn going to end of help issue + /// And to implement some additional shortcuts like Ctrl+F and ZoomIn/ZoomOut. + /// + /// Event arguments. + protected override void OnPreviewKeyDown(KeyEventArgs e) + { + if (Keyboard.Modifiers == ModifierKeys.None) + { + if (e.Key == Key.PageUp) + { + this.Scroll.PageUp(); + e.Handled = true; + return; + } + + if (e.Key == Key.PageDown) + { + this.Scroll.PageDown(); + e.Handled = true; + return; + } + } + + if (Keyboard.Modifiers == ModifierKeys.Control) + { + this.HandleZoomInAndZoomOut(e); + if (e.Handled) + { + return; + } + + if (e.Key == Key.F) + { + this.Find.Focus(); + e.Handled = true; + return; + } + } + + if (Keyboard.Modifiers == (ModifierKeys.Control | ModifierKeys.Shift)) + { + this.HandleZoomInAndZoomOut(e); + } + } + + /// + /// Reads the zoom part of the user settings. + /// + private void ReadZoomUserSetting() + { + if (HelpWindowSettings.Default.HelpZoom < HelpWindow.MinimumZoom || HelpWindowSettings.Default.HelpZoom > HelpWindow.MaximumZoom) + { + HelpWindowSettings.Default.HelpZoom = 100; + } + + this.viewModel.Zoom = HelpWindowSettings.Default.HelpZoom; + } + + /// + /// Handles Zoom in and Zoom out keys. + /// + /// Event arguments. + private void HandleZoomInAndZoomOut(KeyEventArgs e) + { + if (e.Key == Key.OemPlus || e.Key == Key.Add) + { + this.viewModel.ZoomIn(); + e.Handled = true; + } + + if (e.Key == Key.OemMinus || e.Key == Key.Subtract) + { + this.viewModel.ZoomOut(); + e.Handled = true; + } + } + + /// + /// Listens to changes in the zoom in order to update the user settings. + /// + /// Event sender. + /// Event arguments. + private void ViewModel_PropertyChanged(object sender, System.ComponentModel.PropertyChangedEventArgs e) + { + if (e.PropertyName == "Zoom") + { + HelpWindowSettings.Default.HelpZoom = this.viewModel.Zoom; + } + } + + /// + /// Saves the user settings. + /// + /// Event sender. + /// Event arguments. + private void HelpDialog_Closed(object sender, System.EventArgs e) + { + HelpWindowSettings.Default.Save(); + } + + /// + /// Updates the user setting with window state. + /// + /// Event sender. + /// Event arguments. + private void HelpDialog_StateChanged(object sender, System.EventArgs e) + { + HelpWindowSettings.Default.HelpWindowMaximized = this.WindowState == WindowState.Maximized; + } + + /// + /// Sets the positions from user settings and start monitoring position changes. + /// + /// Event sender. + /// Event arguments. + private void HelpDialog_Loaded(object sender, RoutedEventArgs e) + { + this.StateChanged += this.HelpDialog_StateChanged; + this.LocationChanged += this.HelpDialog_LocationChanged; + this.SizeChanged += this.HelpDialog_SizeChanged; + } + + /// + /// Saves size changes in user settings. + /// + /// Event sender. + /// Event arguments. + private void HelpDialog_SizeChanged(object sender, SizeChangedEventArgs e) + { + HelpWindowSettings.Default.HelpWindowWidth = this.Width; + HelpWindowSettings.Default.HelpWindowHeight = this.Height; + } + + /// + /// Saves position changes in user settings. + /// + /// Event sender. + /// Event arguments. + private void HelpDialog_LocationChanged(object sender, System.EventArgs e) + { + HelpWindowSettings.Default.HelpWindowTop = this.Top; + HelpWindowSettings.Default.HelpWindowLeft = this.Left; + } + + /// + /// Called when the settings button is clicked. + /// + /// Event sender. + /// Event arguments. + private void Settings_Click(object sender, RoutedEventArgs e) + { + SettingsDialog settings = new SettingsDialog(); + settings.Owner = this; + + settings.ShowDialog(); + + if (settings.DialogResult == true) + { + this.viewModel.HelpBuilder.AddTextToParagraphBuilder(); + this.viewModel.Search(); + } + } + + /// + /// Called when the Previous button is clicked. + /// + /// Event sender. + /// Event arguments. + private void PreviousMatch_Click(object sender, RoutedEventArgs e) + { + this.MoveToNextMatch(false); + } + + /// + /// Called when the Next button is clicked. + /// + /// Event sender. + /// Event arguments. + private void NextMatch_Click(object sender, RoutedEventArgs e) + { + this.MoveToNextMatch(true); + } + + /// + /// Moves to the previous or next match. + /// + /// True for forward false for backwards. + private void MoveToNextMatch(bool forward) + { + TextPointer caretPosition = this.HelpText.CaretPosition; + Run nextRun = this.viewModel.Searcher.MoveAndHighlightNextNextMatch(forward, caretPosition); + this.MoveToRun(nextRun); + } + + /// + /// Moves to the caret and brings the view to the . + /// + /// Run to move to. + private void MoveToRun(Run run) + { + if (run == null) + { + return; + } + + run.BringIntoView(); + this.HelpText.CaretPosition = run.ElementEnd; + this.HelpText.Focus(); + } + } +} diff --git a/src/Microsoft.Management.UI.Internal/HelpWindow/HelpWindowSettings.Designer.cs b/src/Microsoft.Management.UI.Internal/HelpWindow/HelpWindowSettings.Designer.cs new file mode 100644 index 00000000000..937429ebf09 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/HelpWindow/HelpWindowSettings.Designer.cs @@ -0,0 +1,247 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.16598 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +// namespace Microsoft.Management.UI.Internal { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "10.0.0.0")] + internal sealed partial class HelpWindowSettings : global::System.Configuration.ApplicationSettingsBase { + + private static HelpWindowSettings defaultInstance = ((HelpWindowSettings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new HelpWindowSettings()))); + + public static HelpWindowSettings Default { + get { + return defaultInstance; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool HelpRemarksDisplayed + { + get + { + return ((bool)(this["HelpRemarksDisplayed"])); + } + set + { + this["HelpRemarksDisplayed"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool HelpSyntaxDisplayed { + get { + return ((bool)(this["HelpSyntaxDisplayed"])); + } + set { + this["HelpSyntaxDisplayed"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool HelpExamplesDisplayed { + get { + return ((bool)(this["HelpExamplesDisplayed"])); + } + set { + this["HelpExamplesDisplayed"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool HelpSynopsysDisplayed { + get { + return ((bool)(this["HelpSynopsysDisplayed"])); + } + set { + this["HelpSynopsysDisplayed"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool HelpDescriptionDisplayed { + get { + return ((bool)(this["HelpDescriptionDisplayed"])); + } + set { + this["HelpDescriptionDisplayed"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool HelpParametersDisplayed { + get { + return ((bool)(this["HelpParametersDisplayed"])); + } + set { + this["HelpParametersDisplayed"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool HelpInputsDisplayed { + get { + return ((bool)(this["HelpInputsDisplayed"])); + } + set { + this["HelpInputsDisplayed"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool HelpOutputsDisplayed { + get { + return ((bool)(this["HelpOutputsDisplayed"])); + } + set { + this["HelpOutputsDisplayed"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool HelpNotesDisplayed { + get { + return ((bool)(this["HelpNotesDisplayed"])); + } + set { + this["HelpNotesDisplayed"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool HelpRelatedLinksDisplayed { + get { + return ((bool)(this["HelpRelatedLinksDisplayed"])); + } + set { + this["HelpRelatedLinksDisplayed"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool HelpSearchMatchCase { + get { + return ((bool)(this["HelpSearchMatchCase"])); + } + set { + this["HelpSearchMatchCase"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool HelpSearchWholeWord { + get { + return ((bool)(this["HelpSearchWholeWord"])); + } + set { + this["HelpSearchWholeWord"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("400")] + public double HelpWindowHeight { + get { + return ((double)(this["HelpWindowHeight"])); + } + set { + this["HelpWindowHeight"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("600")] + public double HelpWindowWidth { + get { + return ((double)(this["HelpWindowWidth"])); + } + set { + this["HelpWindowWidth"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("-1")] + public double HelpWindowTop { + get { + return ((double)(this["HelpWindowTop"])); + } + set { + this["HelpWindowTop"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("-1")] + public double HelpWindowLeft { + get { + return ((double)(this["HelpWindowLeft"])); + } + set { + this["HelpWindowLeft"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool HelpWindowMaximized { + get { + return ((bool)(this["HelpWindowMaximized"])); + } + set { + this["HelpWindowMaximized"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("100")] + public double HelpZoom { + get { + return ((double)(this["HelpZoom"])); + } + set { + this["HelpZoom"] = value; + } + } + } +//} diff --git a/src/Microsoft.Management.UI.Internal/HelpWindow/HelpWindowSettings.settings b/src/Microsoft.Management.UI.Internal/HelpWindow/HelpWindowSettings.settings new file mode 100644 index 00000000000..2631ff7c38a --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/HelpWindow/HelpWindowSettings.settings @@ -0,0 +1,60 @@ + + + + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + False + + + False + + + -1 + + + -1 + + + False + + + 100 + + + 500 + + + 700 + + + True + + + diff --git a/src/Microsoft.Management.UI.Internal/HelpWindow/ParagraphBuilder.cs b/src/Microsoft.Management.UI.Internal/HelpWindow/ParagraphBuilder.cs new file mode 100644 index 00000000000..77b1b5c966f --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/HelpWindow/ParagraphBuilder.cs @@ -0,0 +1,357 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Text; +using System.Windows.Documents; +using System.Windows.Media; + +namespace Microsoft.Management.UI.Internal +{ + /// + /// Builds a paragraph based on Text + Bold + Highlight information. + /// Bold are the segments of the text that should be bold, and Highlight are + /// the segments of the text that should be highlighted (like search results). + /// + internal class ParagraphBuilder : INotifyPropertyChanged + { + /// + /// The text spans that should be bold. + /// + private readonly List boldSpans; + + /// + /// The text spans that should be highlighted. + /// + private readonly List highlightedSpans; + + /// + /// The text displayed. + /// + private readonly StringBuilder textBuilder; + + /// + /// Paragraph built in BuildParagraph. + /// + private readonly Paragraph paragraph; + + /// + /// Initializes a new instance of the ParagraphBuilder class. + /// + /// Paragraph we will be adding lines to in BuildParagraph. + internal ParagraphBuilder(Paragraph paragraph) + { + ArgumentNullException.ThrowIfNull(paragraph); + + this.paragraph = paragraph; + this.boldSpans = new List(); + this.highlightedSpans = new List(); + this.textBuilder = new StringBuilder(); + } + + #region INotifyPropertyChanged Members + /// + /// Used to notify of property changes. + /// + public event PropertyChangedEventHandler PropertyChanged; + #endregion + + /// + /// Gets the number of highlights. + /// + internal int HighlightCount + { + get { return this.highlightedSpans.Count; } + } + + /// + /// Gets the paragraph built in BuildParagraph. + /// + internal Paragraph Paragraph + { + get { return this.paragraph; } + } + + /// + /// Called after all the AddText calls have been made to build the paragraph + /// based on the current text. + /// This method goes over 3 collections simultaneously: + /// 1) characters in this.textBuilder + /// 2) spans in this.boldSpans + /// 3) spans in this.highlightedSpans + /// And adds the minimal number of Inlines to the paragraph so that all + /// characters that should be bold and/or highlighted are. + /// + internal void BuildParagraph() + { + this.paragraph.Inlines.Clear(); + + int currentBoldIndex = 0; + TextSpan? currentBoldSpan = this.boldSpans.Count == 0 ? (TextSpan?)null : this.boldSpans[0]; + int currentHighlightedIndex = 0; + TextSpan? currentHighlightedSpan = this.highlightedSpans.Count == 0 ? (TextSpan?)null : this.highlightedSpans[0]; + + bool currentBold = false; + bool currentHighlighted = false; + + StringBuilder sequence = new StringBuilder(); + int i = 0; + foreach (char c in this.textBuilder.ToString()) + { + bool newBold = false; + bool newHighlighted = false; + + ParagraphBuilder.MoveSpanToPosition(ref currentBoldIndex, ref currentBoldSpan, i, this.boldSpans); + newBold = currentBoldSpan == null ? false : currentBoldSpan.Value.Contains(i); + + ParagraphBuilder.MoveSpanToPosition(ref currentHighlightedIndex, ref currentHighlightedSpan, i, this.highlightedSpans); + newHighlighted = currentHighlightedSpan == null ? false : currentHighlightedSpan.Value.Contains(i); + + if (newBold != currentBold || newHighlighted != currentHighlighted) + { + ParagraphBuilder.AddInline(this.paragraph, currentBold, currentHighlighted, sequence); + } + + sequence.Append(c); + + currentHighlighted = newHighlighted; + currentBold = newBold; + i++; + } + + ParagraphBuilder.AddInline(this.paragraph, currentBold, currentHighlighted, sequence); + } + + /// + /// Highlights all occurrences of . + /// This is called after all calls to AddText have been made. + /// + /// Search string. + /// True if search should be case sensitive. + /// True if we should search whole word only. + internal void HighlightAllInstancesOf(string search, bool caseSensitive, bool wholeWord) + { + this.highlightedSpans.Clear(); + + if (search == null || search.Trim().Length == 0) + { + this.BuildParagraph(); + this.OnNotifyPropertyChanged("HighlightCount"); + return; + } + + string text = this.textBuilder.ToString(); + StringComparison comparison = caseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase; + int start = 0; + int match; + while ((match = text.IndexOf(search, start, comparison)) != -1) + { + // false loop + do + { + if (wholeWord) + { + if (match > 0 && char.IsLetterOrDigit(text[match - 1])) + { + break; + } + + if ((match + search.Length <= text.Length - 1) && char.IsLetterOrDigit(text[match + search.Length])) + { + break; + } + } + + this.AddHighlight(match, search.Length); + } + while (false); + + start = match + search.Length; + } + + this.BuildParagraph(); + this.OnNotifyPropertyChanged("HighlightCount"); + } + + /// + /// Adds text to the paragraph later build with BuildParagraph. + /// + /// Text to be added. + /// True if the text should be bold. + internal void AddText(string str, bool bold) + { + ArgumentNullException.ThrowIfNull(str); + + if (str.Length == 0) + { + return; + } + + if (bold) + { + this.boldSpans.Add(new TextSpan(this.textBuilder.Length, str.Length)); + } + + this.textBuilder.Append(str); + } + + /// + /// Called before a derived class starts adding text + /// to reset the current content. + /// + internal void ResetAllText() + { + this.boldSpans.Clear(); + this.highlightedSpans.Clear(); + this.textBuilder.Clear(); + } + + /// + /// Adds an inline to based on the remaining parameters. + /// + /// Paragraph to add Inline to. + /// True if text should be added in bold. + /// True if the text should be added with highlight. + /// The text to add and clear. + private static void AddInline(Paragraph currentParagraph, bool currentBold, bool currentHighlighted, StringBuilder sequence) + { + if (sequence.Length == 0) + { + return; + } + + Run run = new Run(sequence.ToString()); + if (currentHighlighted) + { + run.Background = ParagraphSearcher.HighlightBrush; + } + + Inline inline = currentBold ? (Inline)new Bold(run) : run; + currentParagraph.Inlines.Add(inline); + sequence.Clear(); + } + + /// + /// This is an auxiliar method in BuildParagraph to move the current bold or highlighted spans + /// according to the + /// The current bold and highlighted span should be ending ahead of the current position. + /// Moves and to the + /// proper span in according to the + /// This is an auxiliar method in BuildParagraph. + /// + /// Current index within . + /// Current span within . + /// Character position. This comes from a position within this.textBuilder. + /// The collection of spans. This is either this.boldSpans or this.highlightedSpans. + private static void MoveSpanToPosition(ref int currentSpanIndex, ref TextSpan? currentSpan, int caracterPosition, List allSpans) + { + if (currentSpan == null || caracterPosition <= currentSpan.Value.End) + { + return; + } + + for (int newBoldIndex = currentSpanIndex + 1; newBoldIndex < allSpans.Count; newBoldIndex++) + { + TextSpan newBoldSpan = allSpans[newBoldIndex]; + if (caracterPosition <= newBoldSpan.End) + { + currentSpanIndex = newBoldIndex; + currentSpan = newBoldSpan; + return; + } + } + + // there is no span ending ahead of current position, so + // we set the current span to null to prevent unnecessary comparisons against the currentSpan + currentSpan = null; + } + + /// + /// Adds one individual text highlight + /// This is called after all calls to AddText have been made. + /// + /// Highlight start. + /// Highlight length. + private void AddHighlight(int start, int length) + { + ArgumentOutOfRangeException.ThrowIfNegative(start); + ArgumentOutOfRangeException.ThrowIfGreaterThan(start + length, this.textBuilder.Length, nameof(length)); + + this.highlightedSpans.Add(new TextSpan(start, length)); + } + + /// + /// Called internally to notify when a property changed. + /// + /// Property name. + private void OnNotifyPropertyChanged(string propertyName) + { + PropertyChangedEventHandler handler = this.PropertyChanged; + if (handler != null) + { + handler(this, new PropertyChangedEventArgs(propertyName)); + } + } + + /// + /// A text span used to mark bold and highlighted segments. + /// + internal struct TextSpan + { + /// + /// Index of the first character in the span. + /// + private readonly int start; + + /// + /// Index of the last character in the span. + /// + private readonly int end; + + /// + /// Initializes a new instance of the TextSpan struct. + /// + /// Index of the first character in the span. + /// Index of the last character in the span. + internal TextSpan(int start, int length) + { + ArgumentOutOfRangeException.ThrowIfNegative(start); + ArgumentOutOfRangeException.ThrowIfLessThan(length, 1); + + this.start = start; + this.end = start + length - 1; + } + + /// + /// Gets the index of the first character in the span. + /// + internal int Start + { + get { return this.start; } + } + + /// + /// Gets the index of the first character in the span. + /// + internal int End + { + get + { + return this.end; + } + } + + /// + /// Returns true if the is between start and end (inclusive). + /// + /// Position to verify if is in the span. + /// True if the is between start and end (inclusive). + internal bool Contains(int position) + { + return (position >= this.start) && (position <= this.end); + } + } + } +} diff --git a/src/Microsoft.Management.UI.Internal/HelpWindow/ParagraphSearcher.cs b/src/Microsoft.Management.UI.Internal/HelpWindow/ParagraphSearcher.cs new file mode 100644 index 00000000000..c8b9907751d --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/HelpWindow/ParagraphSearcher.cs @@ -0,0 +1,241 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.Diagnostics; +using System.Windows.Documents; +using System.Windows.Media; + +namespace Microsoft.Management.UI.Internal +{ + /// + /// Moves through search highlights built in a ParagraphBuilder + /// changing the color of the current highlight. + /// + internal class ParagraphSearcher + { + /// + /// Highlight for all matches except the current. + /// + internal static readonly Brush HighlightBrush = Brushes.Yellow; + + /// + /// Highlight for the current match. + /// + private static readonly Brush CurrentHighlightBrush = Brushes.Cyan; + + /// + /// Current match being highlighted in search. + /// + private Run currentHighlightedMatch; + + /// + /// Initializes a new instance of the ParagraphSearcher class. + /// + internal ParagraphSearcher() + { + } + + /// + /// Move to the next highlight starting at the . + /// + /// True for next false for previous. + /// Caret position. + /// The next highlight starting at the . + internal Run MoveAndHighlightNextNextMatch(bool forward, TextPointer caretPosition) + { + Debug.Assert(caretPosition != null, "a caret position is always valid"); + Debug.Assert(caretPosition.Parent != null && caretPosition.Parent is Run, "a caret Parent is always a valid Run"); + Run caretRun = (Run)caretPosition.Parent; + + Run currentRun; + + if (this.currentHighlightedMatch != null) + { + // restore the curent highlighted background to plain highlighted + this.currentHighlightedMatch.Background = ParagraphSearcher.HighlightBrush; + } + + // If the caret is in the end of a highlight we move to the adjacent run + // It has to be in the end because if there is a match at the beginning of the file + // and the caret has not been touched (so it is in the beginning of the file too) + // we want to highlight this first match. + // Considering the caller always set the caret to the end of the highlight + // The condition below works well for successive searchs + // We also need to move to the adjacent run if the caret is at the first run and we + // are moving backwards so that a search backwards when the first run is highlighted + // and the caret is at the beginning will wrap to the end + if ((!forward && IsFirstRun(caretRun)) || + ((caretPosition.GetOffsetToPosition(caretRun.ContentEnd) == 0) && ParagraphSearcher.Ishighlighted(caretRun))) + { + currentRun = ParagraphSearcher.GetNextRun(caretRun, forward); + } + else + { + currentRun = caretRun; + } + + currentRun = ParagraphSearcher.GetNextMatch(currentRun, forward); + + if (currentRun == null) + { + // if we could not find a next highlight wraparound + currentRun = ParagraphSearcher.GetFirstOrLastRun(caretRun, forward); + currentRun = ParagraphSearcher.GetNextMatch(currentRun, forward); + } + + this.currentHighlightedMatch = currentRun; + if (this.currentHighlightedMatch != null) + { + // restore the curent highlighted background to current highlighted + this.currentHighlightedMatch.Background = ParagraphSearcher.CurrentHighlightBrush; + } + + return currentRun; + } + + /// + /// Resets the search for fresh calls to MoveAndHighlightNextNextMatch. + /// + internal void ResetSearch() + { + this.currentHighlightedMatch = null; + } + + /// + /// Returns true if is highlighted. + /// + /// Run to check if is highlighted. + /// True if is highlighted. + private static bool Ishighlighted(Run run) + { + if (run == null) + { + return false; + } + + SolidColorBrush background = run.Background as SolidColorBrush; + if (background != null && background == ParagraphSearcher.HighlightBrush) + { + return true; + } + + return false; + } + + /// + /// Get the next or previous run according to . + /// + /// The current run. + /// True for next false for previous. + /// The next or previous run according to . + private static Run GetNextRun(Run currentRun, bool forward) + { + Bold parentBold = currentRun.Parent as Bold; + + Inline nextInline; + + if (forward) + { + nextInline = parentBold != null ? ((Inline)parentBold).NextInline : currentRun.NextInline; + } + else + { + nextInline = parentBold != null ? ((Inline)parentBold).PreviousInline : currentRun.PreviousInline; + } + + return GetRun(nextInline); + } + + /// + /// Gets the run of an inline. Inlines in a ParagrahBuilder are either a Run or a Bold + /// which contains a Run. + /// + /// Inline to get the run from. + /// The run of the inline. + private static Run GetRun(Inline inline) + { + Bold inlineBold = inline as Bold; + if (inlineBold != null) + { + return (Run)inlineBold.Inlines.FirstInline; + } + + return (Run)inline; + } + + /// + /// Gets the next highlighted run starting and including + /// according to the direction specified in . + /// + /// The current run. + /// True for next false for previous. + /// + /// the next highlighted run starting and including + /// according to the direction specified in . + /// + private static Run GetNextMatch(Run currentRun, bool forward) + { + while (currentRun != null) + { + if (ParagraphSearcher.Ishighlighted(currentRun)) + { + return currentRun; + } + + currentRun = ParagraphSearcher.GetNextRun(currentRun, forward); + } + + return currentRun; + } + + /// + /// Gets the run's paragraph. + /// + /// Run to get the paragraph from. + /// The run's paragraph. + private static Paragraph GetParagraph(Run run) + { + Bold parentBold = run.Parent as Bold; + Paragraph parentParagraph = (parentBold != null ? parentBold.Parent : run.Parent) as Paragraph; + Debug.Assert(parentParagraph != null, "the documents we are saerching are built with ParagraphBuilder, which builds the document like this"); + return parentParagraph; + } + + /// + /// Returns true if the run is the first run of the paragraph. + /// + /// Run to check. + /// True if the run is the first run of the paragraph. + private static bool IsFirstRun(Run run) + { + Paragraph paragraph = GetParagraph(run); + Run firstRun = ParagraphSearcher.GetRun(paragraph.Inlines.FirstInline); + return run == firstRun; + } + + /// + /// Gets the first or lasr run in the paragraph containing . + /// + /// Run containing the caret. + /// True for first false for last. + /// The first or last run in the paragraph containing . + private static Run GetFirstOrLastRun(Run caretRun, bool forward) + { + Debug.Assert(caretRun != null, "a caret run is always valid"); + + Paragraph paragraph = GetParagraph(caretRun); + + Inline firstOrLastInline; + if (forward) + { + firstOrLastInline = paragraph.Inlines.FirstInline; + } + else + { + firstOrLastInline = paragraph.Inlines.LastInline; + } + + return GetRun(firstOrLastInline); + } + } +} diff --git a/src/Microsoft.Management.UI.Internal/HelpWindow/SettingsDialog.xaml b/src/Microsoft.Management.UI.Internal/HelpWindow/SettingsDialog.xaml new file mode 100644 index 00000000000..d99c86c2a6a --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/HelpWindow/SettingsDialog.xaml @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Microsoft.Management.UI.Internal/HelpWindow/SettingsDialog.xaml.cs b/src/Microsoft.Management.UI.Internal/HelpWindow/SettingsDialog.xaml.cs new file mode 100644 index 00000000000..5d499d47f71 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/HelpWindow/SettingsDialog.xaml.cs @@ -0,0 +1,58 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +namespace Microsoft.Management.UI +{ + using System.Windows; + using Microsoft.Management.UI.Internal; + + /// + /// Dialog with settings for the help dialog. + /// + public partial class SettingsDialog : Window + { + /// + /// Initializes a new instance of the SettingsDialog class. + /// + public SettingsDialog() + { + InitializeComponent(); + this.Description.IsChecked = HelpWindowSettings.Default.HelpDescriptionDisplayed; + this.Examples.IsChecked = HelpWindowSettings.Default.HelpExamplesDisplayed; + this.Inputs.IsChecked = HelpWindowSettings.Default.HelpInputsDisplayed; + this.Notes.IsChecked = HelpWindowSettings.Default.HelpNotesDisplayed; + this.Outputs.IsChecked = HelpWindowSettings.Default.HelpOutputsDisplayed; + this.Parameters.IsChecked = HelpWindowSettings.Default.HelpParametersDisplayed; + this.RelatedLinks.IsChecked = HelpWindowSettings.Default.HelpRelatedLinksDisplayed; + this.Remarks.IsChecked = HelpWindowSettings.Default.HelpRemarksDisplayed; + this.Synopsys.IsChecked = HelpWindowSettings.Default.HelpSynopsysDisplayed; + this.Syntax.IsChecked = HelpWindowSettings.Default.HelpSyntaxDisplayed; + this.CaseSensitive.IsChecked = HelpWindowSettings.Default.HelpSearchMatchCase; + this.WholeWord.IsChecked = HelpWindowSettings.Default.HelpSearchWholeWord; + } + + /// + /// Called when the OK button has been clicked. + /// + /// Event sender. + /// Event arguments. + private void OK_Click(object sender, RoutedEventArgs e) + { + HelpWindowSettings.Default.HelpDescriptionDisplayed = this.Description.IsChecked == true; + HelpWindowSettings.Default.HelpExamplesDisplayed = this.Examples.IsChecked == true; + HelpWindowSettings.Default.HelpInputsDisplayed = this.Inputs.IsChecked == true; + HelpWindowSettings.Default.HelpOutputsDisplayed = this.Outputs.IsChecked == true; + HelpWindowSettings.Default.HelpNotesDisplayed = this.Notes.IsChecked == true; + HelpWindowSettings.Default.HelpParametersDisplayed = this.Parameters.IsChecked == true; + HelpWindowSettings.Default.HelpRelatedLinksDisplayed = this.RelatedLinks.IsChecked == true; + HelpWindowSettings.Default.HelpRemarksDisplayed = this.Remarks.IsChecked == true; + HelpWindowSettings.Default.HelpSynopsysDisplayed = this.Synopsys.IsChecked == true; + HelpWindowSettings.Default.HelpSyntaxDisplayed = this.Syntax.IsChecked == true; + HelpWindowSettings.Default.HelpSearchMatchCase = this.CaseSensitive.IsChecked == true; + HelpWindowSettings.Default.HelpSearchWholeWord = this.WholeWord.IsChecked == true; + HelpWindowSettings.Default.Save(); + this.DialogResult = true; + this.Close(); + } + } +} diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/Common/AutomationButton.cs b/src/Microsoft.Management.UI.Internal/ManagementList/Common/AutomationButton.cs new file mode 100644 index 00000000000..40f3e9e15a9 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ManagementList/Common/AutomationButton.cs @@ -0,0 +1,78 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; +using System.Windows; +using System.Windows.Automation.Peers; +using System.Windows.Controls; + +namespace Microsoft.Management.UI.Internal +{ + /// + /// Provides a control that is always visible in the automation tree. + /// + [SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] + [Description("Provides a System.Windows.Controls.Button control that is always visible in the automation tree.")] + public class AutomationButton : Button + { + #region Constructors + + /// + /// Initializes a new instance of the class. + /// + public AutomationButton() + { + // This constructor intentionally left blank + } + + #endregion + + #region Overides + + /// + /// Returns the implementations for this control. + /// + /// The implementations for this control. + protected override AutomationPeer OnCreateAutomationPeer() + { + return new AutomationButtonAutomationPeer(this); + } + + #endregion + } + + /// + /// Provides an automation peer for AutomationButton. + /// + [SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] + internal class AutomationButtonAutomationPeer : ButtonAutomationPeer + { + #region Constructors + + /// + /// Initializes a new instance of the class. + /// + /// The owner of the automation peer. + public AutomationButtonAutomationPeer(Button owner) + : base(owner) + { + // This constructor intentionally left blank + } + + #endregion + + #region Overrides + + /// + /// Gets a value that indicates whether the element is understood by the user as interactive or as contributing to the logical structure of the control in the GUI. Called by IsControlElement(). + /// + /// This method always returns false. + protected override bool IsControlElementCore() + { + return this.Owner.Visibility != Visibility.Hidden; + } + + #endregion + } +} diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/Common/AutomationImage.cs b/src/Microsoft.Management.UI.Internal/ManagementList/Common/AutomationImage.cs new file mode 100644 index 00000000000..1c0690a43c9 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ManagementList/Common/AutomationImage.cs @@ -0,0 +1,77 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; +using System.Windows.Automation.Peers; +using System.Windows.Controls; + +namespace Microsoft.Management.UI.Internal +{ + /// + /// Provides a control that is always visible in the automation tree. + /// + [SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] + [Description("Provides a System.Windows.Controls.Image control that is always visible in the automation tree.")] + public class AutomationImage : Image + { + #region Constructors + + /// + /// Initializes a new instance of the class. + /// + public AutomationImage() + { + // This constructor intentionally left blank + } + + #endregion + + #region Overides + + /// + /// Returns the implementations for this control. + /// + /// The implementations for this control. + protected override AutomationPeer OnCreateAutomationPeer() + { + return new AutomationImageAutomationPeer(this); + } + + #endregion + } + + /// + /// Provides an automation peer for AutomationImage. + /// + [SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] + internal class AutomationImageAutomationPeer : ImageAutomationPeer + { + #region Constructors + + /// + /// Initializes a new instance of the class. + /// + /// The owner of the automation peer. + public AutomationImageAutomationPeer(Image owner) + : base(owner) + { + // This constructor intentionally left blank + } + + #endregion + + #region Overrides + + /// + /// Gets a value that indicates whether the element is understood by the user as interactive or as contributing to the logical structure of the control in the GUI. Called by IsControlElement(). + /// + /// This method always returns false. + protected override bool IsControlElementCore() + { + return false; + } + + #endregion + } +} diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/Common/AutomationTextBlock.cs b/src/Microsoft.Management.UI.Internal/ManagementList/Common/AutomationTextBlock.cs new file mode 100644 index 00000000000..50db1bb78c1 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ManagementList/Common/AutomationTextBlock.cs @@ -0,0 +1,43 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; +using System.Windows.Automation.Peers; +using System.Windows.Controls; + +namespace Microsoft.Management.UI.Internal +{ + /// + /// Provides a control that is always visible in the automation tree. + /// + [SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] + [Description("Provides a System.Windows.Controls.TextBlock control that is always visible in the automation tree.")] + public class AutomationTextBlock : TextBlock + { + #region Structors + + /// + /// Initializes a new instance of the class. + /// + public AutomationTextBlock() + { + // This constructor intentionally left blank + } + + #endregion + + #region Overides + + /// + /// Returns the implementations for this control. + /// + /// The implementations for this control. + protected override AutomationPeer OnCreateAutomationPeer() + { + return new AutomationTextBlockAutomationPeer(this); + } + + #endregion + } +} diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/Common/AutomationTextBlockAutomationPeer.cs b/src/Microsoft.Management.UI.Internal/ManagementList/Common/AutomationTextBlockAutomationPeer.cs new file mode 100644 index 00000000000..6de73299abb --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ManagementList/Common/AutomationTextBlockAutomationPeer.cs @@ -0,0 +1,52 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.Diagnostics.CodeAnalysis; +using System.Windows.Automation.Peers; +using System.Windows.Controls; + +namespace Microsoft.Management.UI.Internal +{ + /// + /// Provides an automation peer for AutomationTextBlock. + /// + [SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] + internal class AutomationTextBlockAutomationPeer : TextBlockAutomationPeer + { + #region Structors + + /// + /// Initializes a new instance of the class. + /// + /// The owner of the automation peer. + public AutomationTextBlockAutomationPeer(TextBlock owner) + : base(owner) + { + // This constructor intentionally left blank + } + + #endregion + + #region Overrides + + /// + /// Gets a value that indicates whether the element is understood by the user as interactive or as contributing to the logical structure of the control in the GUI. Called by IsControlElement(). + /// + /// This method always returns true. + protected override bool IsControlElementCore() + { + return true; + } + + /// + /// Gets the class name. + /// + /// The class name. + protected override string GetClassNameCore() + { + return this.Owner.GetType().Name; + } + + #endregion + } +} diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/Common/BooleanBoxes.cs b/src/Microsoft.Management.UI.Internal/ManagementList/Common/BooleanBoxes.cs new file mode 100644 index 00000000000..92d50d5d007 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ManagementList/Common/BooleanBoxes.cs @@ -0,0 +1,42 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +namespace Microsoft.Management.UI.Internal +{ + /// + /// A class which returns the same boxed bool values. + /// + internal static class BooleanBoxes + { + private static object trueBox = true; + private static object falseBox = false; + + internal static object TrueBox + { + get + { + return trueBox; + } + } + + internal static object FalseBox + { + get + { + return falseBox; + } + } + + internal static object Box(bool value) + { + if (value) + { + return TrueBox; + } + else + { + return FalseBox; + } + } + } +} diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/Common/CommandHelper.cs b/src/Microsoft.Management.UI.Internal/ManagementList/Common/CommandHelper.cs new file mode 100644 index 00000000000..c9c8606f899 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ManagementList/Common/CommandHelper.cs @@ -0,0 +1,50 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.Security; +using System.Text; +using System.Windows; +using System.Windows.Input; + +namespace Microsoft.Management.UI.Internal +{ + internal static class CommandHelper + { + internal static void ExecuteCommand(ICommand command, object parameter, IInputElement target) + { + RoutedCommand command2 = command as RoutedCommand; + if (command2 != null) + { + if (command2.CanExecute(parameter, target)) + { + command2.Execute(parameter, target); + } + } + else if (command.CanExecute(parameter)) + { + command.Execute(parameter); + } + } + + internal static bool CanExecuteCommand(ICommand command, object parameter, IInputElement target) + { + if (command == null) + { + return false; + } + + RoutedCommand command2 = command as RoutedCommand; + + if (command2 != null) + { + return command2.CanExecute(parameter, target); + } + else + { + return command.CanExecute(parameter); + } + } + } +} diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/Common/CustomTypeComparer.cs b/src/Microsoft.Management.UI.Internal/ManagementList/Common/CustomTypeComparer.cs new file mode 100644 index 00000000000..6b0db6d5f95 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ManagementList/Common/CustomTypeComparer.cs @@ -0,0 +1,74 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.Diagnostics; + +namespace Microsoft.Management.UI.Internal +{ + /// + /// The CustomTypeComparer is responsible for holding custom comparers + /// for different types, which are in turn used to perform comparison + /// operations instead of the default IComparable comparison. + /// with a custom comparer. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] + public static class CustomTypeComparer + { + private static Dictionary comparers = new Dictionary(); + + /// + /// The static constructor. + /// + static CustomTypeComparer() + { + comparers.Add(typeof(DateTime), new DateTimeApproximationComparer()); + } + + /// + /// Compares two objects and returns a value indicating + /// whether one is less than, equal to, or greater than the other. + /// + /// + /// The first object to compare. + /// + /// + /// The second object to compare. + /// + /// + /// A type implementing IComparable. + /// + /// + /// If value1 is less than value2, then a value less than zero is returned. + /// If value1 equals value2, than zero is returned. + /// If value1 is greater than value2, then a value greater than zero is returned. + /// + public static int Compare(T value1, T value2) where T : IComparable + { + IComparer comparer; + if (TryGetCustomComparer(out comparer) == false) + { + return value1.CompareTo(value2); + } + + return comparer.Compare(value1, value2); + } + + private static bool TryGetCustomComparer(out IComparer comparer) where T : IComparable + { + comparer = null; + + object uncastComparer = null; + if (comparers.TryGetValue(typeof(T), out uncastComparer) == false) + { + return false; + } + + Debug.Assert(uncastComparer is IComparer, "must be IComparer"); + comparer = (IComparer)uncastComparer; + + return true; + } + } +} diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/Common/DataRoutedEventArgs.cs b/src/Microsoft.Management.UI.Internal/ManagementList/Common/DataRoutedEventArgs.cs new file mode 100644 index 00000000000..f75555b1da4 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ManagementList/Common/DataRoutedEventArgs.cs @@ -0,0 +1,41 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Text; +using System.Windows; + +namespace Microsoft.Management.UI.Internal +{ + /// + /// Routed event args which provide the ability to attach an + /// arbitrary piece of data. + /// + /// There are no restrictions on type T. + [SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] + public class DataRoutedEventArgs : RoutedEventArgs + { + private T data; + + /// + /// Constructs a new instance of the DataRoutedEventArgs class. + /// + /// The data payload to be stored. + /// The routed event. + public DataRoutedEventArgs(T data, RoutedEvent routedEvent) + { + this.data = data; + this.RoutedEvent = routedEvent; + } + + /// + /// Gets a value containing the data being stored. + /// + public T Data + { + get { return this.data; } + } + } +} diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/Common/DateTimeApproximationComparer.cs b/src/Microsoft.Management.UI.Internal/ManagementList/Common/DateTimeApproximationComparer.cs new file mode 100644 index 00000000000..affbe8ca4ab --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ManagementList/Common/DateTimeApproximationComparer.cs @@ -0,0 +1,67 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; + +namespace Microsoft.Management.UI.Internal +{ + /// + /// The DateTimeApproximationComparer is responsible for comparing two + /// DateTime objects at a level of precision determined by + /// the first object. The comparison either compares at the + /// date level or the date and time (down to Seconds precision). + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] + public class DateTimeApproximationComparer : IComparer + { + /// + /// Compares two objects and returns a value indicating + /// whether one is less than, equal to, or greater than the other. + /// + /// + /// The first object to compare. + /// + /// + /// The second object to compare. + /// + /// + /// If value1 is less than value2, then a value less than zero is returned. + /// If value1 equals value2, than zero is returned. + /// If value1 is greater than value2, then a value greater than zero is returned. + /// + public int Compare(DateTime value1, DateTime value2) + { + DateTime roundedX; + DateTime roundedY; + GetRoundedValues(value1, value2, out roundedX, out roundedY); + + return roundedX.CompareTo(roundedY); + } + + private static void GetRoundedValues(DateTime value1, DateTime value2, out DateTime roundedValue1, out DateTime roundedValue2) + { + roundedValue1 = value1; + roundedValue2 = value2; + + bool hasTimeComponent = HasTimeComponent(value1); + + int hour = hasTimeComponent ? value1.Hour : value2.Hour; + int minute = hasTimeComponent ? value1.Minute : value2.Minute; + int second = hasTimeComponent ? value1.Second : value2.Second; + + roundedValue1 = new DateTime(value1.Year, value1.Month, value1.Day, hour, minute, second); + roundedValue2 = new DateTime(value2.Year, value2.Month, value2.Day, value2.Hour, value2.Minute, value2.Second); + } + + private static bool HasTimeComponent(DateTime value) + { + bool hasNoTimeComponent = value.Hour == 0 + && value.Minute == 0 + && value.Second == 0 + && value.Millisecond == 0; + + return !hasNoTimeComponent; + } + } +} diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/Common/DismissiblePopup.Generated.cs b/src/Microsoft.Management.UI.Internal/ManagementList/Common/DismissiblePopup.Generated.cs new file mode 100644 index 00000000000..3a2768b17b0 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ManagementList/Common/DismissiblePopup.Generated.cs @@ -0,0 +1,273 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#region StyleCop Suppression - generated code +using System; +using System.ComponentModel; +using System.Windows; +using System.Windows.Input; + +namespace Microsoft.Management.UI.Internal +{ + + /// + /// A popup which child controls can signal to be dismissed. + /// + /// + /// If a control wants to dismiss the popup then they should execute the DismissPopupCommand on a target in the popup window. + /// + [Localizability(LocalizationCategory.None)] + partial class DismissiblePopup + { + // + // DismissPopup routed command + // + /// + /// A command which child controls can use to tell the popup to close. + /// + public static readonly RoutedCommand DismissPopupCommand = new RoutedCommand("DismissPopup",typeof(DismissiblePopup)); + + static private void DismissPopupCommand_CommandExecuted(object sender, ExecutedRoutedEventArgs e) + { + DismissiblePopup obj = (DismissiblePopup) sender; + obj.OnDismissPopupExecuted( e ); + } + + /// + /// Called when DismissPopup executes. + /// + /// + /// A command which child controls can use to tell the popup to close. + /// + protected virtual void OnDismissPopupExecuted(ExecutedRoutedEventArgs e) + { + OnDismissPopupExecutedImplementation(e); + } + + partial void OnDismissPopupExecutedImplementation(ExecutedRoutedEventArgs e); + + // + // CloseOnEscape dependency property + // + /// + /// Identifies the CloseOnEscape dependency property. + /// + public static readonly DependencyProperty CloseOnEscapeProperty = DependencyProperty.Register( "CloseOnEscape", typeof(bool), typeof(DismissiblePopup), new PropertyMetadata( BooleanBoxes.TrueBox, CloseOnEscapeProperty_PropertyChanged) ); + + /// + /// Gets or sets a value indicating whether the popup closes when ESC is pressed. + /// + [Bindable(true)] + [Category("Common Properties")] + [Description("Gets or sets a value indicating whether the popup closes when ESC is pressed.")] + [Localizability(LocalizationCategory.None)] + public bool CloseOnEscape + { + get + { + return (bool) GetValue(CloseOnEscapeProperty); + } + set + { + SetValue(CloseOnEscapeProperty,BooleanBoxes.Box(value)); + } + } + + static private void CloseOnEscapeProperty_PropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) + { + DismissiblePopup obj = (DismissiblePopup) o; + obj.OnCloseOnEscapeChanged( new PropertyChangedEventArgs((bool)e.OldValue, (bool)e.NewValue) ); + } + + /// + /// Occurs when CloseOnEscape property changes. + /// + public event EventHandler> CloseOnEscapeChanged; + + /// + /// Called when CloseOnEscape property changes. + /// + protected virtual void OnCloseOnEscapeChanged(PropertyChangedEventArgs e) + { + OnCloseOnEscapeChangedImplementation(e); + RaisePropertyChangedEvent(CloseOnEscapeChanged, e); + } + + partial void OnCloseOnEscapeChangedImplementation(PropertyChangedEventArgs e); + + // + // FocusChildOnOpen dependency property + // + /// + /// Identifies the FocusChildOnOpen dependency property. + /// + public static readonly DependencyProperty FocusChildOnOpenProperty = DependencyProperty.Register( "FocusChildOnOpen", typeof(bool), typeof(DismissiblePopup), new PropertyMetadata( BooleanBoxes.TrueBox, FocusChildOnOpenProperty_PropertyChanged) ); + + /// + /// Gets or sets a value indicating whether focus should be set on the child when the popup opens. + /// + [Bindable(true)] + [Category("Common Properties")] + [Description("Gets or sets a value indicating whether focus should be set on the child when the popup opens.")] + [Localizability(LocalizationCategory.None)] + public bool FocusChildOnOpen + { + get + { + return (bool) GetValue(FocusChildOnOpenProperty); + } + set + { + SetValue(FocusChildOnOpenProperty,BooleanBoxes.Box(value)); + } + } + + static private void FocusChildOnOpenProperty_PropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) + { + DismissiblePopup obj = (DismissiblePopup) o; + obj.OnFocusChildOnOpenChanged( new PropertyChangedEventArgs((bool)e.OldValue, (bool)e.NewValue) ); + } + + /// + /// Occurs when FocusChildOnOpen property changes. + /// + public event EventHandler> FocusChildOnOpenChanged; + + /// + /// Called when FocusChildOnOpen property changes. + /// + protected virtual void OnFocusChildOnOpenChanged(PropertyChangedEventArgs e) + { + OnFocusChildOnOpenChangedImplementation(e); + RaisePropertyChangedEvent(FocusChildOnOpenChanged, e); + } + + partial void OnFocusChildOnOpenChangedImplementation(PropertyChangedEventArgs e); + + // + // SetFocusOnClose dependency property + // + /// + /// Identifies the SetFocusOnClose dependency property. + /// + public static readonly DependencyProperty SetFocusOnCloseProperty = DependencyProperty.Register( "SetFocusOnClose", typeof(bool), typeof(DismissiblePopup), new PropertyMetadata( BooleanBoxes.FalseBox, SetFocusOnCloseProperty_PropertyChanged) ); + + /// + /// Indicates whether the focus returns to either a defined by the FocusOnCloseTarget dependency property UIElement or PlacementTarget or not. + /// + [Bindable(true)] + [Category("Common Properties")] + [Description("Indicates whether the focus returns to either a defined by the FocusOnCloseTarget dependency property UIElement or PlacementTarget or not.")] + [Localizability(LocalizationCategory.None)] + public bool SetFocusOnClose + { + get + { + return (bool) GetValue(SetFocusOnCloseProperty); + } + set + { + SetValue(SetFocusOnCloseProperty,BooleanBoxes.Box(value)); + } + } + + static private void SetFocusOnCloseProperty_PropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) + { + DismissiblePopup obj = (DismissiblePopup) o; + obj.OnSetFocusOnCloseChanged( new PropertyChangedEventArgs((bool)e.OldValue, (bool)e.NewValue) ); + } + + /// + /// Occurs when SetFocusOnClose property changes. + /// + public event EventHandler> SetFocusOnCloseChanged; + + /// + /// Called when SetFocusOnClose property changes. + /// + protected virtual void OnSetFocusOnCloseChanged(PropertyChangedEventArgs e) + { + OnSetFocusOnCloseChangedImplementation(e); + RaisePropertyChangedEvent(SetFocusOnCloseChanged, e); + } + + partial void OnSetFocusOnCloseChangedImplementation(PropertyChangedEventArgs e); + + // + // SetFocusOnCloseElement dependency property + // + /// + /// Identifies the SetFocusOnCloseElement dependency property. + /// + public static readonly DependencyProperty SetFocusOnCloseElementProperty = DependencyProperty.Register( "SetFocusOnCloseElement", typeof(UIElement), typeof(DismissiblePopup), new PropertyMetadata( null, SetFocusOnCloseElementProperty_PropertyChanged) ); + + /// + /// If the SetFocusOnClose property is set True and this property is set to a valid UIElement, focus returns to this UIElement after the DismissiblePopup is closed. + /// + [Bindable(true)] + [Category("Common Properties")] + [Description("If the SetFocusOnClose property is set True and this property is set to a valid UIElement, focus returns to this UIElement after the DismissiblePopup is closed.")] + [Localizability(LocalizationCategory.None)] + public UIElement SetFocusOnCloseElement + { + get + { + return (UIElement) GetValue(SetFocusOnCloseElementProperty); + } + set + { + SetValue(SetFocusOnCloseElementProperty,value); + } + } + + static private void SetFocusOnCloseElementProperty_PropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) + { + DismissiblePopup obj = (DismissiblePopup) o; + obj.OnSetFocusOnCloseElementChanged( new PropertyChangedEventArgs((UIElement)e.OldValue, (UIElement)e.NewValue) ); + } + + /// + /// Occurs when SetFocusOnCloseElement property changes. + /// + public event EventHandler> SetFocusOnCloseElementChanged; + + /// + /// Called when SetFocusOnCloseElement property changes. + /// + protected virtual void OnSetFocusOnCloseElementChanged(PropertyChangedEventArgs e) + { + OnSetFocusOnCloseElementChangedImplementation(e); + RaisePropertyChangedEvent(SetFocusOnCloseElementChanged, e); + } + + partial void OnSetFocusOnCloseElementChangedImplementation(PropertyChangedEventArgs e); + + /// + /// Called when a property changes. + /// + private void RaisePropertyChangedEvent(EventHandler> eh, PropertyChangedEventArgs e) + { + if (eh != null) + { + eh(this,e); + } + } + + // + // Static constructor + // + + /// + /// Called when the type is initialized. + /// + static DismissiblePopup() + { + CommandManager.RegisterClassCommandBinding( typeof(DismissiblePopup), new CommandBinding( DismissiblePopup.DismissPopupCommand, DismissPopupCommand_CommandExecuted )); + StaticConstructorImplementation(); + } + + static partial void StaticConstructorImplementation(); + + } +} +#endregion diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/Common/DismissiblePopup.cs b/src/Microsoft.Management.UI.Internal/ManagementList/Common/DismissiblePopup.cs new file mode 100644 index 00000000000..582be1c7ecd --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ManagementList/Common/DismissiblePopup.cs @@ -0,0 +1,150 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Diagnostics; +using System.Windows; +using System.Windows.Automation; +using System.Windows.Controls.Primitives; +using System.Windows.Data; +using System.Windows.Input; +using System.Windows.Media; + +namespace Microsoft.Management.UI.Internal +{ + /// + /// Partial class implementation for DismissiblePopup control. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] + public partial class DismissiblePopup : Popup + { + /// + /// Constructs an instance of DismissablePopup. + /// + public DismissiblePopup() : base() + { + // nothing + } + + private delegate void FocusChildDelegate(); + + /// + /// Responds to the condition in which the value of the IsOpen property changes from false to true. + /// + /// The event arguments. + protected override void OnOpened(EventArgs e) + { + base.OnOpened(e); + + if (this.FocusChildOnOpen) + { + this.Dispatcher.BeginInvoke( + System.Windows.Threading.DispatcherPriority.Loaded, + new FocusChildDelegate(this.FocusChild)); + } + + this.SetupAutomationIdBinding(); + } + + /// + /// Responds when the value of the IsOpen property changes from to true to false. + /// + /// The event arguments. + protected override void OnClosed(EventArgs e) + { + base.OnClosed(e); + + if (this.SetFocusOnClose) + { + // Find a control to set focus on. + if (this.SetFocusOnCloseElement != null) + { + // The focus target is set explicitly. + this.SetFocus(this.SetFocusOnCloseElement); + } + else if (this.PlacementTarget != null) + { + // Use PlacementTarget as a first chance option. + this.SetFocus(this.PlacementTarget); + } + else + { + // Use parent UIObject when neither FocusOnCloseTarget nor PlacementTarget is set. + UIElement parent = this.Parent as UIElement; + if (parent != null) + { + this.SetFocus(parent); + } + } + } + } + + private void SetFocus(UIElement element) + { + if (element.Focusable) + { + element.Focus(); + } + else + { + element.MoveFocus(new TraversalRequest(FocusNavigationDirection.First)); + } + } + + private void SetupAutomationIdBinding() + { + var popupRoot = this.FindPopupRoot(); + + var binding = new Binding(); + binding.Source = this; + binding.Path = new PropertyPath(AutomationProperties.AutomationIdProperty); + popupRoot.SetBinding(AutomationProperties.AutomationIdProperty, binding); + } + + private FrameworkElement FindPopupRoot() + { + DependencyObject element = this.Child; + + while (element.GetType().Name.Equals("PopupRoot", StringComparison.Ordinal) == false) + { + element = VisualTreeHelper.GetParent(element); + } + + Debug.Assert(element != null, "element not null"); + + return (FrameworkElement)element; + } + + /// + /// Provides class handling for the KeyDown routed event that occurs when the user presses a key while this control has focus. + /// + /// The event data. + protected override void OnKeyDown(System.Windows.Input.KeyEventArgs e) + { + //// + // Close the popup if ESC is pressed + //// + if (e.Key == System.Windows.Input.Key.Escape && this.CloseOnEscape) + { + this.IsOpen = false; + } + else + { + base.OnKeyDown(e); + } + } + + partial void OnDismissPopupExecutedImplementation(ExecutedRoutedEventArgs e) + { + this.IsOpen = false; + } + + private void FocusChild() + { + if (this.Child != null) + { + this.Child.MoveFocus(new TraversalRequest(FocusNavigationDirection.First)); + } + } + } +} diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/Common/ExtendedFrameworkElementAutomationPeer.cs b/src/Microsoft.Management.UI.Internal/ManagementList/Common/ExtendedFrameworkElementAutomationPeer.cs new file mode 100644 index 00000000000..0f269df5b75 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ManagementList/Common/ExtendedFrameworkElementAutomationPeer.cs @@ -0,0 +1,99 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.Diagnostics.CodeAnalysis; +using System.Windows; +using System.Windows.Automation.Peers; +using System.Windows.Controls; + +namespace Microsoft.Management.UI.Internal +{ + /// + /// Provides a base automation peer for FrameworkElement controls. + /// + [SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] + public class ExtendedFrameworkElementAutomationPeer : FrameworkElementAutomationPeer + { + #region Fields + + /// + /// Gets or sets the control type of the element that is associated with this automation peer. + /// + private AutomationControlType controlType = AutomationControlType.Custom; + + /// + /// Gets or sets a value that indicates whether the control should show in the logical tree. + /// + private bool isControlElement = true; + + #endregion + + #region Structors + + /// + /// Initializes a new instance of the class. + /// + /// The owner of the automation peer. + public ExtendedFrameworkElementAutomationPeer(FrameworkElement owner) + : base(owner) + { + // This constructor intentionally left blank + } + + /// + /// Initializes a new instance of the class. + /// + /// The owner of the automation peer. + /// The control type of the element that is associated with the automation peer. + public ExtendedFrameworkElementAutomationPeer(FrameworkElement owner, AutomationControlType controlType) + : this(owner) + { + this.controlType = controlType; + } + + /// + /// Initializes a new instance of the class. + /// + /// The owner of the automation peer. + /// The control type of the element that is associated with the automation peer. + /// Whether the element should show in the logical tree. + public ExtendedFrameworkElementAutomationPeer(FrameworkElement owner, AutomationControlType controlType, bool isControlElement) + : this(owner, controlType) + { + this.isControlElement = isControlElement; + } + + #endregion + + #region Overrides + + /// + /// Gets the class name. + /// + /// The class name. + protected override string GetClassNameCore() + { + return this.Owner.GetType().Name; + } + + /// + /// Gets the control type of the element that is associated with the automation peer. + /// + /// Returns the control type of the element that is associated with the automation peer. + protected override AutomationControlType GetAutomationControlTypeCore() + { + return this.controlType; + } + + /// + /// Gets a value that indicates whether the element is understood by the user as interactive or as contributing to the logical structure of the control in the GUI. Called by IsControlElement(). + /// + /// This method always returns true. + protected override bool IsControlElementCore() + { + return this.isControlElement; + } + + #endregion + } +} diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/Common/IAsyncProgress.cs b/src/Microsoft.Management.UI.Internal/ManagementList/Common/IAsyncProgress.cs new file mode 100644 index 00000000000..a87266dd696 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ManagementList/Common/IAsyncProgress.cs @@ -0,0 +1,38 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Text; +using System.Windows; + +namespace Microsoft.Management.UI.Internal +{ + /// + /// An interface designed to provide updates about an asynchronous operation. + /// If the UI is data bound to the properties in this interface then INotifyPropertyChanged should + /// be implemented by the type implementing IAsyncProgress so the UI can get notification of the properties + /// being changed. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] + public interface IAsyncProgress + { + /// + /// Gets a value indicating whether the async operation is currently running. + /// + bool OperationInProgress + { + get; + } + + /// + /// Gets a the error for the async operation. This field is only valid if + /// OperationInProgress is false. null indicates there was no error. + /// + Exception OperationError + { + get; + } + } +} diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/Common/IStateDescriptorFactory.cs b/src/Microsoft.Management.UI.Internal/ManagementList/Common/IStateDescriptorFactory.cs new file mode 100644 index 00000000000..e1539e76707 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ManagementList/Common/IStateDescriptorFactory.cs @@ -0,0 +1,24 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Diagnostics.CodeAnalysis; + +namespace Microsoft.Management.UI.Internal +{ + /// + /// Defines an interface for a factory that creates + /// StateDescriptors. + /// + /// The type T used by the StateDescriptor. + [SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] + public interface IStateDescriptorFactory + { + /// + /// Creates a new StateDescriptor based upon custom + /// logic. + /// + /// A new StateDescriptor. + StateDescriptor Create(); + } +} diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/Common/IntegralConverter.cs b/src/Microsoft.Management.UI.Internal/ManagementList/Common/IntegralConverter.cs new file mode 100644 index 00000000000..27c45ef288b --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ManagementList/Common/IntegralConverter.cs @@ -0,0 +1,83 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Text; +using System.Windows; +using System.Windows.Data; + +namespace Microsoft.Management.UI.Internal +{ + /// + /// Takes a value and returns the largest value which is a integral amount of the second value. + /// + [SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] + public class IntegralConverter : IMultiValueConverter + { + /// + /// Takes a value and returns the largest value which is a integral amount of the second value. + /// + /// + /// The first value is the source. The second is the factor. + /// + /// The parameter is not used. + /// The padding to subtract from the first value. + /// The parameter is not used. + /// + /// The integral value. + /// + public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + ArgumentNullException.ThrowIfNull(values); + + if (values.Length != 2) + { + throw new ArgumentException("Two values expected", "values"); + } + + if (values[0] == DependencyProperty.UnsetValue || + values[1] == DependencyProperty.UnsetValue) + { + return DependencyProperty.UnsetValue; + } + + var source = (double)values[0]; + var factor = (double)values[1]; + + double padding = 0; + + if (parameter != null) + { + padding = double.Parse((string)parameter, CultureInfo.InvariantCulture); + } + + var newSource = source - padding; + + if (newSource < factor) + { + return source; + } + + var remainder = newSource % factor; + var result = newSource - remainder; + + return result; + } + + /// + /// This method is not used. + /// + /// The parameter is not used. + /// The parameter is not used. + /// The parameter is not used. + /// The parameter is not used. + /// The parameter is not used. + public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture) + { + throw new NotImplementedException(); + } + } +} diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/Common/InverseBooleanConverter.cs b/src/Microsoft.Management.UI.Internal/ManagementList/Common/InverseBooleanConverter.cs new file mode 100644 index 00000000000..efefb08bae9 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ManagementList/Common/InverseBooleanConverter.cs @@ -0,0 +1,45 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Windows.Data; + +namespace Microsoft.Management.UI.Internal +{ + /// + /// Takes a bool value and returns the inverse. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] + public class InverseBooleanConverter : IValueConverter + { + /// + /// Converts a boolean value to be it's inverse. + /// + /// The source value. + /// The parameter is not used. + /// The parameter is not used. + /// The parameter is not used. + /// The inverted boolean value. + public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + ArgumentNullException.ThrowIfNull(value); + + var boolValue = (bool)value; + + return !boolValue; + } + + /// + /// This method is not used. + /// + /// The parameter is not used. + /// The parameter is not used. + /// The parameter is not used. + /// The parameter is not used. + /// The parameter is not used. + public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + throw new NotImplementedException(); + } + } +} diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/Common/IsEqualConverter.cs b/src/Microsoft.Management.UI.Internal/ManagementList/Common/IsEqualConverter.cs new file mode 100644 index 00000000000..dbd806a64d6 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ManagementList/Common/IsEqualConverter.cs @@ -0,0 +1,71 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Text; +using System.Windows; +using System.Windows.Data; + +namespace Microsoft.Management.UI.Internal +{ + /// + /// Takes two objects and determines whether they are equal. + /// + [SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] + public class IsEqualConverter : IMultiValueConverter + { + /// + /// Takes two items and determines whether they are equal. + /// + /// + /// Two objects of any type. + /// + /// The parameter is not used. + /// The parameter is not used. + /// The parameter is not used. + /// + /// True if-and-only-if the two objects are equal per Object.Equals(). + /// Null is equal only to null. + /// + public object Convert(object[] values, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + ArgumentNullException.ThrowIfNull(values); + + if (values.Length != 2) + { + throw new ArgumentException("Two values expected", "values"); + } + + object item1 = values[0]; + object item2 = values[1]; + + if (item1 == null) + { + return item2 == null; + } + + if (item2 == null) + { + return false; + } + + bool equal = item1.Equals(item2); + return equal; + } + + /// + /// This method is not used. + /// + /// The parameter is not used. + /// The parameter is not used. + /// The parameter is not used. + /// The parameter is not used. + /// The parameter is not used. + public object[] ConvertBack(object value, Type[] targetTypes, object parameter, System.Globalization.CultureInfo culture) + { + throw new NotImplementedException(); + } + } +} diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/Common/IsNotNullConverter.cs b/src/Microsoft.Management.UI.Internal/ManagementList/Common/IsNotNullConverter.cs new file mode 100644 index 00000000000..265e0266c53 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ManagementList/Common/IsNotNullConverter.cs @@ -0,0 +1,46 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Windows.Data; + +namespace Microsoft.Management.UI.Internal +{ + /// + /// The IsNotNullConverter is responsible for converting a value into + /// a boolean indicting whether the value is not null. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] + public class IsNotNullConverter : IValueConverter + { + #region IValueConverter Members + + /// + /// Determines if value is not null. + /// + /// The object to check. + /// The parameter is not used. + /// The parameter is not used. + /// The parameter is not used. + /// Returns true if value is not null, false otherwise. + public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + return value != null; + } + + /// + /// This method is not used. + /// + /// The parameter is not used. + /// The parameter is not used. + /// The parameter is not used. + /// The parameter is not used. + /// The parameter is not used. + public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + throw new NotSupportedException(); + } + + #endregion + } +} diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/Common/KeyboardHelp.cs b/src/Microsoft.Management.UI.Internal/ManagementList/Common/KeyboardHelp.cs new file mode 100644 index 00000000000..386b33996c1 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ManagementList/Common/KeyboardHelp.cs @@ -0,0 +1,149 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Text; +using System.Windows; +using System.Windows.Input; + +namespace Microsoft.Management.UI.Internal +{ + internal enum LogicalDirection + { + None, + Left, + Right + } + + internal static class KeyboardHelp + { + /// + /// Gets the logical direction for a key, taking into account RTL settings. + /// + /// The element to get FlowDirection from. + /// The key pressed. + /// The logical direction. + public static LogicalDirection GetLogicalDirection(DependencyObject element, Key key) + { + Debug.Assert(element != null, "element not null"); + + bool rightToLeft = IsElementRightToLeft(element); + + switch (key) + { + case Key.Right: + if (rightToLeft) + { + return LogicalDirection.Left; + } + else + { + return LogicalDirection.Right; + } + + case Key.Left: + if (rightToLeft) + { + return LogicalDirection.Right; + } + else + { + return LogicalDirection.Left; + } + + default: + return LogicalDirection.None; + } + } + + /// + /// Gets the focus direction for a key, taking into account RTL settings. + /// + /// The element to get FlowDirection from. + /// The key pressed. + /// The focus direction. + public static FocusNavigationDirection GetNavigationDirection(DependencyObject element, Key key) + { + Debug.Assert(element != null, "element not null"); + Debug.Assert(IsFlowDirectionKey(key)); + + bool rightToLeft = IsElementRightToLeft(element); + + switch (key) + { + case Key.Right: + if (rightToLeft) + { + return FocusNavigationDirection.Left; + } + else + { + return FocusNavigationDirection.Right; + } + + case Key.Left: + if (rightToLeft) + { + return FocusNavigationDirection.Right; + } + else + { + return FocusNavigationDirection.Left; + } + + case Key.Down: + return FocusNavigationDirection.Down; + case Key.Up: + return FocusNavigationDirection.Up; + default: + Debug.Fail("Non-direction key specified"); + return FocusNavigationDirection.First; + } + } + + /// + /// Determines if the control key is pressed. + /// + /// True if a control is pressed. + public static bool IsControlPressed() + { + if ((Keyboard.Modifiers & ModifierKeys.Control) == ModifierKeys.Control) + { + return true; + } + else + { + return false; + } + } + + /// + /// Determines if the key is a navigation key. + /// + /// The key pressed. + /// True if the key is a navigation key. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + private static bool IsFlowDirectionKey(Key key) + { + switch (key) + { + case Key.Right: + case Key.Left: + case Key.Down: + case Key.Up: + return true; + default: + return false; + } + } + + private static bool IsElementRightToLeft(DependencyObject element) + { + FlowDirection flowDirection = FrameworkElement.GetFlowDirection(element); + bool rightToLeft = flowDirection == FlowDirection.RightToLeft; + return rightToLeft; + } + } +} diff --git a/src/Microsoft.Management.UI.Internal/ManagementList/Common/ListOrganizer.Generated.cs b/src/Microsoft.Management.UI.Internal/ManagementList/Common/ListOrganizer.Generated.cs new file mode 100644 index 00000000000..799cd260e12 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ManagementList/Common/ListOrganizer.Generated.cs @@ -0,0 +1,487 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +// +// +// This code was generated by a tool. DO NOT EDIT +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// + +#region StyleCop Suppression - generated code +using System; +using System.Collections; +using System.ComponentModel; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Controls.Primitives; +using System.Windows.Data; +using System.Windows.Input; + +namespace Microsoft.Management.UI.Internal +{ + + /// + /// This control presents a dropdown listbox with associated organizing actions that can be performed on it. + /// + /// + /// + /// + /// If a custom template is provided for this control, then the template MUST provide the following template parts: + /// + /// PART_Picker - A required template part which must be of type PickerBase. This control provides basic functionality for Picker-like controls. + /// + /// + [TemplatePart(Name="PART_Picker", Type=typeof(PickerBase))] + [Localizability(LocalizationCategory.None)] + partial class ListOrganizer + { + // + // Fields + // + private PickerBase picker; + + // + // ItemDeleted RoutedEvent + // + /// + /// Identifies the ItemDeleted RoutedEvent. + /// + public static readonly RoutedEvent ItemDeletedEvent = EventManager.RegisterRoutedEvent("ItemDeleted",RoutingStrategy.Bubble,typeof(EventHandler>),typeof(ListOrganizer)); + + /// + /// Occurs when an item is deleted from the list. + /// + public event EventHandler> ItemDeleted + { + add + { + AddHandler(ItemDeletedEvent,value); + } + remove + { + RemoveHandler(ItemDeletedEvent,value); + } + } + + // + // ItemSelected RoutedEvent + // + /// + /// Identifies the ItemSelected RoutedEvent. + /// + public static readonly RoutedEvent ItemSelectedEvent = EventManager.RegisterRoutedEvent("ItemSelected",RoutingStrategy.Bubble,typeof(EventHandler>),typeof(ListOrganizer)); + + /// + /// Occurs when an item is selected in the list. + /// + public event EventHandler> ItemSelected + { + add + { + AddHandler(ItemSelectedEvent,value); + } + remove + { + RemoveHandler(ItemSelectedEvent,value); + } + } + + // + // DeleteItem routed command + // + /// + /// Informs the ListOrganizer that it should delete the item passed. + /// + public static readonly RoutedCommand DeleteItemCommand = new RoutedCommand("DeleteItem",typeof(ListOrganizer)); + + static private void DeleteItemCommand_CommandExecuted(object sender, ExecutedRoutedEventArgs e) + { + ListOrganizer obj = (ListOrganizer) sender; + obj.OnDeleteItemExecuted( e ); + } + + /// + /// Called when DeleteItem executes. + /// + /// + /// Informs the ListOrganizer that it should delete the item passed. + /// + protected virtual void OnDeleteItemExecuted(ExecutedRoutedEventArgs e) + { + OnDeleteItemExecutedImplementation(e); + } + + partial void OnDeleteItemExecutedImplementation(ExecutedRoutedEventArgs e); + + // + // SelectItem routed command + // + /// + /// Informs the ListOrganizer that it should select the item passed. + /// + public static readonly RoutedCommand SelectItemCommand = new RoutedCommand("SelectItem",typeof(ListOrganizer)); + + static private void SelectItemCommand_CommandExecuted(object sender, ExecutedRoutedEventArgs e) + { + ListOrganizer obj = (ListOrganizer) sender; + obj.OnSelectItemExecuted( e ); + } + + /// + /// Called when SelectItem executes. + /// + /// + /// Informs the ListOrganizer that it should select the item passed. + /// + protected virtual void OnSelectItemExecuted(ExecutedRoutedEventArgs e) + { + OnSelectItemExecutedImplementation(e); + } + + partial void OnSelectItemExecutedImplementation(ExecutedRoutedEventArgs e); + + // + // DropDownButtonTemplate dependency property + // + /// + /// Identifies the DropDownButtonTemplate dependency property. + /// + public static readonly DependencyProperty DropDownButtonTemplateProperty = DependencyProperty.Register( "DropDownButtonTemplate", typeof(ControlTemplate), typeof(ListOrganizer), new PropertyMetadata( null, DropDownButtonTemplateProperty_PropertyChanged) ); + + /// + /// Gets or sets a value that controls the visual tree of the DropDown button. + /// + [Bindable(true)] + [Category("Common Properties")] + [Description("Gets or sets a value that controls the visual tree of the DropDown button.")] + [Localizability(LocalizationCategory.None)] + public ControlTemplate DropDownButtonTemplate + { + get + { + return (ControlTemplate) GetValue(DropDownButtonTemplateProperty); + } + set + { + SetValue(DropDownButtonTemplateProperty,value); + } + } + + static private void DropDownButtonTemplateProperty_PropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) + { + ListOrganizer obj = (ListOrganizer) o; + obj.OnDropDownButtonTemplateChanged( new PropertyChangedEventArgs((ControlTemplate)e.OldValue, (ControlTemplate)e.NewValue) ); + } + + /// + /// Occurs when DropDownButtonTemplate property changes. + /// + public event EventHandler> DropDownButtonTemplateChanged; + + /// + /// Called when DropDownButtonTemplate property changes. + /// + protected virtual void OnDropDownButtonTemplateChanged(PropertyChangedEventArgs e) + { + OnDropDownButtonTemplateChangedImplementation(e); + RaisePropertyChangedEvent(DropDownButtonTemplateChanged, e); + } + + partial void OnDropDownButtonTemplateChangedImplementation(PropertyChangedEventArgs e); + + // + // DropDownStyle dependency property + // + /// + /// Identifies the DropDownStyle dependency property. + /// + public static readonly DependencyProperty DropDownStyleProperty = DependencyProperty.Register( "DropDownStyle", typeof(Style), typeof(ListOrganizer), new PropertyMetadata( null, DropDownStyleProperty_PropertyChanged) ); + + /// + /// Gets or sets the style of the drop-down. + /// + [Bindable(true)] + [Category("Common Properties")] + [Description("Gets or sets the style of the drop-down.")] + [Localizability(LocalizationCategory.None)] + public Style DropDownStyle + { + get + { + return (Style) GetValue(DropDownStyleProperty); + } + set + { + SetValue(DropDownStyleProperty,value); + } + } + + static private void DropDownStyleProperty_PropertyChanged(DependencyObject o, DependencyPropertyChangedEventArgs e) + { + ListOrganizer obj = (ListOrganizer) o; + obj.OnDropDownStyleChanged( new PropertyChangedEventArgs + + + + + + + + + + diff --git a/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/AllModulesControl.xaml.cs b/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/AllModulesControl.xaml.cs new file mode 100644 index 00000000000..5d360017c16 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/AllModulesControl.xaml.cs @@ -0,0 +1,50 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.ComponentModel; +using System.Windows; +using System.Windows.Controls; + +namespace Microsoft.PowerShell.Commands.ShowCommandInternal +{ + /// + /// Interaction logic for AllModulesControl.xaml. + /// + public partial class AllModulesControl : UserControl + { + #region Construction and Destructor + + /// + /// Initializes a new instance of the AllModulesControl class. + /// + public AllModulesControl() + { + InitializeComponent(); + + this.Loaded += (obj, args) => + { + this.ModulesCombo.Focus(); + }; + } + + #endregion + /// + /// Gets current control of the ShowModuleControl. + /// + internal ShowModuleControl CurrentShowModuleControl + { + get { return this.ShowModuleControl; } + } + + private void RefreshButton_Click(object sender, System.Windows.RoutedEventArgs e) + { + AllModulesViewModel viewModel = this.DataContext as AllModulesViewModel; + if (viewModel == null) + { + return; + } + + viewModel.OnRefresh(); + } + } +} diff --git a/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/CmdletControl.xaml b/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/CmdletControl.xaml new file mode 100644 index 00000000000..f7e77ed944d --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/CmdletControl.xaml @@ -0,0 +1,61 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/CmdletControl.xaml.cs b/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/CmdletControl.xaml.cs new file mode 100644 index 00000000000..0837bb567e4 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/CmdletControl.xaml.cs @@ -0,0 +1,102 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.Windows; +using System.Windows.Controls; + +namespace Microsoft.PowerShell.Commands.ShowCommandInternal +{ + /// + /// Interaction logic for CmdletControl.xaml. + /// + public partial class CmdletControl : UserControl + { + /// + /// Field used for the CurrentCommandViewModel parameter. + /// + private CommandViewModel currentCommandViewModel; + + #region Construction and Destructor + /// + /// Initializes a new instance of the CmdletControl class. + /// + public CmdletControl() + { + InitializeComponent(); + this.NotImportedControl.ImportModuleButton.Click += ImportModuleButton_Click; + this.ParameterSetTabControl.DataContextChanged += new DependencyPropertyChangedEventHandler(this.ParameterSetTabControl_DataContextChanged); + this.KeyDown += this.CmdletControl_KeyDown; + this.helpButton.innerButton.Click += this.HelpButton_Click; + } + #endregion + + #region Properties + /// + /// Gets the owner of the ViewModel. + /// + private CommandViewModel CurrentCommandViewModel + { + get { return this.currentCommandViewModel; } + } + #endregion + + #region Private Events + + /// + /// DataContextChanged event. + /// + /// Event sender. + /// Event args. + private void ParameterSetTabControl_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e) + { + if (this.DataContext == null) + { + return; + } + + CommandViewModel viewModel = (CommandViewModel)this.DataContext; + this.currentCommandViewModel = viewModel; + + if (viewModel.ParameterSets.Count == 0) + { + return; + } + + this.ParameterSetTabControl.SelectedItem = viewModel.ParameterSets[0]; + } + + /// + /// Key down event for user press F1 button. + /// + /// Event sender. + /// Event args. + private void CmdletControl_KeyDown(object sender, System.Windows.Input.KeyEventArgs e) + { + if (e.Key == System.Windows.Input.Key.F1) + { + this.CurrentCommandViewModel.OpenHelpWindow(); + } + } + + /// + /// Help button event. + /// + /// Event sender. + /// Event args. + private void HelpButton_Click(object sender, RoutedEventArgs e) + { + this.CurrentCommandViewModel.OpenHelpWindow(); + } + + /// + /// Import Module Button event. + /// + /// Event sender. + /// Event args. + private void ImportModuleButton_Click(object sender, RoutedEventArgs e) + { + this.CurrentCommandViewModel.OnImportModule(); + } + #endregion + } +} diff --git a/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/ImageButton/ImageButton.xaml b/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/ImageButton/ImageButton.xaml new file mode 100644 index 00000000000..024b287e80a --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/ImageButton/ImageButton.xaml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + diff --git a/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/ImageButton/ImageButton.xaml.cs b/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/ImageButton/ImageButton.xaml.cs new file mode 100644 index 00000000000..05247f46ff0 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/ImageButton/ImageButton.xaml.cs @@ -0,0 +1,46 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.Diagnostics.CodeAnalysis; +using System.Windows.Automation; +using System.Windows.Automation.Peers; +using System.Windows.Controls; + +namespace Microsoft.PowerShell.Commands.ShowCommandInternal +{ + /// + /// Button with images to represent enabled and disabled states. + /// + [SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes", Justification = "Required by XAML")] + public partial class ImageButton : ImageButtonBase + { + /// + /// Initializes a new instance of the ImageButton class. + /// + public ImageButton() + { + InitializeComponent(); + this.Loaded += this.ImageButton_Loaded; + } + + /// + /// Copies the automation id and name from the parent control to the inner button. + /// + /// Event sender. + /// Event arguments. + private void ImageButton_Loaded(object sender, System.Windows.RoutedEventArgs e) + { + object thisAutomationId = this.GetValue(AutomationProperties.AutomationIdProperty); + if (thisAutomationId != null) + { + this.innerButton.SetValue(AutomationProperties.AutomationIdProperty, thisAutomationId); + } + + object thisAutomationName = this.GetValue(AutomationProperties.NameProperty); + if (thisAutomationName != null) + { + this.innerButton.SetValue(AutomationProperties.NameProperty, thisAutomationName); + } + } + } +} diff --git a/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/ImageButton/ImageButtonBase.cs b/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/ImageButton/ImageButtonBase.cs new file mode 100644 index 00000000000..76ab5cf3c28 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/ImageButton/ImageButtonBase.cs @@ -0,0 +1,64 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Diagnostics.CodeAnalysis; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Input; +using System.Windows.Media; + +namespace Microsoft.PowerShell.Commands.ShowCommandInternal +{ + /// + /// Implements the ImageButtonBase base class to the ImageButton and ImageToggleButton. + /// + [SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes", Justification = "Required by XAML")] + public class ImageButtonBase : Grid + { + /// + /// Command associated with this button. + /// + public static readonly DependencyProperty CommandProperty = + DependencyProperty.Register("Command", typeof(RoutedUICommand), typeof(ImageButton)); + + /// + /// Image to be used for the enabled state. + /// + public static readonly DependencyProperty EnabledImageSourceProperty = + DependencyProperty.Register("EnabledImageSource", typeof(ImageSource), typeof(ImageButton)); + + /// + /// Image to be used for the disabled state. + /// + public static readonly DependencyProperty DisabledImageSourceProperty = + DependencyProperty.Register("DisabledImageSource", typeof(ImageSource), typeof(ImageButton)); + + /// + /// Gets or sets the image to be used for the enabled state. + /// + public ImageSource EnabledImageSource + { + get { return (ImageSource)GetValue(ImageButton.EnabledImageSourceProperty); } + set { SetValue(ImageButton.EnabledImageSourceProperty, value); } + } + + /// + /// Gets or sets the image to be used for the disabled state. + /// + public ImageSource DisabledImageSource + { + get { return (ImageSource)GetValue(ImageButton.DisabledImageSourceProperty); } + set { SetValue(ImageButton.DisabledImageSourceProperty, value); } + } + + /// + /// Gets or sets the command associated with this button. + /// + public RoutedUICommand Command + { + get { return (RoutedUICommand)GetValue(ImageButton.CommandProperty); } + set { SetValue(ImageButton.CommandProperty, value); } + } + } +} diff --git a/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/ImageButton/ImageButtonCommon.xaml b/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/ImageButton/ImageButtonCommon.xaml new file mode 100644 index 00000000000..f89e474a2de --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/ImageButton/ImageButtonCommon.xaml @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/ImageButton/ImageButtonToolTipConverter.cs b/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/ImageButton/ImageButtonToolTipConverter.cs new file mode 100644 index 00000000000..bec10afc6b7 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/ImageButton/ImageButtonToolTipConverter.cs @@ -0,0 +1,73 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Diagnostics.CodeAnalysis; +using System.Windows.Controls; +using System.Windows.Data; + +namespace Microsoft.PowerShell.Commands.ShowCommandInternal +{ + /// + /// Converts a an ImageButtonBase to its corresponding ToolTip. + /// + [SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes", Justification = "Needed for XAML")] + public class ImageButtonToolTipConverter : IValueConverter + { + // This class is meant to be used like this in XAML: + // + // ... + // + // + // + // ... + // + #region IValueConverter Members + + /// + /// Converts a an ImageButtonBase to its corresponding ToolTip by checking if it has a tooltip property + /// or a command with tooltip text + /// + /// The ImageButtonBase we are trying to Convert. + /// is not used. + /// is not used. + /// is not used. + /// The resulting object obtained from retrieving the property value in (or property values if contains dots) out of . . + public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + ImageButtonBase imageButtonBase = value as ImageButtonBase; + if (imageButtonBase == null) + { + return null; + } + + object toolTipObj = imageButtonBase.GetValue(Button.ToolTipProperty); + if (toolTipObj != null) + { + return toolTipObj.ToString(); + } + + if (imageButtonBase.Command != null && !string.IsNullOrEmpty(imageButtonBase.Command.Text)) + { + return imageButtonBase.Command.Text.Replace("_", string.Empty); + } + + return null; + } + + /// + /// This method is not supported. + /// + /// is not used. + /// is not used. + /// is not used. + /// is not used. + /// No value is returned. + public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) + { + throw new NotSupportedException(); + } + + #endregion + } +} diff --git a/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/ImageButton/ImageToggleButton.xaml b/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/ImageButton/ImageToggleButton.xaml new file mode 100644 index 00000000000..c472721ac85 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/ImageButton/ImageToggleButton.xaml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + diff --git a/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/ImageButton/ImageToggleButton.xaml.cs b/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/ImageButton/ImageToggleButton.xaml.cs new file mode 100644 index 00000000000..adc1e1a6421 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/ImageButton/ImageToggleButton.xaml.cs @@ -0,0 +1,60 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.Diagnostics.CodeAnalysis; +using System.Windows; +using System.Windows.Automation; + +namespace Microsoft.PowerShell.Commands.ShowCommandInternal +{ + /// + /// Toggle button with images to represent enabled and disabled states. + /// + [SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes", Justification = "Required by XAML")] + public partial class ImageToggleButton : ImageButtonBase + { + /// + /// Value indicating the button is checked. + /// + public static readonly DependencyProperty IsCheckedProperty = + DependencyProperty.Register("IsChecked", typeof(bool), typeof(ImageToggleButton)); + + /// + /// Initializes a new instance of the ImageToggleButton class. + /// + public ImageToggleButton() + { + InitializeComponent(); + this.Loaded += this.ImageButton_Loaded; + } + + /// + /// Gets or sets a value indicating whether the button is checked. + /// + public bool IsChecked + { + get { return (bool)GetValue(ImageToggleButton.IsCheckedProperty); } + set { SetValue(ImageToggleButton.IsCheckedProperty, value); } + } + + /// + /// Copies the automation id and name from the parent control to the inner button. + /// + /// Event sender. + /// Event arguments. + private void ImageButton_Loaded(object sender, System.Windows.RoutedEventArgs e) + { + object thisAutomationId = this.GetValue(AutomationProperties.AutomationIdProperty); + if (thisAutomationId != null) + { + this.toggleInnerButton.SetValue(AutomationProperties.AutomationIdProperty, thisAutomationId); + } + + object thisAutomationName = this.GetValue(AutomationProperties.NameProperty); + if (thisAutomationName != null) + { + this.toggleInnerButton.SetValue(AutomationProperties.NameProperty, thisAutomationName); + } + } + } +} diff --git a/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/MultipleSelectionControl.xaml b/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/MultipleSelectionControl.xaml new file mode 100644 index 00000000000..557741edf0d --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/MultipleSelectionControl.xaml @@ -0,0 +1,27 @@ + + + + + + + + + + + + + + + + + diff --git a/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/MultipleSelectionControl.xaml.cs b/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/MultipleSelectionControl.xaml.cs new file mode 100644 index 00000000000..f57d5dfda51 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/MultipleSelectionControl.xaml.cs @@ -0,0 +1,57 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.Globalization; +using System.Management.Automation; +using System.Text; +using System.Windows; +using System.Windows.Controls; + +namespace Microsoft.PowerShell.Commands.ShowCommandInternal +{ + /// + /// Interaction logic for MultipleSelectionControl.xaml. + /// + public partial class MultipleSelectionControl : UserControl + { + /// + /// Initializes a new instance of the MultipleSelectionControl class. + /// + public MultipleSelectionControl() + { + InitializeComponent(); + } + + /// + /// Show more items in new dialog. + /// + /// Event sender. + /// Event arguments. + private void ButtonBrowse_Click(object sender, RoutedEventArgs e) + { + MultipleSelectionDialog multipleSelectionDialog = new MultipleSelectionDialog(); + multipleSelectionDialog.Title = this.multipleValueButton.ToolTip.ToString(); + multipleSelectionDialog.listboxParameter.ItemsSource = comboxParameter.ItemsSource; + multipleSelectionDialog.ShowDialog(); + + if (multipleSelectionDialog.DialogResult != true) + { + return; + } + + StringBuilder newComboText = new StringBuilder(); + + foreach (object selectedItem in multipleSelectionDialog.listboxParameter.SelectedItems) + { + newComboText.Append(CultureInfo.InvariantCulture, $"{selectedItem},"); + } + + if (newComboText.Length > 1) + { + newComboText.Remove(newComboText.Length - 1, 1); + } + + comboxParameter.Text = newComboText.ToString(); + } + } +} diff --git a/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/NotImportedCmdletControl.xaml b/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/NotImportedCmdletControl.xaml new file mode 100644 index 00000000000..cb0a9198b80 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/NotImportedCmdletControl.xaml @@ -0,0 +1,28 @@ + + + + + + + + + + + + + + diff --git a/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/NotImportedCmdletControl.xaml.cs b/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/NotImportedCmdletControl.xaml.cs new file mode 100644 index 00000000000..65774004236 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/NotImportedCmdletControl.xaml.cs @@ -0,0 +1,24 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.Windows.Controls; + +namespace Microsoft.PowerShell.Commands.ShowCommandInternal +{ + /// + /// Interaction logic for NotImportedCmdletControl.xaml. + /// + public partial class NotImportedCmdletControl : UserControl + { + #region Construction and Destructor + + /// + /// Initializes a new instance of the NotImportedCmdletControl class. + /// + public NotImportedCmdletControl() + { + InitializeComponent(); + } + #endregion + } +} diff --git a/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/ParameterSetControl.xaml b/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/ParameterSetControl.xaml new file mode 100644 index 00000000000..c59f519d663 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/ParameterSetControl.xaml @@ -0,0 +1,22 @@ + + + + + + + + + + + + diff --git a/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/ParameterSetControl.xaml.cs b/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/ParameterSetControl.xaml.cs new file mode 100644 index 00000000000..6efef65eec6 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/ParameterSetControl.xaml.cs @@ -0,0 +1,403 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Globalization; +using System.Management.Automation; +using System.Windows; +using System.Windows.Automation; +using System.Windows.Controls; +using System.Windows.Data; + +using Microsoft.Management.UI.Internal; +using Microsoft.PowerShell.Commands.ShowCommandExtension; + +namespace Microsoft.PowerShell.Commands.ShowCommandInternal +{ + /// + /// Interaction logic for ParameterSetControl.xaml. + /// + public partial class ParameterSetControl : UserControl + { + /// + /// First focusable element in the generated UI. + /// + private UIElement firstFocusableElement; + + /// + /// Field used for the CurrentParameterSetViewModel parameter. + /// + private ParameterSetViewModel currentParameterSetViewModel; + + #region Construction and Destructor + /// + /// Initializes a new instance of the ParameterSetControl class. + /// + public ParameterSetControl() + { + InitializeComponent(); + this.DataContextChanged += new DependencyPropertyChangedEventHandler(this.ParameterSetControl_DataContextChanged); + } + #endregion + + #region Public Methods + + /// + /// Focuses the first focusable element in this control. + /// + public void FocusFirstElement() + { + if (this.firstFocusableElement != null) + { + this.firstFocusableElement.Focus(); + } + } + + #endregion + + #region Private Property + /// + /// Gets current ParameterSetViewModel. + /// + private ParameterSetViewModel CurrentParameterSetViewModel + { + get { return this.currentParameterSetViewModel; } + } + + #endregion + + /// + /// Creates a CheckBox for switch parameters. + /// + /// DataContext object. + /// Row number. + /// a CheckBox for switch parameters. + private static CheckBox CreateCheckBox(ParameterViewModel parameterViewModel, int rowNumber) + { + CheckBox checkBox = new CheckBox(); + + checkBox.SetBinding(Label.ContentProperty, new Binding("NameCheckLabel")); + checkBox.DataContext = parameterViewModel; + checkBox.HorizontalAlignment = System.Windows.HorizontalAlignment.Left; + checkBox.SetValue(Grid.ColumnProperty, 0); + checkBox.SetValue(Grid.ColumnSpanProperty, 2); + checkBox.SetValue(Grid.RowProperty, rowNumber); + checkBox.IsThreeState = false; + checkBox.Margin = new Thickness(8, rowNumber == 0 ? 7 : 5, 0, 5); + checkBox.SetBinding(CheckBox.ToolTipProperty, new Binding("ToolTip")); + checkBox.SetBinding(AutomationProperties.HelpTextProperty, new Binding("ToolTip")); + Binding valueBinding = new Binding("Value"); + checkBox.SetBinding(CheckBox.IsCheckedProperty, valueBinding); + + //// Add AutomationProperties.AutomationId for Ui Automation test. + checkBox.SetValue( + System.Windows.Automation.AutomationProperties.AutomationIdProperty, + string.Create(CultureInfo.CurrentCulture, $"chk{parameterViewModel.Name}")); + + checkBox.SetValue( + System.Windows.Automation.AutomationProperties.NameProperty, + parameterViewModel.Name); + + return checkBox; + } + + /// + /// Creates a ComboBox control for input type field. + /// + /// DataContext object. + /// Row number. + /// Control data source. + /// Return a ComboBox control. + private static ComboBox CreateComboBoxControl(ParameterViewModel parameterViewModel, int rowNumber, IEnumerable itemsSource) + { + ComboBox comboBox = new ComboBox(); + + comboBox.DataContext = parameterViewModel; + comboBox.SetValue(Grid.ColumnProperty, 1); + comboBox.SetValue(Grid.RowProperty, rowNumber); + comboBox.Margin = new Thickness(2); + comboBox.SetBinding(TextBox.ToolTipProperty, new Binding("ToolTip")); + comboBox.ItemsSource = itemsSource; + + Binding selectedItemBinding = new Binding("Value"); + comboBox.SetBinding(ComboBox.SelectedItemProperty, selectedItemBinding); + + string automationId = string.Create(CultureInfo.CurrentCulture, $"combox{parameterViewModel.Name}"); + + //// Add AutomationProperties.AutomationId for Ui Automation test. + comboBox.SetValue( + System.Windows.Automation.AutomationProperties.AutomationIdProperty, + automationId); + + comboBox.SetValue( + System.Windows.Automation.AutomationProperties.NameProperty, + parameterViewModel.Name); + + return comboBox; + } + + /// + /// Creates a MultiSelectCombo control for input type field. + /// + /// DataContext object. + /// Row number. + /// Control data source. + /// Return a MultiSelectCombo control. + private static MultipleSelectionControl CreateMultiSelectComboControl(ParameterViewModel parameterViewModel, int rowNumber, IEnumerable itemsSource) + { + MultipleSelectionControl multiControls = new MultipleSelectionControl(); + + multiControls.DataContext = parameterViewModel; + multiControls.SetValue(Grid.ColumnProperty, 1); + multiControls.SetValue(Grid.RowProperty, rowNumber); + multiControls.Margin = new Thickness(2); + multiControls.comboxParameter.ItemsSource = itemsSource; + multiControls.SetBinding(TextBox.ToolTipProperty, new Binding("ToolTip")); + + Binding valueBinding = new Binding("Value"); + valueBinding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged; + multiControls.comboxParameter.SetBinding(ComboBox.TextProperty, valueBinding); + + // Add AutomationProperties.AutomationId for Ui Automation test. + multiControls.SetValue(System.Windows.Automation.AutomationProperties.AutomationIdProperty, string.Create(CultureInfo.CurrentCulture, $"combox{parameterViewModel.Name}")); + + multiControls.comboxParameter.SetValue( + System.Windows.Automation.AutomationProperties.NameProperty, + parameterViewModel.Name); + + string buttonToolTipAndName = string.Format( + CultureInfo.CurrentUICulture, + ShowCommandResources.SelectMultipleValuesForParameterFormat, + parameterViewModel.Name); + + multiControls.multipleValueButton.SetValue(Button.ToolTipProperty, buttonToolTipAndName); + multiControls.multipleValueButton.SetValue( + System.Windows.Automation.AutomationProperties.NameProperty, + buttonToolTipAndName); + + return multiControls; + } + + /// + /// Creates a TextBox control for input type field. + /// + /// DataContext object. + /// Row number. + /// Return a TextBox control. + private static TextBox CreateTextBoxControl(ParameterViewModel parameterViewModel, int rowNumber) + { + TextBox textBox = new TextBox(); + + textBox.DataContext = parameterViewModel; + textBox.SetValue(Grid.ColumnProperty, 1); + textBox.SetValue(Grid.RowProperty, rowNumber); + textBox.Margin = new Thickness(2); + textBox.SetBinding(TextBox.ToolTipProperty, new Binding("ToolTip")); + + Binding valueBinding = new Binding("Value"); + valueBinding.UpdateSourceTrigger = UpdateSourceTrigger.PropertyChanged; + textBox.SetBinding(TextBox.TextProperty, valueBinding); + + //// Add AutomationProperties.AutomationId for UI Automation test. + textBox.SetValue( + System.Windows.Automation.AutomationProperties.AutomationIdProperty, + string.Create(CultureInfo.CurrentCulture, $"txt{parameterViewModel.Name}")); + + textBox.SetValue( + System.Windows.Automation.AutomationProperties.NameProperty, + parameterViewModel.Name); + + ShowCommandParameterType parameterType = parameterViewModel.Parameter.ParameterType; + + if (parameterType.IsArray) + { + parameterType = parameterType.ElementType; + } + + if (parameterType.IsScriptBlock || parameterType.ImplementsDictionary) + { + textBox.AcceptsReturn = true; + textBox.VerticalScrollBarVisibility = ScrollBarVisibility.Auto; + textBox.HorizontalScrollBarVisibility = ScrollBarVisibility.Auto; + textBox.Loaded += ParameterSetControl.MultiLineTextBox_Loaded; + } + + return textBox; + } + + /// + /// Called for a newly created multiline text box to increase its height and. + /// + /// Event sender. + /// Event arguments. + private static void MultiLineTextBox_Loaded(object sender, RoutedEventArgs e) + { + TextBox senderTextBox = (TextBox)sender; + senderTextBox.Loaded -= ParameterSetControl.MultiLineTextBox_Loaded; + + // This will set the height to about 3 lines since the total height of the + // TextBox is a bit greater than a line's height + senderTextBox.Height = senderTextBox.ActualHeight * 2; + } + + #region Event Methods + + /// + /// When user switch ParameterSet.It will trigger this event. + /// This event method will renew generate all controls for current ParameterSet. + /// + /// Event sender. + /// Event args. + private void ParameterSetControl_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e) + { + this.firstFocusableElement = null; + this.MainGrid.Children.Clear(); + this.MainGrid.RowDefinitions.Clear(); + + ParameterSetViewModel viewModel = e.NewValue as ParameterSetViewModel; + if (viewModel == null) + { + return; + } + + this.currentParameterSetViewModel = viewModel; + + for (int rowNumber = 0; rowNumber < viewModel.Parameters.Count; rowNumber++) + { + ParameterViewModel parameter = viewModel.Parameters[rowNumber]; + this.MainGrid.RowDefinitions.Add(this.CreateNewRow()); + + if (parameter.Parameter.ParameterType.IsSwitch) + { + this.AddControlToMainGrid(ParameterSetControl.CreateCheckBox(parameter, rowNumber)); + } + else + { + this.CreateAndAddLabel(parameter, rowNumber); + Control control = null; + if (parameter.Parameter.HasParameterSet) + { + // For ValidateSet parameter + ArrayList itemsSource = new ArrayList(); + itemsSource.Add(string.Empty); + + for (int i = 0; i < parameter.Parameter.ValidParamSetValues.Count; i++) + { + itemsSource.Add(parameter.Parameter.ValidParamSetValues[i]); + } + + control = ParameterSetControl.CreateComboBoxControl(parameter, rowNumber, itemsSource); + } + else if (parameter.Parameter.ParameterType.IsEnum) + { + if (parameter.Parameter.ParameterType.HasFlagAttribute) + { + ArrayList itemsSource = new ArrayList(); + itemsSource.Add(string.Empty); + itemsSource.AddRange(parameter.Parameter.ParameterType.EnumValues); + control = ParameterSetControl.CreateComboBoxControl(parameter, rowNumber, itemsSource); + } + else + { + control = ParameterSetControl.CreateMultiSelectComboControl(parameter, rowNumber, parameter.Parameter.ParameterType.EnumValues); + } + } + else if (parameter.Parameter.ParameterType.IsBoolean) + { + control = ParameterSetControl.CreateComboBoxControl(parameter, rowNumber, new string[] { string.Empty, "$True", "$False" }); + } + else + { + // For input parameter + control = ParameterSetControl.CreateTextBoxControl(parameter, rowNumber); + } + + if (control != null) + { + this.AddControlToMainGrid(control); + } + } + } + } + + /// + /// When user trigger click on anyone CheckBox. Get value from sender. + /// + /// Event sender. + /// Event args. + private void CheckBox_Click(object sender, RoutedEventArgs e) + { + CheckBox senderCheck = (CheckBox)sender; + ((ParameterViewModel)senderCheck.DataContext).Value = senderCheck.IsChecked.ToString(); + } + + #endregion + + #region Private Method + + /// + /// Creates a RowDefinition for MainGrid. + /// + /// Return a RowDefinition object. + private RowDefinition CreateNewRow() + { + RowDefinition row = new RowDefinition(); + row.Height = GridLength.Auto; + return row; + } + + /// + /// Adds a control to MainGrid;. + /// + /// Will adding UIControl. + private void AddControlToMainGrid(UIElement uiControl) + { + if (this.firstFocusableElement == null && uiControl is not Label) + { + this.firstFocusableElement = uiControl; + } + + this.MainGrid.Children.Add(uiControl); + } + + /// + /// Creates a Label control and add it to MainGrid. + /// + /// DataContext object. + /// Row number. + private void CreateAndAddLabel(ParameterViewModel parameterViewModel, int rowNumber) + { + Label label = this.CreateLabel(parameterViewModel, rowNumber); + this.AddControlToMainGrid(label); + } + + /// + /// Creates a Label control for input type field. + /// + /// DataContext object. + /// Row number. + /// Return a Label control. + private Label CreateLabel(ParameterViewModel parameterViewModel, int rowNumber) + { + Label label = new Label(); + + label.SetBinding(Label.ContentProperty, new Binding("NameTextLabel")); + label.DataContext = parameterViewModel; + label.HorizontalAlignment = System.Windows.HorizontalAlignment.Left; + label.SetValue(Grid.ColumnProperty, 0); + label.SetValue(Grid.RowProperty, rowNumber); + label.Margin = new Thickness(2); + label.SetBinding(Label.ToolTipProperty, new Binding("ToolTip")); + + //// Add AutomationProperties.AutomationId for Ui Automation test. + label.SetValue( + System.Windows.Automation.AutomationProperties.AutomationIdProperty, + string.Create(CultureInfo.CurrentCulture, $"lbl{parameterViewModel.Name}")); + + return label; + } + #endregion + } +} diff --git a/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/ShowModuleControl.xaml b/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/ShowModuleControl.xaml new file mode 100644 index 00000000000..e03c7859ed0 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/ShowModuleControl.xaml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + + + + diff --git a/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/ShowModuleControl.xaml.cs b/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/ShowModuleControl.xaml.cs new file mode 100644 index 00000000000..4bdaa32fd9f --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ShowCommand/Controls/ShowModuleControl.xaml.cs @@ -0,0 +1,86 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.Management.Automation; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Input; + +namespace Microsoft.PowerShell.Commands.ShowCommandInternal +{ + /// + /// Control taht shows cmdlets in a module and details for a selected cmdlet. + /// + public partial class ShowModuleControl : UserControl + { + /// + /// Field used for the Owner parameter. + /// + private Window owner; + + /// + /// Initializes a new instance of the ShowModuleControl class. + /// + public ShowModuleControl() + { + InitializeComponent(); + + // See comment in method summary to understand why this event is handled + this.CommandList.PreviewMouseMove += this.CommandList_PreviewMouseMove; + + // See comment in method summary to understand why this event is handled + this.CommandList.SelectionChanged += this.CommandList_SelectionChanged; + } + + /// + /// Gets or sets the owner of the container. + /// + public Window Owner + { + get { return this.owner; } + set { this.owner = value; } + } + + #region Events Handlers + /// + /// WPF has an interesting feature in list selection where if you hold the mouse button down, + /// it will select the item under it, but if you keep the mouse button down and move the mouse + /// (if the list supported drag and drop, the mouse action would be the same as dragging) it + /// will select other list items. + /// If the first selection change causes details for the item to be displayed and resizes the list, + /// the selection can skip to another list item that happens to be over as the list got resized. + /// In summary, resizing the list on selection can cause a selection bug. If the user selects an + /// item in the end of the list the next item downwards can be selected. + /// The WPF drag-and-select feature is not a standard win32 list behavior, and we can do without it + /// since it causes this problem. + /// WPF sets up this behavior by using a mouse capture. We undo the behavior in the handler below + /// which removes the behavior. + /// + /// Event sender. + /// Event arguments. + private void CommandList_PreviewMouseMove(object sender, MouseEventArgs e) + { + if (this.CommandList.IsMouseCaptured) + { + this.CommandList.ReleaseMouseCapture(); + } + } + + /// + /// Ensures the selected item is scrolled into view and that the list is focused. + /// An item could be out of the view if the selection was changed in the object model + /// + /// Event sender. + /// Event arguments. + private void CommandList_SelectionChanged(object sender, SelectionChangedEventArgs e) + { + if (this.CommandList.SelectedItem == null) + { + return; + } + + this.CommandList.ScrollIntoView(this.CommandList.SelectedItem); + } + #endregion Events Handlers + } +} diff --git a/src/Microsoft.Management.UI.Internal/ShowCommand/ShowCommandSettings.Designer.cs b/src/Microsoft.Management.UI.Internal/ShowCommand/ShowCommandSettings.Designer.cs new file mode 100644 index 00000000000..18194cec8fb --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ShowCommand/ShowCommandSettings.Designer.cs @@ -0,0 +1,148 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.16808 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Microsoft.Management.UI.Internal.ShowCommand { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "10.0.0.0")] + internal sealed partial class ShowCommandSettings : global::System.Configuration.ApplicationSettingsBase { + + private static ShowCommandSettings defaultInstance = ((ShowCommandSettings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new ShowCommandSettings()))); + + public static ShowCommandSettings Default { + get { + return defaultInstance; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("-1")] + public double ShowOneCommandTop { + get { + return ((double)(this["ShowOneCommandTop"])); + } + set { + this["ShowOneCommandTop"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("-1")] + public double ShowOneCommandLeft { + get { + return ((double)(this["ShowOneCommandLeft"])); + } + set { + this["ShowOneCommandLeft"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("-1")] + public double ShowOneCommandWidth { + get { + return ((double)(this["ShowOneCommandWidth"])); + } + set { + this["ShowOneCommandWidth"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("-1")] + public double ShowOneCommandHeight { + get { + return ((double)(this["ShowOneCommandHeight"])); + } + set { + this["ShowOneCommandHeight"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("-1")] + public double ShowCommandsTop { + get { + return ((double)(this["ShowCommandsTop"])); + } + set { + this["ShowCommandsTop"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("-1")] + public double ShowCommandsLeft { + get { + return ((double)(this["ShowCommandsLeft"])); + } + set { + this["ShowCommandsLeft"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("-1")] + public double ShowCommandsWidth { + get { + return ((double)(this["ShowCommandsWidth"])); + } + set { + this["ShowCommandsWidth"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("-1")] + public double ShowCommandsHeight { + get { + return ((double)(this["ShowCommandsHeight"])); + } + set { + this["ShowCommandsHeight"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool ShowCommandsWindowMaximized { + get { + return ((bool)(this["ShowCommandsWindowMaximized"])); + } + set { + this["ShowCommandsWindowMaximized"] = value; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("False")] + public bool ShowOneCommandWindowMaximized { + get { + return ((bool)(this["ShowOneCommandWindowMaximized"])); + } + set { + this["ShowOneCommandWindowMaximized"] = value; + } + } + } +} diff --git a/src/Microsoft.Management.UI.Internal/ShowCommand/ShowCommandSettings.settings b/src/Microsoft.Management.UI.Internal/ShowCommand/ShowCommandSettings.settings new file mode 100644 index 00000000000..53a4da1a5d7 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ShowCommand/ShowCommandSettings.settings @@ -0,0 +1,36 @@ + + + + + + -1 + + + -1 + + + -1 + + + -1 + + + -1 + + + -1 + + + -1 + + + -1 + + + False + + + False + + + diff --git a/src/Microsoft.Management.UI.Internal/ShowCommand/ViewModel/AllModulesViewModel.cs b/src/Microsoft.Management.UI.Internal/ShowCommand/ViewModel/AllModulesViewModel.cs new file mode 100644 index 00000000000..04fc95e4223 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ShowCommand/ViewModel/AllModulesViewModel.cs @@ -0,0 +1,658 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Globalization; +using System.Linq; +using System.Management.Automation; +using System.Windows; + +using Microsoft.Management.UI.Internal; +using Microsoft.PowerShell.Commands.ShowCommandExtension; + +namespace Microsoft.PowerShell.Commands.ShowCommandInternal +{ + /// + /// Contains all Commands, Parameters, ParameterSet and Common Parameter. + /// + public class AllModulesViewModel : INotifyPropertyChanged + { + #region Private Fields + /// + /// Flag indicating a wait message is being displayed. + /// + private bool waitMessageDisplayed; + + /// + /// True if this ViewModel is not supposed to show common parameters. + /// + private bool noCommonParameter; + + /// + /// the filterName of command. + /// + private string commandNameFilter; + + /// + /// Field used for the Modules property. + /// + private List modules; + + /// + /// true if a command can be run. + /// + private bool canRun; + + /// + /// true if a command can be copied. + /// + private bool canCopy; + + /// + /// the selected module being displayed in the GUI. + /// + private ModuleViewModel selectedModule; + + /// + /// the visibility of the refresh button. + /// + private Visibility refreshVisibility = Visibility.Collapsed; + + /// + /// Provides an extra viewModel object that allows callers to control certain aspects of the GUI. + /// + private object extraViewModel; + + /// + /// private property for ZoomLevel. + /// + private double zoomLevel = 1.0; + #endregion + + #region Construction and Destructor + /// + /// Initializes a new instance of the AllModulesViewModel class. + /// + /// The loaded modules. + /// Commands to show. + public AllModulesViewModel(Dictionary importedModules, IEnumerable commands) + { + ArgumentNullException.ThrowIfNull(commands); + + if (!commands.GetEnumerator().MoveNext()) + { + throw new ArgumentNullException("commands"); + } + + this.Initialization(importedModules, commands, true); + } + + /// + /// Initializes a new instance of the AllModulesViewModel class. + /// + /// The loaded modules. + /// All PowerShell commands. + /// True not to show common parameters. + public AllModulesViewModel(Dictionary importedModules, IEnumerable commands, bool noCommonParameter) + { + ArgumentNullException.ThrowIfNull(commands); + + this.Initialization(importedModules, commands, noCommonParameter); + } + + #endregion + + #region INotifyPropertyChanged Members + /// + /// PropertyChanged Event. + /// + public event PropertyChangedEventHandler PropertyChanged; + #endregion + + /// + /// Indicates the selected command in the selected module needs to display the help for a command. + /// + public event EventHandler SelectedCommandInSelectedModuleNeedsHelp; + + /// + /// Indicates the selected command in the selected module needs to import a module for a command. + /// + public event EventHandler SelectedCommandInSelectedModuleNeedsImportModule; + + /// + /// Indicates the selected command in the selected module should be run. + /// + public event EventHandler RunSelectedCommandInSelectedModule; + + /// + /// Indicates we want to refresh the viewModel. + /// + public event EventHandler Refresh; + + #region Public Properties + + /// + /// Get or Sets Zoom level. + /// + public double ZoomLevel + { + get + { + return this.zoomLevel; + } + + set + { + if (value > 0) + { + this.zoomLevel = value / 100.0; + this.OnNotifyPropertyChanged("ZoomLevel"); + } + } + } + + /// + /// Gets the tooltip for the refresh button. + /// + public static string RefreshTooltip + { + get { return string.Format(CultureInfo.CurrentUICulture, ShowCommandResources.RefreshShowCommandTooltipFormat, "import-module"); } + } + + /// + /// Gets or sets the visibility of the refresh button. + /// + public Visibility RefreshVisibility + { + get + { + return this.refreshVisibility; + } + + set + { + if (this.refreshVisibility == value) + { + return; + } + + this.refreshVisibility = value; + this.OnNotifyPropertyChanged("RefreshVisibility"); + } + } + + /// + /// Gets a value indicating whether common parameters are displayed. + /// + public bool NoCommonParameter + { + get { return this.noCommonParameter; } + } + + /// + /// Gets or sets the filterName of command. + /// + public string CommandNameFilter + { + get + { + return this.commandNameFilter; + } + + set + { + if (this.CommandNameFilter == value) + { + return; + } + + this.commandNameFilter = value; + if (this.selectedModule != null) + { + this.selectedModule.RefreshFilteredCommands(this.CommandNameFilter); + this.selectedModule.SelectedCommand = null; + } + + this.OnNotifyPropertyChanged("CommandNameFilter"); + } + } + + /// + /// Gets or sets the selected module being displayed in the GUI. + /// + public ModuleViewModel SelectedModule + { + get + { + return this.selectedModule; + } + + set + { + if (this.selectedModule == value) + { + return; + } + + if (this.selectedModule != null) + { + this.selectedModule.SelectedCommandNeedsImportModule -= this.SelectedModule_SelectedCommandNeedsImportModule; + this.selectedModule.SelectedCommandNeedsHelp -= this.SelectedModule_SelectedCommandNeedsHelp; + this.selectedModule.RunSelectedCommand -= this.SelectedModule_RunSelectedCommand; + this.selectedModule.PropertyChanged -= this.SelectedModule_PropertyChanged; + } + + this.selectedModule = value; + this.SetCanRun(); + this.SetCanCopy(); + + if (this.selectedModule != null) + { + this.selectedModule.RefreshFilteredCommands(this.CommandNameFilter); + this.selectedModule.SelectedCommandNeedsImportModule += this.SelectedModule_SelectedCommandNeedsImportModule; + this.selectedModule.SelectedCommandNeedsHelp += this.SelectedModule_SelectedCommandNeedsHelp; + this.selectedModule.RunSelectedCommand += this.SelectedModule_RunSelectedCommand; + this.selectedModule.PropertyChanged += this.SelectedModule_PropertyChanged; + this.selectedModule.SelectedCommand = null; + } + + this.OnNotifyPropertyChanged("SelectedModule"); + } + } + + /// + /// Gets a value indicating whether we can run a command. + /// + public bool CanRun + { + get + { + return this.canRun; + } + } + + /// + /// Gets a value indicating whether we can copy a command. + /// + public bool CanCopy + { + get + { + return this.canCopy; + } + } + + /// + /// Gets the Modules parameter. + /// + public List Modules + { + get { return this.modules; } + } + + /// + /// Gets the visibility of the wait message. + /// + public Visibility WaitMessageVisibility + { + get + { + return this.waitMessageDisplayed ? Visibility.Visible : Visibility.Hidden; + } + } + + /// + /// Gets the visibility of the main grid. + /// + public Visibility MainGridVisibility + { + get + { + return this.waitMessageDisplayed ? Visibility.Hidden : Visibility.Visible; + } + } + + /// + /// Gets a value indicating whether the main grid is displayed. + /// + public bool MainGridDisplayed + { + get + { + return !this.waitMessageDisplayed; + } + } + + /// + /// Gets or sets a value indicating whether the wait message is displayed. + /// + public bool WaitMessageDisplayed + { + get + { + return this.waitMessageDisplayed; + } + + set + { + if (this.waitMessageDisplayed == value) + { + return; + } + + this.waitMessageDisplayed = value; + this.SetCanCopy(); + this.SetCanRun(); + this.OnNotifyPropertyChanged("WaitMessageDisplayed"); + this.OnNotifyPropertyChanged("WaitMessageVisibility"); + this.OnNotifyPropertyChanged("MainGridDisplayed"); + this.OnNotifyPropertyChanged("MainGridVisibility"); + } + } + + /// + /// Gets or sets an extra viewModel object that allows callers to control certain aspects of the GUI. + /// + public object ExtraViewModel + { + get + { + return this.extraViewModel; + } + + set + { + if (this.extraViewModel == value) + { + return; + } + + this.extraViewModel = value; + this.OnNotifyPropertyChanged("ExtraViewModel"); + } + } + #endregion + + /// + /// Returns the selected script. + /// + /// The selected script. + public string GetScript() + { + if (this.SelectedModule == null) + { + return null; + } + + if (this.SelectedModule.SelectedCommand == null) + { + return null; + } + + return this.SelectedModule.SelectedCommand.GetScript(); + } + + /// + /// Triggers Refresh. + /// + internal void OnRefresh() + { + EventHandler handler = this.Refresh; + if (handler != null) + { + handler(this, new EventArgs()); + } + } + + #region Private Methods + /// + /// If current modules name is ALL, then return true. + /// + /// The modules name. + /// Return true is the module name is ALLModulesViewModel. + private static bool IsAll(string name) + { + return name.Equals(ShowCommandResources.All, StringComparison.Ordinal); + } + + /// + /// Monitors property changes in the selected module to call: + /// SetCanRun for IsThereASelectedImportedCommandWhereAllMandatoryParametersHaveValues + /// SetCanCopy for SetCanCopy + /// + /// Event sender. + /// Event arguments. + private void SelectedModule_PropertyChanged(object sender, PropertyChangedEventArgs e) + { + if (e.PropertyName == "IsThereASelectedImportedCommandWhereAllMandatoryParametersHaveValues") + { + this.SetCanRun(); + } + else if (e.PropertyName == "IsThereASelectedCommand") + { + this.SetCanCopy(); + } + } + + /// + /// Called to set this.CanRun when: + /// The SelectedModule changes, since there will be no selected command in the new module, and CanRun should be false + /// WaitMessageDisplayedMessage changes since this being true will cause this.MainGridDisplayed to be false and CanRun should be false + /// IsThereASelectedImportedCommandWhereAllMandatoryParametersHaveValues changes in the selected module + /// + private void SetCanRun() + { + bool newValue = this.selectedModule != null && this.MainGridDisplayed && + this.selectedModule.IsThereASelectedImportedCommandWhereAllMandatoryParametersHaveValues; + + if (this.canRun == newValue) + { + return; + } + + this.canRun = newValue; + this.OnNotifyPropertyChanged("CanRun"); + } + + /// + /// Called to set this.CanCopy when: + /// The SelectedModule changes, since there will be no selected command in the new module, and CanCopy should be false + /// WaitMessageDisplayedMessage changes since this being true will cause this.MainGridDisplayed to be false and CanCopy should be false + /// IsThereASelectedCommand changes in the selected module + /// + private void SetCanCopy() + { + bool newValue = this.selectedModule != null && this.MainGridDisplayed && this.selectedModule.IsThereASelectedCommand; + + if (this.canCopy == newValue) + { + return; + } + + this.canCopy = newValue; + this.OnNotifyPropertyChanged("CanCopy"); + } + + /// + /// Initialize AllModulesViewModel. + /// + /// All loaded modules. + /// List of commands in all modules. + /// Whether showing common parameter. + private void Initialization(Dictionary importedModules, IEnumerable commands, bool noCommonParameterInModel) + { + if (commands == null) + { + return; + } + + Dictionary rawModuleViewModels = new Dictionary(); + + this.noCommonParameter = noCommonParameterInModel; + + // separates commands in their Modules + foreach (ShowCommandCommandInfo command in commands) + { + ModuleViewModel moduleViewModel; + if (!rawModuleViewModels.TryGetValue(command.ModuleName, out moduleViewModel)) + { + moduleViewModel = new ModuleViewModel(command.ModuleName, importedModules); + rawModuleViewModels.Add(command.ModuleName, moduleViewModel); + } + + CommandViewModel commandViewModel; + + try + { + commandViewModel = CommandViewModel.GetCommandViewModel(moduleViewModel, command, noCommonParameterInModel); + } + catch (RuntimeException) + { + continue; + } + + moduleViewModel.Commands.Add(commandViewModel); + moduleViewModel.SetAllModules(this); + } + + // populates this.modules + this.modules = new List(); + + // if there is just one module then use only it + if (rawModuleViewModels.Values.Count == 1) + { + this.modules.Add(rawModuleViewModels.Values.First()); + this.modules[0].SortCommands(false); + this.SelectedModule = this.modules[0]; + return; + } + + // If there are more modules, create an additional module to aggregate all commands + ModuleViewModel allCommandsModule = new ModuleViewModel(ShowCommandResources.All, null); + this.modules.Add(allCommandsModule); + allCommandsModule.SetAllModules(this); + + if (rawModuleViewModels.Values.Count > 0) + { + foreach (ModuleViewModel module in rawModuleViewModels.Values) + { + module.SortCommands(false); + this.modules.Add(module); + + allCommandsModule.Commands.AddRange(module.Commands); + } + } + + allCommandsModule.SortCommands(true); + + this.modules.Sort(this.Compare); + this.SelectedModule = this.modules.Count == 0 ? null : this.modules[0]; + } + + /// + /// Compare two ModuleViewModel target and source. + /// + /// The source ModuleViewModel. + /// The target ModuleViewModel. + /// Compare result. + private int Compare(ModuleViewModel source, ModuleViewModel target) + { + if (AllModulesViewModel.IsAll(source.Name) && !AllModulesViewModel.IsAll(target.Name)) + { + return -1; + } + + if (!AllModulesViewModel.IsAll(source.Name) && AllModulesViewModel.IsAll(target.Name)) + { + return 1; + } + + return string.Compare(source.Name, target.Name, StringComparison.OrdinalIgnoreCase); + } + + /// + /// Called when the SelectedCommandNeedsHelp event is triggered in the Selected Module. + /// + /// Event sender. + /// Event arguments. + private void SelectedModule_SelectedCommandNeedsHelp(object sender, HelpNeededEventArgs e) + { + this.OnSelectedCommandInSelectedModuleNeedsHelp(e); + } + + /// + /// Called when the SelectedCommandNeedsImportModule event is triggered in the Selected Module. + /// + /// Event sender. + /// Event arguments. + private void SelectedModule_SelectedCommandNeedsImportModule(object sender, ImportModuleEventArgs e) + { + this.OnSelectedCommandInSelectedModuleNeedsImportModule(e); + } + + /// + /// Triggers SelectedCommandInSelectedModuleNeedsHelp. + /// + /// Event arguments. + private void OnSelectedCommandInSelectedModuleNeedsHelp(HelpNeededEventArgs e) + { + EventHandler handler = this.SelectedCommandInSelectedModuleNeedsHelp; + if (handler != null) + { + handler(this, e); + } + } + + /// + /// Triggers SelectedCommandInSelectedModuleNeedsImportModule. + /// + /// Event arguments. + private void OnSelectedCommandInSelectedModuleNeedsImportModule(ImportModuleEventArgs e) + { + EventHandler handler = this.SelectedCommandInSelectedModuleNeedsImportModule; + if (handler != null) + { + handler(this, e); + } + } + + /// + /// Called when the RunSelectedCommand is triggered in the selected module. + /// + /// Event sender. + /// Event arguments. + private void SelectedModule_RunSelectedCommand(object sender, CommandEventArgs e) + { + this.OnRunSelectedCommandInSelectedModule(e); + } + + /// + /// Triggers RunSelectedCommandInSelectedModule. + /// + /// Event arguments. + private void OnRunSelectedCommandInSelectedModule(CommandEventArgs e) + { + EventHandler handler = this.RunSelectedCommandInSelectedModule; + if (handler != null) + { + handler(this, e); + } + } + + /// + /// If property changed will be notify. + /// + /// The changed property. + private void OnNotifyPropertyChanged(string propertyName) + { + PropertyChangedEventHandler handler = this.PropertyChanged; + if (handler != null) + { + handler(this, new PropertyChangedEventArgs(propertyName)); + } + } + #endregion + } +} diff --git a/src/Microsoft.Management.UI.Internal/ShowCommand/ViewModel/CommandEventArgs.cs b/src/Microsoft.Management.UI.Internal/ShowCommand/ViewModel/CommandEventArgs.cs new file mode 100644 index 00000000000..857d466c80f --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ShowCommand/ViewModel/CommandEventArgs.cs @@ -0,0 +1,35 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; + +namespace Microsoft.PowerShell.Commands.ShowCommandInternal +{ + /// + /// Arguments for the event triggered when something happens at the cmdlet level. + /// + public class CommandEventArgs : EventArgs + { + /// + /// the command targeted by the event. + /// + private CommandViewModel command; + + /// + /// Initializes a new instance of the CommandEventArgs class. + /// + /// The command targeted by the event. + public CommandEventArgs(CommandViewModel command) + { + this.command = command; + } + + /// + /// Gets the command targeted by the event. + /// + public CommandViewModel Command + { + get { return this.command; } + } + } +} diff --git a/src/Microsoft.Management.UI.Internal/ShowCommand/ViewModel/CommandViewModel.cs b/src/Microsoft.Management.UI.Internal/ShowCommand/ViewModel/CommandViewModel.cs new file mode 100644 index 00000000000..cfa2798963c --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ShowCommand/ViewModel/CommandViewModel.cs @@ -0,0 +1,645 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Linq; +using System.Management.Automation; +using System.Text; +using System.Windows; + +using Microsoft.Management.UI; +using Microsoft.Management.UI.Internal; +using Microsoft.PowerShell.Commands.ShowCommandExtension; + +using SMAI = System.Management.Automation.Internal; + +namespace Microsoft.PowerShell.Commands.ShowCommandInternal +{ + /// + /// Contains information about a cmdlet's Shard ParameterSet, + /// ParameterSets, Parameters, Common Parameters and error message. + /// + public class CommandViewModel : INotifyPropertyChanged + { + #region Private Fields + /// + /// The name of the AllParameterSets. + /// + private const string SharedParameterSetName = "__AllParameterSets"; + + /// + /// Grid length constant. + /// + private static readonly GridLength star = new GridLength(1, GridUnitType.Star); + + /// + /// The module containing this cmdlet in the gui. + /// + private ModuleViewModel parentModule; + + /// + /// The name of the default ParameterSet. + /// + private string defaultParameterSetName; + + /// + /// Field used for the AreCommonParametersExpanded parameter. + /// + private bool areCommonParametersExpanded; + + /// + /// Field used for the SelectedParameterSet parameter. + /// + private ParameterSetViewModel selectedParameterSet; + + /// + /// Field used for the ParameterSets parameter. + /// + private List parameterSets = new List(); + + /// + /// Field used for the ParameterSetTabControlVisibility parameter. + /// + private bool noCommonParameters; + + /// + /// Field used for the CommonParameters parameter. + /// + private ParameterSetViewModel comonParameters; + + /// + /// The ShowCommandCommandInfo this model is based on. + /// + private ShowCommandCommandInfo commandInfo; + + /// + /// value indicating whether the selected parameter set has all mandatory parameters valid. + /// + private bool selectedParameterSetAllMandatoryParametersHaveValues; + + /// + /// value indicating whether the command name should be qualified by the module in GetScript. + /// + private bool moduleQualifyCommandName; + + /// + /// The height for common parameters that will depend on CommonParameterVisibility. + /// + private GridLength commonParametersHeight; + #endregion + + /// + /// Prevents a default instance of the CommandViewModel class from being created. + /// + private CommandViewModel() + { + } + + #region INotifyPropertyChanged Members + + /// + /// PropertyChanged Event. + /// + public event PropertyChangedEventHandler PropertyChanged; + + #endregion + + /// + /// Indicates the command needs to display the help for a command. + /// + public event EventHandler HelpNeeded; + + /// + /// Indicates a module needs to be imported. + /// + public event EventHandler ImportModule; + + #region Public Properties + /// + /// Gets or sets a value indicating whether the command name should be qualified by the module in GetScript. + /// + public bool ModuleQualifyCommandName + { + get { return this.moduleQualifyCommandName; } + set { this.moduleQualifyCommandName = value; } + } + + /// + /// Gets or sets a value indicating whether the common parameters are expanded. + /// + public bool AreCommonParametersExpanded + { + get + { + return this.areCommonParametersExpanded; + } + + set + { + if (this.areCommonParametersExpanded == value) + { + return; + } + + this.areCommonParametersExpanded = value; + this.OnNotifyPropertyChanged("AreCommonParametersExpanded"); + this.SetCommonParametersHeight(); + } + } + + /// + /// Gets or sets the SelectedParameterSet parameter. + /// + public ParameterSetViewModel SelectedParameterSet + { + get + { + return this.selectedParameterSet; + } + + set + { + if (this.selectedParameterSet != value) + { + if (this.selectedParameterSet != null) + { + this.selectedParameterSet.PropertyChanged -= this.SelectedParameterSet_PropertyChanged; + } + + this.selectedParameterSet = value; + if (this.selectedParameterSet != null) + { + this.selectedParameterSet.PropertyChanged += this.SelectedParameterSet_PropertyChanged; + this.SelectedParameterSetAllMandatoryParametersHaveValues = this.SelectedParameterSet.AllMandatoryParametersHaveValues; + } + else + { + this.SelectedParameterSetAllMandatoryParametersHaveValues = true; + } + + this.OnNotifyPropertyChanged("SelectedParameterSet"); + } + } + } + + /// + /// Gets or sets a value indicating whether the selected parameter set has all mandatory parameters valid. + /// If there is no selected parameter set this value is true + /// + public bool SelectedParameterSetAllMandatoryParametersHaveValues + { + get + { + return this.selectedParameterSetAllMandatoryParametersHaveValues; + } + + set + { + if (this.selectedParameterSetAllMandatoryParametersHaveValues == value) + { + return; + } + + this.selectedParameterSetAllMandatoryParametersHaveValues = value; + this.OnNotifyPropertyChanged("SelectedParameterSetAllMandatoryParametersHaveValues"); + } + } + + /// + /// Gets the ParameterSets parameter. + /// + public List ParameterSets + { + get { return this.parameterSets; } + } + + /// + /// Gets the visibility for the tab control displaying several ParameterSetControl. This is displayed when there are more than 1 parameter sets. + /// + public Visibility ParameterSetTabControlVisibility + { + get { return (this.ParameterSets.Count > 1) && this.IsImported ? Visibility.Visible : Visibility.Collapsed; } + } + + /// + /// Gets the visibility for the single ParameterSetControl displayed when there is only 1 parameter set. + /// + public Visibility SingleParameterSetControlVisibility + { + get { return (this.ParameterSets.Count == 1) ? Visibility.Visible : Visibility.Collapsed; } + } + + /// + /// Gets the CommonParameters parameter. + /// + public ParameterSetViewModel CommonParameters + { + get { return this.comonParameters; } + } + + /// + /// Gets the CommonParameterVisibility parameter. + /// + public Visibility CommonParameterVisibility + { + get { return this.noCommonParameters || (this.CommonParameters.Parameters.Count == 0) ? Visibility.Collapsed : Visibility.Visible; } + } + + /// + /// Gets or sets the height for common parameters that will depend on CommonParameterVisibility. + /// + public GridLength CommonParametersHeight + { + get + { + return this.commonParametersHeight; + } + + set + { + if (this.commonParametersHeight == value) + { + return; + } + + this.commonParametersHeight = value; + this.OnNotifyPropertyChanged("CommonParametersHeight"); + } + } + + /// + /// Gets the visibility for the control displayed when the module is not imported. + /// + public Visibility NotImportedVisibility + { + get + { + return this.IsImported ? Visibility.Collapsed : Visibility.Visible; + } + } + + /// + /// Gets the visibility for the control displayed when there are no parameters. + /// + public Visibility NoParameterVisibility + { + get + { + bool hasNoParameters = this.ParameterSets.Count == 0 || (this.ParameterSets.Count == 1 && this.ParameterSets[0].Parameters.Count == 0); + return this.IsImported && hasNoParameters ? Visibility.Visible : Visibility.Collapsed; + } + } + + /// + /// Gets a value indicating whether the cmdlet comes from a module which is imported. + /// + public bool IsImported + { + get + { + return this.commandInfo.Module == null || this.ParentModule.IsModuleImported; + } + } + + /// + /// Gets the Name parameter. + /// + public string Name + { + get + { + if (this.commandInfo != null) + { + return this.commandInfo.Name; + } + + return string.Empty; + } + } + + /// + /// Gets the module path if it is not null or empty, or the name otherwise. + /// + public string ModuleName + { + get + { + if (this.commandInfo != null && this.commandInfo.ModuleName != null) + { + return this.commandInfo.ModuleName; + } + + return string.Empty; + } + } + + /// + /// Gets the module containing this cmdlet in the GUI. + /// + public ModuleViewModel ParentModule + { + get + { + return this.parentModule; + } + } + + /// + /// Gets Tooltip string for the cmdlet. + /// + public string ToolTip + { + get + { + return string.Format( + CultureInfo.CurrentCulture, + ShowCommandResources.CmdletTooltipFormat, + this.Name, + this.ParentModule.DisplayName, + this.IsImported ? ShowCommandResources.Imported : ShowCommandResources.NotImported); + } + } + + /// + /// Gets the message to be displayed when the cmdlet belongs to a module that is not imported. + /// + public string ImportModuleMessage + { + get + { + return string.Format( + CultureInfo.CurrentCulture, + ShowCommandResources.NotImportedFormat, + this.ModuleName, + this.Name, + ShowCommandResources.ImportModuleButtonText); + } + } + + /// + /// Gets the title for the cmdlet details. + /// + public string DetailsTitle + { + get + { + if (this.IsImported) + { + return string.Format( + CultureInfo.CurrentCulture, + ShowCommandResources.DetailsParameterTitleFormat, + this.Name); + } + else + { + return string.Format( + CultureInfo.CurrentCulture, + ShowCommandResources.NameLabelFormat, + this.Name); + } + } + } + #endregion + + /// + /// Gets a Grid length constant. + /// + internal static GridLength Star + { + get { return CommandViewModel.star; } + } + + /// + /// Gets the builded PowerShell script. + /// + /// Return script as string. + public string GetScript() + { + StringBuilder builder = new StringBuilder(); + + string commandName = this.commandInfo.CommandType == CommandTypes.ExternalScript ? this.commandInfo.Definition : this.Name; + + if (this.ModuleQualifyCommandName && !string.IsNullOrEmpty(this.ModuleName)) + { + commandName = this.ModuleName + "\\" + commandName; + } + + if (commandName.Contains(' ')) + { + builder.Append($"& \"{commandName}\""); + } + else + { + builder.Append(commandName); + } + + builder.Append(' '); + + if (this.SelectedParameterSet != null) + { + builder.Append(this.SelectedParameterSet.GetScript()); + builder.Append(' '); + } + + if (this.CommonParameters != null) + { + builder.Append(this.CommonParameters.GetScript()); + } + + string script = builder.ToString(); + + return script.Trim(); + } + + /// + /// Showing help information for current active cmdlet. + /// + public void OpenHelpWindow() + { + this.OnHelpNeeded(); + } + + /// + /// Determines whether current command name and a specified ParameterSetName have same name. + /// + /// The name of ShareParameterSet. + /// Return true is ShareParameterSet. Else return false. + internal static bool IsSharedParameterSetName(string name) + { + return name.Equals(CommandViewModel.SharedParameterSetName, StringComparison.OrdinalIgnoreCase); + } + + /// + /// Creates a new CommandViewModel out the . + /// + /// Module to which the CommandViewModel will belong to. + /// Will showing command. + /// True to ommit displaying common parameter. + /// If commandInfo is null + /// + /// If could not create the CommandViewModel. For instance the ShowCommandCommandInfo corresponding to + /// the following function will throw a RuntimeException when the ShowCommandCommandInfo Parameters + /// are retrieved: + /// function CrashMe ([I.Am.A.Type.That.Does.Not.Exist]$name) {} + /// + /// The CommandViewModel corresponding to commandInfo. + internal static CommandViewModel GetCommandViewModel(ModuleViewModel module, ShowCommandCommandInfo commandInfo, bool noCommonParameters) + { + ArgumentNullException.ThrowIfNull(commandInfo); + + CommandViewModel returnValue = new CommandViewModel(); + returnValue.commandInfo = commandInfo; + returnValue.noCommonParameters = noCommonParameters; + returnValue.parentModule = module; + + Dictionary commonParametersTable = new Dictionary(); + + foreach (ShowCommandParameterSetInfo parameterSetInfo in commandInfo.ParameterSets) + { + if (parameterSetInfo.IsDefault) + { + returnValue.defaultParameterSetName = parameterSetInfo.Name; + } + + List parametersForParameterSet = new List(); + foreach (ShowCommandParameterInfo parameterInfo in parameterSetInfo.Parameters) + { + bool isCommon = Cmdlet.CommonParameters.Contains(parameterInfo.Name); + + if (isCommon) + { + if (!commonParametersTable.ContainsKey(parameterInfo.Name)) + { + commonParametersTable.Add(parameterInfo.Name, new ParameterViewModel(parameterInfo, parameterSetInfo.Name)); + } + + continue; + } + + parametersForParameterSet.Add(new ParameterViewModel(parameterInfo, parameterSetInfo.Name)); + } + + if (parametersForParameterSet.Count != 0) + { + returnValue.ParameterSets.Add(new ParameterSetViewModel(parameterSetInfo.Name, parametersForParameterSet)); + } + } + + List commonParametersList = commonParametersTable.Values.ToList(); + returnValue.comonParameters = new ParameterSetViewModel(string.Empty, commonParametersList); + + returnValue.parameterSets.Sort(returnValue.Compare); + + if (returnValue.parameterSets.Count > 0) + { + // Setting SelectedParameterSet will also set SelectedParameterSetAllMandatoryParametersHaveValues + returnValue.SelectedParameterSet = returnValue.ParameterSets[0]; + } + else + { + returnValue.SelectedParameterSetAllMandatoryParametersHaveValues = true; + } + + returnValue.SetCommonParametersHeight(); + + return returnValue; + } + + /// + /// Called to trigger the event fired when help is needed for the command. + /// + internal void OnHelpNeeded() + { + EventHandler handler = this.HelpNeeded; + if (handler != null) + { + handler(this, new HelpNeededEventArgs(this.Name)); + } + } + + /// + /// Called to trigger the event fired when a module needs to be imported. + /// + internal void OnImportModule() + { + EventHandler handler = this.ImportModule; + if (handler != null) + { + handler(this, new EventArgs()); + } + } + + #region Private Methods + /// + /// Called to set the height for common parameters initially or when the AreCommonParametersExpanded changes. + /// + private void SetCommonParametersHeight() + { + this.CommonParametersHeight = this.AreCommonParametersExpanded ? CommandViewModel.Star : GridLength.Auto; + } + + /// + /// Compares source and target by being the default parameter set and then by name. + /// + /// Source paremeterset. + /// Target parameterset. + /// 0 if they are the same, -1 if source is smaller, 1 if source is larger. + private int Compare(ParameterSetViewModel source, ParameterSetViewModel target) + { + if (this.defaultParameterSetName != null) + { + if (source.Name.Equals(this.defaultParameterSetName) && target.Name.Equals(this.defaultParameterSetName)) + { + return 0; + } + + if (source.Name.Equals(this.defaultParameterSetName, StringComparison.Ordinal)) + { + return -1; + } + + if (target.Name.Equals(this.defaultParameterSetName, StringComparison.Ordinal)) + { + return 1; + } + } + + return string.CompareOrdinal(source.Name, target.Name); + } + + /// + /// If property changed will be notify. + /// + /// The changed property. + private void OnNotifyPropertyChanged(string propertyName) + { + PropertyChangedEventHandler handler = this.PropertyChanged; + if (handler != null) + { + handler(this, new PropertyChangedEventArgs(propertyName)); + } + } + + /// + /// Called when the PropertyChanged event is triggered on the SelectedParameterSet. + /// + /// Event sender. + /// Event arguments. + private void SelectedParameterSet_PropertyChanged(object sender, PropertyChangedEventArgs e) + { + if (!e.PropertyName.Equals("AllMandatoryParametersHaveValues")) + { + return; + } + + this.SelectedParameterSetAllMandatoryParametersHaveValues = this.SelectedParameterSet.AllMandatoryParametersHaveValues; + } + + #endregion + } +} diff --git a/src/Microsoft.Management.UI.Internal/ShowCommand/ViewModel/HelpNeededEventArgs.cs b/src/Microsoft.Management.UI.Internal/ShowCommand/ViewModel/HelpNeededEventArgs.cs new file mode 100644 index 00000000000..3d4c42a6cbe --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ShowCommand/ViewModel/HelpNeededEventArgs.cs @@ -0,0 +1,35 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; + +namespace Microsoft.PowerShell.Commands.ShowCommandInternal +{ + /// + /// Arguments for the event triggered when it is necessary to display help for a command. + /// + public class HelpNeededEventArgs : EventArgs + { + /// + /// the name for the command needing help. + /// + private string commandName; + + /// + /// Initializes a new instance of the HelpNeededEventArgs class. + /// + /// The name for the command needing help. + public HelpNeededEventArgs(string commandName) + { + this.commandName = commandName; + } + + /// + /// Gets the name for the command needing help. + /// + public string CommandName + { + get { return this.commandName; } + } + } +} diff --git a/src/Microsoft.Management.UI.Internal/ShowCommand/ViewModel/ImportModuleEventArgs.cs b/src/Microsoft.Management.UI.Internal/ShowCommand/ViewModel/ImportModuleEventArgs.cs new file mode 100644 index 00000000000..3d7a7ccf3f0 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ShowCommand/ViewModel/ImportModuleEventArgs.cs @@ -0,0 +1,70 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; + +namespace Microsoft.PowerShell.Commands.ShowCommandInternal +{ + /// + /// Arguments for the event triggered when it is necessary to display help for a command. + /// + public class ImportModuleEventArgs : EventArgs + { + /// + /// the name for the command belonging to the module to be imported. + /// + private string commandName; + + /// + /// the module path or name for the module we want to import. + /// + private string parentModuleName; + + /// + /// the name of the module that is selected, which can be different from parentModuleName + /// if "All" is selected + /// + private string selectedModuleName; + + /// + /// Initializes a new instance of the ImportModuleEventArgs class. + /// + /// The name for the command needing help. + /// The name of the module containing the command. + /// + /// the name of the module that is selected, which can be different from parentModuleName + /// if "All" is selected + /// + public ImportModuleEventArgs(string commandName, string parentModuleName, string selectedModuleName) + { + this.commandName = commandName; + this.parentModuleName = parentModuleName; + this.selectedModuleName = selectedModuleName; + } + + /// + /// Gets the name for the command belonging to the module to be imported. + /// + public string CommandName + { + get { return this.commandName; } + } + + /// + /// Gets the module path or name for the module we want to import. + /// + public string ParentModuleName + { + get { return this.parentModuleName; } + } + + /// + /// Gets the name of the module that is selected, which can be different from parentModuleName + /// if "All" is selected + /// + public string SelectedModuleName + { + get { return this.selectedModuleName; } + } + } +} diff --git a/src/Microsoft.Management.UI.Internal/ShowCommand/ViewModel/ModuleViewModel.cs b/src/Microsoft.Management.UI.Internal/ShowCommand/ViewModel/ModuleViewModel.cs new file mode 100644 index 00000000000..950dbe93758 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ShowCommand/ViewModel/ModuleViewModel.cs @@ -0,0 +1,528 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.Management.Automation; +using System.Windows; + +using Microsoft.Management.UI.Internal; +using Microsoft.PowerShell.Commands.ShowCommandExtension; + +namespace Microsoft.PowerShell.Commands.ShowCommandInternal +{ + /// + /// ModuleViewModel Contains information about a PowerShell module. + /// + public class ModuleViewModel : INotifyPropertyChanged + { + /// + /// True if the module is imported. + /// + private bool isModuleImported; + + /// + /// Field used for the Name parameter. + /// + private string name; + + /// + /// Filter commands property of this module. + /// + private ObservableCollection filteredCommands; + + /// + /// The selected command property of this module. + /// + private CommandViewModel selectedCommand; + + /// + /// Field used for the Commands parameter. + /// + private List commands; + + /// + /// value indicating whether there is a selected command which belongs to an imported module, + /// with no parameter sets or with a selected parameter set where all mandatory parameters have values + /// + private bool isThereASelectedImportedCommandWhereAllMandatoryParametersHaveValues; + + /// + /// value indicating whether there is a selected command. + /// + private bool isThereASelectedCommand; + + /// + /// The AllModulesViewModel containing this, if any. + /// + private AllModulesViewModel allModules; + + #region Construction and Destructor + /// + /// Initializes a new instance of the ModuleViewModel class. + /// + /// Module name. + /// All loaded modules. + public ModuleViewModel(string name, Dictionary importedModules) + { + ArgumentNullException.ThrowIfNull(name); + + this.name = name; + this.commands = new List(); + this.filteredCommands = new ObservableCollection(); + + // This check looks to see if the given module name shows up in + // the set of modules that are known to be imported in the current + // session. In remote PowerShell sessions, the core cmdlet module + // Microsoft.PowerShell.Core doesn't appear as being imported despite + // always being loaded by default. To make sure we don't incorrectly + // mark this module as not imported, check for it by name. + this.isModuleImported = + importedModules == null ? true : name.Length == 0 || + importedModules.ContainsKey(name) || + string.Equals("Microsoft.PowerShell.Core", name, StringComparison.OrdinalIgnoreCase); + } + #endregion + + #region INotifyPropertyChanged Members + + /// + /// PropertyChanged Event. + /// + public event PropertyChangedEventHandler PropertyChanged; + #endregion + + /// + /// Indicates the selected command in needs to display the help for a command. + /// + public event EventHandler SelectedCommandNeedsHelp; + + /// + /// Indicates the selected command needs to import a module. + /// + public event EventHandler SelectedCommandNeedsImportModule; + + /// + /// Indicates the selected command should be run. + /// + public event EventHandler RunSelectedCommand; + + #region Public Property + /// + /// Gets the name property of this ModuleView. + /// + public string Name + { + get { return this.name; } + } + + /// + /// Gets the GUI friendly module name. + /// + public string DisplayName + { + get + { + if (!string.IsNullOrEmpty(this.name)) + { + return this.name; + } + + return ShowCommandResources.NoModuleName; + } + } + + /// + /// Gets CommandControl is visibility or not. + /// + public Visibility CommandControlVisibility + { + get { return this.selectedCommand == null ? Visibility.Collapsed : Visibility.Visible; } + } + + /// + /// Gets CommandControl Height. + /// + public GridLength CommandRowHeight + { + get { return this.selectedCommand == null ? GridLength.Auto : CommandViewModel.Star; } + } + + /// + /// Gets the commands under in this module. + /// + public List Commands + { + get { return this.commands; } + } + + /// + /// Gets the filter commands of this module. + /// + public ObservableCollection FilteredCommands + { + get { return this.filteredCommands; } + } + + /// + /// Gets or sets the selected commands of this module. + /// + public CommandViewModel SelectedCommand + { + get + { + return this.selectedCommand; + } + + set + { + if (value == this.selectedCommand) + { + return; + } + + if (this.selectedCommand != null) + { + this.selectedCommand.PropertyChanged -= this.SelectedCommand_PropertyChanged; + this.selectedCommand.HelpNeeded -= this.SelectedCommand_HelpNeeded; + this.selectedCommand.ImportModule -= this.SelectedCommand_ImportModule; + } + + this.selectedCommand = value; + + this.SetIsThereASelectedImportedCommandWhereAllMandatoryParametersHaveValues(); + + if (this.selectedCommand != null) + { + this.selectedCommand.PropertyChanged += this.SelectedCommand_PropertyChanged; + this.selectedCommand.HelpNeeded += this.SelectedCommand_HelpNeeded; + this.selectedCommand.ImportModule += this.SelectedCommand_ImportModule; + this.IsThereASelectedCommand = true; + } + else + { + this.IsThereASelectedCommand = false; + } + + this.OnNotifyPropertyChanged("SelectedCommand"); + this.OnNotifyPropertyChanged("CommandControlVisibility"); + this.OnNotifyPropertyChanged("CommandRowHeight"); + } + } + + /// + /// Gets or sets a value indicating whether there is a selected command. + /// + public bool IsThereASelectedCommand + { + get + { + return this.isThereASelectedCommand; + } + + set + { + if (value == this.isThereASelectedCommand) + { + return; + } + + this.isThereASelectedCommand = value; + this.OnNotifyPropertyChanged("IsThereASelectedCommand"); + } + } + + /// + /// Gets or sets a value indicating whether there is a selected command which belongs + /// to an imported module, with no parameter sets or with a selected parameter set + /// where all mandatory parameters have values + /// + public bool IsThereASelectedImportedCommandWhereAllMandatoryParametersHaveValues + { + get + { + return this.isThereASelectedImportedCommandWhereAllMandatoryParametersHaveValues; + } + + set + { + if (value == this.isThereASelectedImportedCommandWhereAllMandatoryParametersHaveValues) + { + return; + } + + this.isThereASelectedImportedCommandWhereAllMandatoryParametersHaveValues = value; + + this.OnNotifyPropertyChanged("IsThereASelectedImportedCommandWhereAllMandatoryParametersHaveValues"); + } + } + + /// + /// Gets the AllModulesViewModel containing this, if any. + /// + public AllModulesViewModel AllModules + { + get + { + return this.allModules; + } + } + #endregion + + /// + /// Gets a value indicating whether the module is imported. + /// + internal bool IsModuleImported + { + get + { + return this.isModuleImported; + } + } + + /// + /// Sets the AllModulesViewModel containing this. + /// + /// The AllModulesViewModel containing this. + internal void SetAllModules(AllModulesViewModel parentAllModules) + { + this.allModules = parentAllModules; + } + + /// + /// Sorts commands and optionally sets ModuleQualifyCommandName. + /// + /// True to mark repeated commands with a flag that will produce a module qualified name in GetScript. + internal void SortCommands(bool markRepeatedCmdlets) + { + this.commands.Sort(this.Compare); + + if (!markRepeatedCmdlets || this.commands.Count == 0) + { + return; + } + + CommandViewModel reference = this.commands[0]; + for (int i = 1; i < this.commands.Count; i++) + { + CommandViewModel command = this.commands[i]; + if (reference.Name.Equals(command.Name, StringComparison.OrdinalIgnoreCase)) + { + reference.ModuleQualifyCommandName = true; + command.ModuleQualifyCommandName = true; + } + else + { + reference = command; + } + } + } + + /// + /// According commandNameFilter to filter command,and added the filter commands into filteredCommands property. + /// + /// Current filter. + internal void RefreshFilteredCommands(string filter) + { + this.filteredCommands.Clear(); + if (string.IsNullOrEmpty(filter)) + { + foreach (CommandViewModel command in this.Commands) + { + this.filteredCommands.Add(command); + } + + return; + } + + WildcardPattern filterPattern = null; + if (WildcardPattern.ContainsWildcardCharacters(filter)) + { + filterPattern = new WildcardPattern(filter, WildcardOptions.IgnoreCase); + } + + foreach (CommandViewModel command in this.Commands) + { + if (ModuleViewModel.Matches(filterPattern, command.Name, filter)) + { + this.filteredCommands.Add(command); + continue; + } + + if (filterPattern != null) + { + continue; + } + + string[] textSplit = filter.Split(' '); + if (textSplit.Length != 2) + { + continue; + } + + if (ModuleViewModel.Matches(filterPattern, command.Name, textSplit[0] + "-" + textSplit[1])) + { + this.filteredCommands.Add(command); + } + } + } + + /// + /// Called in response to a GUI event that requires the command to be run. + /// + internal void OnRunSelectedCommand() + { + EventHandler handler = this.RunSelectedCommand; + if (handler != null) + { + handler(this, new CommandEventArgs(this.SelectedCommand)); + } + } + + /// + /// Triggers the SelectedCommandNeedsHelp event. + /// + /// Event arguments. + internal void OnSelectedCommandNeedsHelp(HelpNeededEventArgs e) + { + EventHandler handler = this.SelectedCommandNeedsHelp; + if (handler != null) + { + handler(this, e); + } + } + + /// + /// Triggers the SelectedCommandNeedsImportModule event. + /// + internal void OnSelectedCommandNeedsImportModule() + { + EventHandler handler = this.SelectedCommandNeedsImportModule; + if (handler != null) + { + handler(this, new ImportModuleEventArgs(this.SelectedCommand.Name, this.SelectedCommand.ModuleName, this.Name)); + } + } + #region Private Method + + /// + /// Uses pattern matching if pattern is not null or calls MatchesEvenIfInPlural otherwise. + /// + /// Pattern corresponding to filter. + /// Command name string. + /// Filter string. + /// True if coparisonText matches str or pattern. + private static bool Matches(WildcardPattern filterPattern, string commandName, string filter) + { + if (filterPattern != null) + { + return filterPattern.IsMatch(commandName); + } + + return ModuleViewModel.MatchesEvenIfInPlural(commandName, filter); + } + + /// + /// Returns true if filter matches commandName, even when filter is in the plural. + /// + /// Command name string. + /// Filter string. + /// Return match result. + private static bool MatchesEvenIfInPlural(string commandName, string filter) + { + if (commandName.Contains(filter, StringComparison.OrdinalIgnoreCase)) + { + return true; + } + + if (filter.Length > 5 && filter.EndsWith("es", StringComparison.OrdinalIgnoreCase)) + { + ReadOnlySpan filterSpan = filter.AsSpan(0, filter.Length - 2); + return commandName.AsSpan().Contains(filterSpan, StringComparison.OrdinalIgnoreCase); + } + + if (filter.Length > 4 && filter.EndsWith("s", StringComparison.OrdinalIgnoreCase)) + { + ReadOnlySpan filterSpan = filter.AsSpan(0, filter.Length - 1); + return commandName.AsSpan().Contains(filterSpan, StringComparison.OrdinalIgnoreCase); + } + + return false; + } + + /// + /// Handles the HelpNeeded event in the selected command and triggers the SelectedCommandNeedsHelp event. + /// + /// HelpNeeded event sender. + /// HelpNeeded event argument. + private void SelectedCommand_HelpNeeded(object sender, HelpNeededEventArgs e) + { + this.OnSelectedCommandNeedsHelp(e); + } + + /// + /// Handles the ImportModule event in the selected command and triggers the SelectedCommandNeedsImportModule event. + /// + /// HelpNeeded event sender. + /// HelpNeeded event argument. + private void SelectedCommand_ImportModule(object sender, EventArgs e) + { + this.OnSelectedCommandNeedsImportModule(); + } + + /// + /// Called when the SelectedCommand property changes to update IsThereASelectedImportedCommandWhereAllMandatoryParametersHaveValues. + /// + /// Event sender. + /// Event arguments. + private void SelectedCommand_PropertyChanged(object sender, PropertyChangedEventArgs e) + { + if (!e.PropertyName.Equals("SelectedParameterSetAllMandatoryParametersHaveValues")) + { + return; + } + + this.SetIsThereASelectedImportedCommandWhereAllMandatoryParametersHaveValues(); + } + + /// + /// Called to set IsThereASelectedImportedCommandWhereAllMandatoryParametersHaveValues when + /// SelectedParameterSetAllMandatoryParametersHaveValues changes in the SelectedCommand or + /// when the SelectedCommand changes + /// + private void SetIsThereASelectedImportedCommandWhereAllMandatoryParametersHaveValues() + { + this.IsThereASelectedImportedCommandWhereAllMandatoryParametersHaveValues = + this.selectedCommand != null && + this.selectedCommand.IsImported && + this.selectedCommand.SelectedParameterSetAllMandatoryParametersHaveValues; + } + + /// + /// Compare source commandmodule is equal like target commandmodule. + /// + /// Source commandmodule. + /// Target commandmodule. + /// Return compare result. + private int Compare(CommandViewModel source, CommandViewModel target) + { + return string.Compare(source.Name, target.Name, StringComparison.OrdinalIgnoreCase); + } + #endregion + + /// + /// If property changed will be notify. + /// + /// The changed property. + private void OnNotifyPropertyChanged(string propertyName) + { + PropertyChangedEventHandler handler = this.PropertyChanged; + if (handler != null) + { + handler(this, new PropertyChangedEventArgs(propertyName)); + } + } + } +} diff --git a/src/Microsoft.Management.UI.Internal/ShowCommand/ViewModel/ParameterSetViewModel.cs b/src/Microsoft.Management.UI.Internal/ShowCommand/ViewModel/ParameterSetViewModel.cs new file mode 100644 index 00000000000..b4f42dd78a2 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ShowCommand/ViewModel/ParameterSetViewModel.cs @@ -0,0 +1,388 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; +using System.Management.Automation; +using System.Management.Automation.Language; +using System.Text; + +using Microsoft.PowerShell.Commands.ShowCommandExtension; + +namespace Microsoft.PowerShell.Commands.ShowCommandInternal +{ + /// + /// Contains information about a single ParameterSet inside a cmdlet. + /// + public class ParameterSetViewModel : INotifyPropertyChanged + { + /// + /// Field used for the Name parameter. + /// + private string name; + + /// + /// value indicating all mandatory parameters have values. + /// + private bool allMandatoryParametersHaveValues; + + /// + /// Field used for the Parameters parameter. + /// + private List parameters; + + #region Construction and Destructor + + /// + /// Initializes a new instance of the ParameterSetViewModel class. + /// + /// The name of the parameterSet. + /// The array parameters of the parameterSet. + [SuppressMessage("Microsoft.Design", "CA1002:DoNotExposeGenericLists", Justification = "this type is internal, made public only for WPF Binding")] + public ParameterSetViewModel( + string name, + List parameters) + { + ArgumentNullException.ThrowIfNull(name); + + ArgumentNullException.ThrowIfNull(parameters); + + parameters.Sort(Compare); + + this.name = name; + this.parameters = parameters; + foreach (ParameterViewModel parameter in this.parameters) + { + if (!parameter.IsMandatory) + { + continue; + } + + parameter.PropertyChanged += this.MandatoryParameter_PropertyChanged; + } + + this.EvaluateAllMandatoryParametersHaveValues(); + } + #endregion + + #region INotifyPropertyChanged Members + + /// + /// PropertyChanged Event. + /// + public event PropertyChangedEventHandler PropertyChanged; + + #endregion + + #region Public Property + /// + /// Gets the ParameterSet Name. + /// + public string Name + { + get { return this.name; } + } + + /// + /// Gets the Parameters of this parameterset. + /// + public List Parameters + { + get { return this.parameters; } + } + + /// + /// Gets or sets a value indicating whether all mandatory parameters have values. + /// + public bool AllMandatoryParametersHaveValues + { + get + { + return this.allMandatoryParametersHaveValues; + } + + set + { + if (this.allMandatoryParametersHaveValues != value) + { + this.allMandatoryParametersHaveValues = value; + this.OnNotifyPropertyChanged("AllMandatoryParametersHaveValues"); + } + } + } + #endregion + + #region Public Method + /// + /// Creates script according parameters of this parameterset. + /// + /// Return script of this parameterset parameters. + public string GetScript() + { + if (this.Parameters == null || this.Parameters.Count == 0) + { + return string.Empty; + } + + StringBuilder builder = new StringBuilder(); + foreach (ParameterViewModel parameter in this.Parameters) + { + if (parameter.Value == null) + { + continue; + } + + if (parameter.Parameter.ParameterType.IsSwitch) + { + if (((bool?)parameter.Value) == true) + { + builder.Append($"-{parameter.Name} "); + } + + continue; + } + + string parameterValueString = parameter.Value.ToString(); + + if (parameterValueString.Length == 0) + { + continue; + } + + ShowCommandParameterType parameterType = parameter.Parameter.ParameterType; + + if (parameterType.IsEnum || parameterType.IsString || (parameterType.IsArray && parameterType.ElementType.IsString)) + { + parameterValueString = ParameterSetViewModel.GetDelimitedParameter(parameterValueString, "\"", "\""); + } + else if (parameterType.IsScriptBlock) + { + parameterValueString = ParameterSetViewModel.GetDelimitedParameter(parameterValueString, "{", "}"); + } + else + { + parameterValueString = ParameterSetViewModel.GetDelimitedParameter(parameterValueString, "(", ")"); + } + + builder.Append($"-{parameter.Name} {parameterValueString} "); + } + + return builder.ToString().Trim(); + } + + /// + /// Gets the individual parameter count of this parameterset. + /// + /// Return individual parameter count of this parameterset. + public int GetIndividualParameterCount() + { + if (this.Parameters == null || this.Parameters.Count == 0) + { + return 0; + } + + int i = 0; + + foreach (ParameterViewModel p in this.Parameters) + { + if (p.IsInSharedParameterSet) + { + return i; + } + + i++; + } + + return i; + } + + #endregion + + #region Internal Method + + /// + /// Compare source parametermodel is equal like target parametermodel. + /// + /// The source of parametermodel. + /// The target of parametermodel. + /// Return compare result. + internal static int Compare(ParameterViewModel source, ParameterViewModel target) + { + if (source.Parameter.IsMandatory && !target.Parameter.IsMandatory) + { + return -1; + } + + if (!source.Parameter.IsMandatory && target.Parameter.IsMandatory) + { + return 1; + } + + return string.Compare(source.Parameter.Name, target.Parameter.Name); + } + + #endregion + + /// + /// Gets the delimited parameter if it needs delimitation and is not delimited. + /// + /// Value needing delimitation. + /// Open delimitation. + /// Close delimitation. + /// The delimited parameter if it needs delimitation and is not delimited. + private static string GetDelimitedParameter(string parameterValue, string openDelimiter, string closeDelimiter) + { + string parameterValueTrimmed = parameterValue.Trim(); + + if (parameterValueTrimmed.Length == 0) + { + return openDelimiter + parameterValue + closeDelimiter; + } + + char delimitationChar = ParameterSetViewModel.ParameterNeedsDelimitation(parameterValueTrimmed, openDelimiter.Length == 1 && openDelimiter[0] == '{'); + switch (delimitationChar) + { + case '1': + return openDelimiter + parameterValue + closeDelimiter; + case '\'': + return '\'' + parameterValue + '\''; + case '\"': + return '\"' + parameterValue + '\"'; + default: + return parameterValueTrimmed; + } + } + + /// + /// Returns '0' if the does not need delimitation, '1' if it does, and a quote character if it needs to be delimited with a quote. + /// + /// Parameter value to check. + /// True if the parameter value should be a scriptblock. + /// '0' if the parameter does not need delimitation, '1' if it needs, '\'' if it needs to be delimited with single quote and '\"' if it needs to be delimited with double quotes. + private static char ParameterNeedsDelimitation(string parameterValue, bool requireScriptblock) + { + Token[] tokens; + ParseError[] errors; + ScriptBlockAst values = Parser.ParseInput("commandName -parameterName " + parameterValue, out tokens, out errors); + + if (values == null || values.EndBlock == null || values.EndBlock.Statements.Count == 0) + { + return '1'; + } + + PipelineAst pipeline = values.EndBlock.Statements[0] as PipelineAst; + if (pipeline == null || pipeline.PipelineElements.Count == 0) + { + return '1'; + } + + CommandAst commandAst = pipeline.PipelineElements[0] as CommandAst; + + if (commandAst == null || commandAst.CommandElements.Count == 0) + { + return '1'; + } + + // 3 is for CommandName, Parameter and its value + if (commandAst.CommandElements.Count != 3) + { + return '1'; + } + + if (requireScriptblock) + { + ScriptBlockExpressionAst scriptAst = commandAst.CommandElements[2] as ScriptBlockExpressionAst; + return scriptAst == null ? '1' : '0'; + } + + StringConstantExpressionAst stringValue = commandAst.CommandElements[2] as StringConstantExpressionAst; + if (stringValue != null) + { + if (errors.Length == 0) + { + return '0'; + } + + char stringTerminationChar; + + if (stringValue.StringConstantType == StringConstantType.BareWord) + { + stringTerminationChar = parameterValue[0]; + } + else if (stringValue.StringConstantType == StringConstantType.DoubleQuoted || stringValue.StringConstantType == StringConstantType.DoubleQuotedHereString) + { + stringTerminationChar = '\"'; + } + else + { + stringTerminationChar = '\''; + } + + char oppositeTerminationChar = stringTerminationChar == '\"' ? '\'' : '\"'; + + // If the string is not terminated, it should be delimited by the opposite string termination character + return oppositeTerminationChar; + } + + if (errors.Length != 0) + { + return '1'; + } + + return '0'; + } + + /// + /// Called to evaluate the value of AllMandatoryParametersHaveValues. + /// + private void EvaluateAllMandatoryParametersHaveValues() + { + bool newCanRun = true; + foreach (ParameterViewModel parameter in this.parameters) + { + if (!parameter.IsMandatory) + { + continue; + } + + if (!parameter.HasValue) + { + newCanRun = false; + break; + } + } + + this.AllMandatoryParametersHaveValues = newCanRun; + } + + /// + /// If property changed will be notify. + /// + /// The changed property. + private void OnNotifyPropertyChanged(string propertyName) + { + PropertyChangedEventHandler handler = this.PropertyChanged; + if (handler != null) + { + handler(this, new PropertyChangedEventArgs(propertyName)); + } + } + + /// + /// Used to track changes to parameter values in order to verify the enabled state of buttons. + /// + /// Event arguments. + /// Event sender. + private void MandatoryParameter_PropertyChanged(object sender, PropertyChangedEventArgs e) + { + if (!e.PropertyName.Equals("Value", StringComparison.Ordinal)) + { + return; + } + + this.EvaluateAllMandatoryParametersHaveValues(); + } + } +} diff --git a/src/Microsoft.Management.UI.Internal/ShowCommand/ViewModel/ParameterViewModel.cs b/src/Microsoft.Management.UI.Internal/ShowCommand/ViewModel/ParameterViewModel.cs new file mode 100644 index 00000000000..93227df87a1 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ShowCommand/ViewModel/ParameterViewModel.cs @@ -0,0 +1,272 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.ComponentModel; +using System.Globalization; +using System.Management.Automation; +using System.Text; + +using Microsoft.Management.UI.Internal; +using Microsoft.PowerShell.Commands.ShowCommandExtension; + +namespace Microsoft.PowerShell.Commands.ShowCommandInternal +{ + /// + /// Contains information about a single parameter inside a parameter set. + /// If a parameter with the same name belongs to two (or more) parameter sets, + /// there will be two (or more) ParameterViewModel objects for the parameters, + /// each one inside its own ParameterSetViewModel. + /// + public class ParameterViewModel : INotifyPropertyChanged + { + /// + /// ParameterMetadata contains information that is the same throughout parameter sets + /// like Name and Type. + /// Note: It also happens to contain a list of all ParameterSetMetadata for the parametersets + /// in this cmdlet, but this information is not used in this class since if a parameter is + /// in multiple parametersets, there will be a ParameterViewModel for each time the parameter + /// appears in a parameterset. + /// + private ShowCommandParameterInfo parameter; + + /// + /// value entered in the GUI for the parameter. + /// + private object parameterValue; + + /// + /// Name of the parameter set this parameter is in. + /// + private string parameterSetName; + + #region Construction and Destructor + /// + /// Initializes a new instance of the ParameterViewModel class. + /// + /// The parameter information for this parameter. + /// The name of the parameter set this parameter is in. + public ParameterViewModel(ShowCommandParameterInfo parameter, string parameterSetName) + { + ArgumentNullException.ThrowIfNull(parameter); + + ArgumentNullException.ThrowIfNull(parameterSetName); + + this.parameter = parameter; + this.parameterSetName = parameterSetName; + + if (this.parameter.ParameterType.IsSwitch) + { + this.parameterValue = false; + } + else + { + this.parameterValue = string.Empty; + } + } + #endregion + + #region INotifyPropertyChanged Members + + /// + /// PropertyChanged Event. + /// + public event PropertyChangedEventHandler PropertyChanged; + + #endregion + + #region Properties + /// + /// Gets the ParameterMetadata that contains information that is the same throughout parameter sets + /// like Name and Type. + /// + public ShowCommandParameterInfo Parameter + { + get { return this.parameter; } + } + + /// + /// Gets or sets the value for this parameter from the GUI. + /// + public object Value + { + get + { + return this.parameterValue; + } + + set + { + if (this.parameterValue != value) + { + this.parameterValue = value; + this.OnNotifyPropertyChanged("Value"); + } + } + } + + /// + /// Gets the parameter name. + /// + public string Name + { + get { return this.Parameter.Name; } + } + + /// + /// Gets the name of the parameter set this parameter is in. + /// + public string ParameterSetName + { + get { return this.parameterSetName; } + } + + /// + /// Gets a value indicating whether this parameter is in the shared parameterset. + /// + public bool IsInSharedParameterSet + { + get { return CommandViewModel.IsSharedParameterSetName(this.parameterSetName); } + } + + /// + /// Gets Name with an extra suffix to indicate if the parameter is mandatory to serve. + /// + public string NameTextLabel + { + get + { + return this.Parameter.IsMandatory ? + string.Format( + CultureInfo.CurrentUICulture, + ShowCommandResources.MandatoryNameLabelFormat, + this.Name, + ShowCommandResources.MandatoryLabelSegment) : + string.Format( + CultureInfo.CurrentUICulture, + ShowCommandResources.NameLabelFormat, + this.Name); + } + } + + /// + /// Gets Label in the case this parameter is used in a combo box. + /// + public string NameCheckLabel + { + get + { + string returnValue = this.Parameter.Name; + if (this.Parameter.IsMandatory) + { + returnValue = string.Create(CultureInfo.CurrentUICulture, $"{returnValue}{ShowCommandResources.MandatoryLabelSegment}"); + } + + return returnValue; + } + } + + /// + /// Gets Tooltip string for the parameter. + /// + public string ToolTip + { + get + { + return ParameterViewModel.EvaluateTooltip( + this.Parameter.ParameterType.FullName, + this.Parameter.Position, + this.Parameter.IsMandatory, + this.IsInSharedParameterSet, + this.Parameter.ValueFromPipeline); + } + } + + /// + /// Gets a value indicating whether the parameter is mandatory. + /// + public bool IsMandatory + { + get { return this.Parameter.IsMandatory; } + } + + /// + /// Gets a value indicating whether the parameter has a value. + /// + public bool HasValue + { + get + { + if (this.Value == null) + { + return false; + } + + if (this.Parameter.ParameterType.IsSwitch) + { + return ((bool?)this.Value) == true; + } + + return this.Value.ToString().Length != 0; + } + } + #endregion + + /// + /// Evaluates the tooltip based on the parameters. + /// + /// Parameter type name. + /// Parameter position. + /// True if the parameter is mandatory. + /// True if the parameter is shared by parameter sets. + /// True if the parameter takes value from the pipeline. + /// the tooltip based on the parameters. + internal static string EvaluateTooltip(string typeName, int position, bool mandatory, bool shared, bool valueFromPipeline) + { + StringBuilder returnValue = new StringBuilder(string.Format( + CultureInfo.CurrentCulture, + ShowCommandResources.TypeFormat, + typeName)); + string newlineFormatString = Environment.NewLine + "{0}"; + + if (position >= 0) + { + string positionFormat = string.Format( + CultureInfo.CurrentCulture, + ShowCommandResources.PositionFormat, + position); + + returnValue.AppendFormat(CultureInfo.InvariantCulture, newlineFormatString, positionFormat); + } + + string optionalOrMandatory = mandatory ? ShowCommandResources.Mandatory : ShowCommandResources.Optional; + + returnValue.AppendFormat(CultureInfo.InvariantCulture, newlineFormatString, optionalOrMandatory); + + if (shared) + { + returnValue.AppendFormat(CultureInfo.InvariantCulture, newlineFormatString, ShowCommandResources.CommonToAllParameterSets); + } + + if (valueFromPipeline) + { + returnValue.AppendFormat(CultureInfo.InvariantCulture, newlineFormatString, ShowCommandResources.CanReceiveValueFromPipeline); + } + + return returnValue.ToString(); + } + + /// + /// If property changed will be notify. + /// + /// The changed property. + private void OnNotifyPropertyChanged(string propertyName) + { + PropertyChangedEventHandler handler = this.PropertyChanged; + if (handler != null) + { + handler(this, new PropertyChangedEventArgs(propertyName)); + } + } + } +} diff --git a/src/Microsoft.Management.UI.Internal/ShowCommand/Windows/MultipleSelectionDialog.xaml b/src/Microsoft.Management.UI.Internal/ShowCommand/Windows/MultipleSelectionDialog.xaml new file mode 100644 index 00000000000..185c5513ddb --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ShowCommand/Windows/MultipleSelectionDialog.xaml @@ -0,0 +1,31 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Microsoft.Management.UI.Internal/ShowCommand/Windows/MultipleSelectionDialog.xaml.cs b/src/Microsoft.Management.UI.Internal/ShowCommand/Windows/MultipleSelectionDialog.xaml.cs new file mode 100644 index 00000000000..8097952ffab --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ShowCommand/Windows/MultipleSelectionDialog.xaml.cs @@ -0,0 +1,43 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.Windows; + +namespace Microsoft.PowerShell.Commands.ShowCommandInternal +{ + /// + /// Interaction logic for MultipleSelectionDialog.xaml. + /// + public partial class MultipleSelectionDialog : Window + { + /// + /// Initializes a new instance of the MultipleSelectionDialog class. + /// + public MultipleSelectionDialog() + { + this.InitializeComponent(); + } + + /// + /// OK Click event function. + /// + /// Event sender. + /// Event arguments. + private void ButtonOK_Click(object sender, RoutedEventArgs e) + { + this.DialogResult = true; + this.Close(); + } + + /// + /// Cancel Click event function. + /// + /// Event sender. + /// Event arguments. + private void ButtonCancel_Click(object sender, RoutedEventArgs e) + { + this.DialogResult = false; + this.Close(); + } + } +} diff --git a/src/Microsoft.Management.UI.Internal/ShowCommand/Windows/ShowAllModulesWindow.xaml b/src/Microsoft.Management.UI.Internal/ShowCommand/Windows/ShowAllModulesWindow.xaml new file mode 100644 index 00000000000..7a88bb3699b --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ShowCommand/Windows/ShowAllModulesWindow.xaml @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Microsoft.Management.UI.Internal/ShowCommand/Windows/ShowAllModulesWindow.xaml.cs b/src/Microsoft.Management.UI.Internal/ShowCommand/Windows/ShowAllModulesWindow.xaml.cs new file mode 100644 index 00000000000..b644264376b --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ShowCommand/Windows/ShowAllModulesWindow.xaml.cs @@ -0,0 +1,197 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Windows; +using System.Windows.Input; + +using Microsoft.Management.UI.Internal; +using Microsoft.Management.UI.Internal.ShowCommand; + +namespace Microsoft.PowerShell.Commands.ShowCommandInternal +{ + /// + /// Interaction logic for CmdletGUI.xaml. + /// + public partial class ShowAllModulesWindow : Window + { + /// + /// private constants for ZoomLevel. + /// + private double zoomLevel = 1.0; + + /// + /// Zoom Increments. + /// + private const double ZOOM_INCREMENT = 0.2; + + /// + /// Max ZoomLevel. + /// + private const double ZOOM_MAX = 3.0; + + /// + /// Min ZoomLevel. + /// + private const double ZOOM_MIN = 0.5; + + #region Construction and Destructor + + /// + /// Initializes a new instance of the ShowAllModulesWindow class. + /// + public ShowAllModulesWindow() + { + this.InitializeComponent(); + + if (this.AllModulesControl != null && this.AllModulesControl.ShowModuleControl != null) + { + this.AllModulesControl.ShowModuleControl.Owner = this; + } + + this.SizeChanged += this.ShowAllModulesWindow_SizeChanged; + this.LocationChanged += this.ShowAllModulesWindow_LocationChanged; + this.StateChanged += this.ShowAllModulesWindow_StateChanged; + this.Loaded += this.ShowAllModulesWindow_Loaded; + + RoutedCommand plusSettings = new RoutedCommand(); + KeyGestureConverter keyGestureConverter = new KeyGestureConverter(); + + try + { + plusSettings.InputGestures.Add((KeyGesture)keyGestureConverter.ConvertFromString(UICultureResources.ZoomIn1Shortcut)); + plusSettings.InputGestures.Add((KeyGesture)keyGestureConverter.ConvertFromString(UICultureResources.ZoomIn2Shortcut)); + plusSettings.InputGestures.Add((KeyGesture)keyGestureConverter.ConvertFromString(UICultureResources.ZoomIn3Shortcut)); + plusSettings.InputGestures.Add((KeyGesture)keyGestureConverter.ConvertFromString(UICultureResources.ZoomIn4Shortcut)); + CommandBindings.Add(new CommandBinding(plusSettings, ZoomEventHandlerPlus)); + } + catch (NotSupportedException) + { + // localized has a problematic string - going to default + plusSettings.InputGestures.Add((KeyGesture)keyGestureConverter.ConvertFromString("Ctrl+Add")); + plusSettings.InputGestures.Add((KeyGesture)keyGestureConverter.ConvertFromString("Ctrl+Plus")); + CommandBindings.Add(new CommandBinding(plusSettings, ZoomEventHandlerPlus)); + } + + RoutedCommand minusSettings = new RoutedCommand(); + try + { + minusSettings.InputGestures.Add((KeyGesture)keyGestureConverter.ConvertFromString(UICultureResources.ZoomOut1Shortcut)); + minusSettings.InputGestures.Add((KeyGesture)keyGestureConverter.ConvertFromString(UICultureResources.ZoomOut2Shortcut)); + minusSettings.InputGestures.Add((KeyGesture)keyGestureConverter.ConvertFromString(UICultureResources.ZoomOut3Shortcut)); + minusSettings.InputGestures.Add((KeyGesture)keyGestureConverter.ConvertFromString(UICultureResources.ZoomOut4Shortcut)); + + CommandBindings.Add(new CommandBinding(minusSettings, ZoomEventHandlerMinus)); + } + catch (NotSupportedException) + { + // localized has a problematic string - going to default + minusSettings.InputGestures.Add((KeyGesture)keyGestureConverter.ConvertFromString("Ctrl+Subtract")); + minusSettings.InputGestures.Add((KeyGesture)keyGestureConverter.ConvertFromString("Ctrl+Minus")); + CommandBindings.Add(new CommandBinding(minusSettings, ZoomEventHandlerMinus)); + } + } + + /// + /// Saves the user settings. + /// + /// Event arguments. + protected override void OnClosed(System.EventArgs e) + { + ShowCommandSettings.Default.Save(); + base.OnClosed(e); + } + + /// + /// Sets the focus on the CommandName control. + /// + /// Event sender. + /// Event arguments. + private void ShowAllModulesWindow_Loaded(object sender, RoutedEventArgs e) + { + this.AllModulesControl.CommandName.Focus(); + } + + /// + /// Saves size changes in user settings. + /// + /// Event sender. + /// Event arguments. + private void ShowAllModulesWindow_SizeChanged(object sender, SizeChangedEventArgs e) + { + ShowCommandSettings.Default.ShowCommandsWidth = this.Width; + ShowCommandSettings.Default.ShowCommandsHeight = this.Height; + } + + /// + /// Saves position changes in user settings. + /// + /// Event sender. + /// Event arguments. + private void ShowAllModulesWindow_LocationChanged(object sender, System.EventArgs e) + { + ShowCommandSettings.Default.ShowCommandsTop = this.Top; + ShowCommandSettings.Default.ShowCommandsLeft = this.Left; + } + + /// + /// Updates the user setting with window state. + /// + /// Event sender. + /// Event arguments. + private void ShowAllModulesWindow_StateChanged(object sender, System.EventArgs e) + { + ShowCommandSettings.Default.ShowCommandsWindowMaximized = this.WindowState == WindowState.Maximized; + } + + /// + /// Implements ZoomIn. + /// + /// . + /// . + private void ZoomEventHandlerPlus(object sender, ExecutedRoutedEventArgs e) + { + AllModulesViewModel viewModel = this.DataContext as AllModulesViewModel; + if (viewModel == null) + { + return; + } + + if (this.zoomLevel == 0) + { + this.zoomLevel = 1; + } + + if (this.zoomLevel < ZOOM_MAX) + { + // ViewModel applies ZoomLevel after dividing it by 100, So multiply it by 100 and then later reset to normal by dividing for next zoom + this.zoomLevel = (this.zoomLevel + ZOOM_INCREMENT) * 100; + viewModel.ZoomLevel = this.zoomLevel; + this.zoomLevel /= 100; + } + } + + /// + /// Implements ZoomOut. + /// + /// . + /// . + private void ZoomEventHandlerMinus(object sender, ExecutedRoutedEventArgs e) + { + AllModulesViewModel viewModel = this.DataContext as AllModulesViewModel; + if (viewModel == null) + { + return; + } + + if (this.zoomLevel >= ZOOM_MIN) + { + // ViewModel applies ZoomLevel after dividing it by 100, So multiply it by 100 and then later reset to normal by dividing it for next zoom + this.zoomLevel = (this.zoomLevel - ZOOM_INCREMENT) * 100; + viewModel.ZoomLevel = this.zoomLevel; + this.zoomLevel /= 100; + } + } + #endregion + } +} diff --git a/src/Microsoft.Management.UI.Internal/ShowCommand/Windows/ShowCommandWindow.xaml b/src/Microsoft.Management.UI.Internal/ShowCommand/Windows/ShowCommandWindow.xaml new file mode 100644 index 00000000000..6617070be4e --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ShowCommand/Windows/ShowCommandWindow.xaml @@ -0,0 +1,32 @@ + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Microsoft.Management.UI.Internal/ShowCommand/Windows/ShowCommandWindow.xaml.cs b/src/Microsoft.Management.UI.Internal/ShowCommand/Windows/ShowCommandWindow.xaml.cs new file mode 100644 index 00000000000..c03a42ed864 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/ShowCommand/Windows/ShowCommandWindow.xaml.cs @@ -0,0 +1,71 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.Windows; + +using Microsoft.Management.UI.Internal.ShowCommand; + +namespace Microsoft.PowerShell.Commands.ShowCommandInternal +{ + /// + /// Interaction logic for CmdletGUI.xaml. + /// + public partial class ShowCommandWindow : Window + { + #region Construction and Destructor + + /// + /// Initializes a new instance of the ShowCommandWindow class. + /// + public ShowCommandWindow() + { + this.InitializeComponent(); + this.SizeChanged += this.ShowCommandWindow_SizeChanged; + this.LocationChanged += this.ShowCommandWindow_LocationChanged; + this.StateChanged += this.ShowCommandWindow_StateChanged; + } + + /// + /// Saves the user settings. + /// + /// Event arguments. + protected override void OnClosed(System.EventArgs e) + { + ShowCommandSettings.Default.Save(); + base.OnClosed(e); + } + + /// + /// Saves size changes in user settings. + /// + /// Event sender. + /// Event arguments. + private void ShowCommandWindow_SizeChanged(object sender, SizeChangedEventArgs e) + { + ShowCommandSettings.Default.ShowOneCommandWidth = this.Width; + ShowCommandSettings.Default.ShowOneCommandHeight = this.Height; + } + + /// + /// Saves position changes in user settings. + /// + /// Event sender. + /// Event arguments. + private void ShowCommandWindow_LocationChanged(object sender, System.EventArgs e) + { + ShowCommandSettings.Default.ShowOneCommandTop = this.Top; + ShowCommandSettings.Default.ShowOneCommandLeft = this.Left; + } + + /// + /// Updates the user setting with window state. + /// + /// Event sender. + /// Event arguments. + private void ShowCommandWindow_StateChanged(object sender, System.EventArgs e) + { + ShowCommandSettings.Default.ShowOneCommandWindowMaximized = this.WindowState == WindowState.Maximized; + } + #endregion + } +} diff --git a/src/Microsoft.Management.UI.Internal/app.config b/src/Microsoft.Management.UI.Internal/app.config new file mode 100644 index 00000000000..a2f13686380 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/app.config @@ -0,0 +1,99 @@ + + + + +
+
+ + + + + + -1 + + + -1 + + + -1 + + + -1 + + + -1 + + + -1 + + + -1 + + + -1 + + + False + + + False + + + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + True + + + False + + + False + + + -1 + + + -1 + + + False + + + 100 + + + 500 + + + 700 + + + True + + + + diff --git a/src/Microsoft.Management.UI.Internal/commandHelpers/HelpWindowHelper.cs b/src/Microsoft.Management.UI.Internal/commandHelpers/HelpWindowHelper.cs new file mode 100644 index 00000000000..e0b036a93d0 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/commandHelpers/HelpWindowHelper.cs @@ -0,0 +1,55 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Diagnostics.CodeAnalysis; +using System.Management.Automation; +using System.Threading; +using System.Windows; + +using Microsoft.Management.UI; +using Microsoft.PowerShell.Commands.ShowCommandInternal; + +namespace Microsoft.PowerShell.Commands.Internal +{ + /// + /// Implements the WPF window part of the ShowWindow option of get-help. + /// + internal static class HelpWindowHelper + { + /// + /// Shows the help window. + /// + /// Object with help information. + /// Cmdlet calling this method. + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called from methods called using reflection")] + private static void ShowHelpWindow(PSObject helpObj, PSCmdlet cmdlet) + { + Window ownerWindow = ShowCommandHelper.GetHostWindow(cmdlet); + if (ownerWindow != null) + { + ownerWindow.Dispatcher.Invoke( + new SendOrPostCallback( + (_) => + { + HelpWindow helpWindow = new HelpWindow(helpObj); + helpWindow.Owner = ownerWindow; + helpWindow.Show(); + + helpWindow.Closed += new EventHandler((sender, e) => ownerWindow.Focus()); + }), + string.Empty); + return; + } + + Thread guiThread = new Thread( + (ThreadStart)delegate + { + HelpWindow helpWindow = new HelpWindow(helpObj); + helpWindow.ShowDialog(); + }); + guiThread.SetApartmentState(ApartmentState.STA); + guiThread.Start(); + } + } +} diff --git a/src/Microsoft.Management.UI.Internal/commandHelpers/OutGridView.cs b/src/Microsoft.Management.UI.Internal/commandHelpers/OutGridView.cs new file mode 100644 index 00000000000..81621624992 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/commandHelpers/OutGridView.cs @@ -0,0 +1,607 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics.CodeAnalysis; +using System.Management.Automation; +using System.Management.Automation.Internal; +using System.Threading; +using System.Windows; +using System.Windows.Automation; +using System.Windows.Controls; +using System.Windows.Input; +using System.Windows.Media; + +namespace Microsoft.Management.UI.Internal +{ + /// + /// OutGridViewWindow definition for PowerShell command out-gridview. + /// + internal class OutGridViewWindow + { + #region private Fields + + /// + /// Zoom Increments. + /// + private const double ZOOM_INCREMENT = 0.2; + + /// + /// Max ZoomLevel. + /// + private const double ZOOM_MAX = 3.0; + + /// + /// Min ZoomLevel. + /// + private const double ZOOM_MIN = 0.5; + + /// + /// Window for gridView. + /// + private Window gridViewWindow; + + /// + /// Local ManagementList. + /// + private ManagementList managementList; + + /// + /// A collection of PSObjects to be data bound to the local Management List. + /// + private ObservableCollection listItems; + + /// + /// Event used for the thread gridViewWindows signaling main thread after Windows loaded. + /// + private AutoResetEvent gridViewWindowLoaded; + + /// Is used to store any Management list calls exceptions. + private Exception exception = null; + + /// + /// Is used to block thread of the pipeline. + /// + private AutoResetEvent closedEvent; + + /// + /// OK Button's content. + /// + private static readonly string OKButtonContent = XamlLocalizableResources.OutGridView_Button_OK; + + /// + /// Cancel Button's content. + /// + private static readonly string CancelButtonContent = XamlLocalizableResources.OutGridView_Button_Cancel; + + /// + /// Used to store selected items in the ok processing. + /// + private List selectedItems; + + /// + /// The GUI thread of Out-GridView. + /// + private Thread guiThread; + + /// + /// private constants for ZoomLevel. + /// + private double zoomLevel = 1.0; + + #endregion private Fields + + #region internal Constructors + + /// + /// Constructor for OutGridView. + /// + internal OutGridViewWindow() + { + // Initialize the data source collection. + this.listItems = new ObservableCollection(); + } + + #endregion internal Constructors + + #region private delegates + /// + /// ThreadDelegate definition. + /// + /// Start GridView Window delegate. + private delegate void ThreadDelegate(object arg); + + #endregion private delegates + + #region Private method that are intended to be called by the Out-GridView cmdlet. + + /// + /// Start a new thread as STA for gridView Window. + /// + /// Commands of the PowerShell. + /// Selection mode of the list. + /// ClosedEvent. + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "The method is called using reflection.")] + private void StartWindow(string invocation, string outputModeOptions, AutoResetEvent closedEvent) + { + this.closedEvent = closedEvent; + this.gridViewWindowLoaded = new AutoResetEvent(false); + + ParameterizedThreadStart threadStart = new ParameterizedThreadStart( + new ThreadDelegate(delegate + { + try + { + this.gridViewWindow = new Window(); + this.managementList = CreateManagementList(outputModeOptions); + this.gridViewWindow.Loaded += this.GridViewWindowLoaded; + this.gridViewWindow.Content = CreateMainGrid(outputModeOptions); + this.gridViewWindow.Title = invocation; + this.gridViewWindow.Closed += this.GridViewWindowClosed; + + RoutedCommand plusSettings = new RoutedCommand(); + KeyGestureConverter keyGestureConverter = new KeyGestureConverter(); + + try + { + plusSettings.InputGestures.Add((KeyGesture)keyGestureConverter.ConvertFromString(UICultureResources.ZoomIn1Shortcut)); + plusSettings.InputGestures.Add((KeyGesture)keyGestureConverter.ConvertFromString(UICultureResources.ZoomIn2Shortcut)); + plusSettings.InputGestures.Add((KeyGesture)keyGestureConverter.ConvertFromString(UICultureResources.ZoomIn3Shortcut)); + plusSettings.InputGestures.Add((KeyGesture)keyGestureConverter.ConvertFromString(UICultureResources.ZoomIn4Shortcut)); + this.gridViewWindow.CommandBindings.Add(new CommandBinding(plusSettings, ZoomEventHandlerPlus)); + } + catch (NotSupportedException) + { + // localized has a problematic string - going to default + plusSettings.InputGestures.Add((KeyGesture)keyGestureConverter.ConvertFromString("Ctrl+Add")); + plusSettings.InputGestures.Add((KeyGesture)keyGestureConverter.ConvertFromString("Ctrl+Plus")); + this.gridViewWindow.CommandBindings.Add(new CommandBinding(plusSettings, ZoomEventHandlerPlus)); + } + + RoutedCommand minusSettings = new RoutedCommand(); + try + { + minusSettings.InputGestures.Add((KeyGesture)keyGestureConverter.ConvertFromString(UICultureResources.ZoomOut1Shortcut)); + minusSettings.InputGestures.Add((KeyGesture)keyGestureConverter.ConvertFromString(UICultureResources.ZoomOut2Shortcut)); + minusSettings.InputGestures.Add((KeyGesture)keyGestureConverter.ConvertFromString(UICultureResources.ZoomOut3Shortcut)); + minusSettings.InputGestures.Add((KeyGesture)keyGestureConverter.ConvertFromString(UICultureResources.ZoomOut4Shortcut)); + + this.gridViewWindow.CommandBindings.Add(new CommandBinding(minusSettings, ZoomEventHandlerMinus)); + } + catch (NotSupportedException) + { + // localized has a problematic string - going to default + minusSettings.InputGestures.Add((KeyGesture)keyGestureConverter.ConvertFromString("Ctrl+Subtract")); + minusSettings.InputGestures.Add((KeyGesture)keyGestureConverter.ConvertFromString("Ctrl+Minus")); + this.gridViewWindow.CommandBindings.Add(new CommandBinding(minusSettings, ZoomEventHandlerMinus)); + } + + this.gridViewWindow.ShowDialog(); + } + catch (Exception e) + { + // Store the exception in a local variable that will be checked later. + if (e.InnerException != null) + { + this.exception = e.InnerException; + } + else + { + this.exception = e; + } + } + })); + + guiThread = new Thread(threadStart); + guiThread.SetApartmentState(ApartmentState.STA); + + guiThread.Start(); + } + + /// + /// Implements ZoomIn. + /// + /// . + /// . + private void ZoomEventHandlerPlus(object sender, ExecutedRoutedEventArgs e) + { + if (this.zoomLevel == 0) + { + this.zoomLevel = 1; + } + + if (this.zoomLevel < ZOOM_MAX) + { + this.zoomLevel += ZOOM_INCREMENT; + Grid g = this.gridViewWindow.Content as Grid; + + if (g != null) + { + g.LayoutTransform = new ScaleTransform(this.zoomLevel, this.zoomLevel, 0, 0); + } + } + } + + /// + /// Implements ZoomOut. + /// + /// . + /// . + private void ZoomEventHandlerMinus(object sender, ExecutedRoutedEventArgs e) + { + if (this.zoomLevel >= ZOOM_MIN) + { + this.zoomLevel -= ZOOM_INCREMENT; + Grid g = this.gridViewWindow.Content as Grid; + if (g != null) + { + g.LayoutTransform = new ScaleTransform(this.zoomLevel, this.zoomLevel, 0, 0); + } + } + } + + /// + /// Creates a new ManagementList. + /// + /// Output mode of the out-gridview. + /// A new ManagementList. + private ManagementList CreateManagementList(string outputMode) + { + ManagementList newList = new ManagementList(); + + newList.ViewSaverUserActionState = UserActionState.Hidden; + newList.ViewManagerUserActionState = UserActionState.Hidden; + newList.List.VerticalAlignment = VerticalAlignment.Stretch; + newList.List.SetValue(Grid.RowProperty, 0); + newList.List.SelectionMode = (outputMode == "Single") ? SelectionMode.Single : SelectionMode.Extended; + + return newList; + } + + /// + /// Creates a new main grid for window. + /// + /// Output mode of the out-gridview. + /// A new mainGrid. + private Grid CreateMainGrid(string outputMode) + { + Grid mainGrid = new Grid(); + mainGrid.RowDefinitions.Add(new RowDefinition()); + mainGrid.RowDefinitions[0].Height = new GridLength(1, GridUnitType.Star); + mainGrid.Children.Add(managementList); + + if (outputMode == "None") + { + return mainGrid; + } + + // OK and Cancel button should only be displayed if OutputMode is not None. + mainGrid.RowDefinitions.Add(new RowDefinition()); + mainGrid.RowDefinitions[1].Height = GridLength.Auto; + mainGrid.Children.Add(CreateButtonGrid()); + + return mainGrid; + } + + /// + /// Creates a OK button. + /// + /// A new buttonGrid. + private Grid CreateButtonGrid() + { + Grid buttonGrid = new Grid(); + + //// This will allow OK and Cancel to have the same width + buttonGrid.SetValue(Grid.IsSharedSizeScopeProperty, true); + buttonGrid.ColumnDefinitions.Add(new ColumnDefinition()); + buttonGrid.ColumnDefinitions.Add(new ColumnDefinition()); + buttonGrid.ColumnDefinitions[0].Width = GridLength.Auto; + buttonGrid.ColumnDefinitions[0].SharedSizeGroup = "okCancel"; + buttonGrid.ColumnDefinitions[1].Width = GridLength.Auto; + buttonGrid.ColumnDefinitions[1].SharedSizeGroup = "okCancel"; + buttonGrid.HorizontalAlignment = HorizontalAlignment.Right; + buttonGrid.SetValue(Grid.RowProperty, 1); + + //// This will add OK and Cancel button to buttonGrid. + buttonGrid.Children.Add(CreateOKButton()); + buttonGrid.Children.Add(CreateCancelButton()); + + return buttonGrid; + } + + /// + /// Creates a OK button. + /// + /// A new OK button. + private Button CreateOKButton() + { + Button ok = new Button(); + ok.Content = OKButtonContent; + ok.Margin = new Thickness(5); + ok.Padding = new Thickness(2); + ok.SetValue(Grid.ColumnProperty, 0); + ok.IsDefault = true; + ok.SetValue(AutomationProperties.AutomationIdProperty, "OGVOK"); + ok.Click += OK_Click; + return ok; + } + + /// + /// Creates a Cancel button. + /// + /// A new Cancel button. + private Button CreateCancelButton() + { + Button cancel = new Button(); + cancel.Content = CancelButtonContent; + cancel.Margin = new Thickness(5); + cancel.Padding = new Thickness(2); + cancel.SetValue(Grid.ColumnProperty, 1); + cancel.IsCancel = true; + cancel.SetValue(AutomationProperties.AutomationIdProperty, "OGVCancel"); + cancel.Click += Cancel_Click; + return cancel; + } + + /// + /// Store the selected items for use in EndProcessing. + /// + /// Event sender. + /// Event arguments. + private void OK_Click(object sender, RoutedEventArgs e) + { + if (this.managementList.List.SelectedItems.Count != 0) + { + this.selectedItems = new List(); + foreach (PSObject obj in this.managementList.List.SelectedItems) + { + this.selectedItems.Add(obj); + } + } + + this.gridViewWindow.Close(); + } + + /// + /// Closes the window. + /// + /// Event sender. + /// Event arguments. + private void Cancel_Click(object sender, RoutedEventArgs e) + { + this.gridViewWindow.Close(); + } + + /// + /// Gets selected items from List. + /// + /// Selected items of the list. + private List SelectedItems() + { + return this.selectedItems; + } + + /// + /// Closes the window. + /// + public void CloseWindow() + { + this.gridViewWindow.Dispatcher.Invoke(new ThreadStart(delegate { this.gridViewWindow.Close(); })); + } + + /// + /// Add column definitions to the underlying management list. + /// + /// An array of property names to add. + /// An array of display names to add. + /// An array of types to add. + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "The method is called using reflection.")] + private void AddColumns(string[] propertyNames, string[] displayNames, Type[] types) + { + // Wait for the gridViewWindow thread to signal that loading of Window is done + if (this.gridViewWindowLoaded != null) + { + this.gridViewWindowLoaded.WaitOne(); + this.gridViewWindowLoaded = null; + } + + this.managementList.Dispatcher.Invoke( + System.Windows.Threading.DispatcherPriority.Normal, + new Action( + () => + { + // Pick the length of the shortest incoming arrays. Normally all incoming arrays should be of the same length. + int length = propertyNames.Length; + if (length > displayNames.Length) + { + length = displayNames.Length; + } + + if (length > types.Length) + { + length = types.Length; + } + + try + { + // Clear all columns in case the view is changed. + this.managementList.List.Columns.Clear(); + + // Clear column filter rules. + this.managementList.AddFilterRulePicker.ColumnFilterRules.Clear(); + + // Add columns with provided names and Types. + for (int i = 0; i < propertyNames.Length; ++i) + { + DataTemplate dataTemplate; + bool haveTemplate = this.managementList.FilterRulePanel.TryGetContentTemplate(types[i], out dataTemplate); + InnerListColumn column = null; + + if (haveTemplate) + { + column = new InnerListColumn(new UIPropertyGroupDescription(propertyNames[i], displayNames[i], types[i])); + } + else + { + column = new InnerListColumn(new UIPropertyGroupDescription(propertyNames[i], displayNames[i], typeof(string))); + } + + this.managementList.AddColumn(column); + } + + this.managementList.List.SetColumnHeaderActions(); + + if (this.managementList.List.ItemsSource == null) + { + // Setting ItemsSource implicitly regenerates all columns. + this.managementList.List.ItemsSource = this.listItems; + } + + // Set focus on ListView + this.managementList.List.SelectedIndex = 0; + this.managementList.List.Focus(); + } + catch (Exception e) + { + // Store the exception in a local variable that will be checked later. + if (e.InnerException != null) + { + this.exception = e.InnerException; + } + else + { + this.exception = e; + } + } + })); + } + + /// + /// Add an item to ObservableCollection. + /// + /// PSObject of comlet data. + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "The method is called using reflection.")] + private void AddItem(PSObject value) + { + if (this.GetWindowClosedStatus()) + { + return; + } + + this.managementList.Dispatcher.BeginInvoke( + System.Windows.Threading.DispatcherPriority.Normal, + new Action( + () => + { + try + { + // Remove any potential ANSI decoration + foreach (var property in value.Properties) + { + if (property.Value is string str) + { + StringDecorated decoratedString = new StringDecorated(str); + property.Value = decoratedString.ToString(OutputRendering.PlainText); + } + } + + this.listItems.Add(value); + } + catch (Exception e) + { + // Store the exception in a local variable that will be checked later. + if (e.InnerException != null) + { + this.exception = e.InnerException; + } + else + { + this.exception = e; + } + } + })); + } + + /// + /// Returns the state of GridView Window. + /// + /// The status of GridView Window close or not. + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "The method is called using reflection.")] + private bool GetWindowClosedStatus() + { + if (this.closedEvent == null) + { + return false; + } + + return this.closedEvent.WaitOne(0); + } + + /// + /// Returns any exception that has been thrown by previous method calls. + /// + /// The thrown and caught exception. It returns null if no exceptions were thrown by any previous method calls. + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "The method is called using reflection.")] + private Exception GetLastException() + { + Exception local = this.exception; + + if (local != null) + { + // Clear the caught exception. + this.exception = null; + return local; + } + + return this.exception; + } + + #endregion Private method that are intended to be called by the Out-GridView cmdlet. + + #region Private methods + + /// + /// GridView Window is closing callback process. + /// + /// The sender object. + /// Event Args. + private void GridViewWindowClosed(object sender, EventArgs e) + { + if (this.closedEvent != null && !this.closedEvent.SafeWaitHandle.IsClosed) + { + try + { + this.closedEvent.Set(); + } + catch (ObjectDisposedException) + { + // we tried to avoid this exception with "&& !this.closedEvent.SafeWaitHandle.IsClosed" + // but since this runs in a different thread the if condition could be evaluated and after that + // the handle disposed + } + } + } + + /// + /// Set loaded as true when this method invoked. + /// + /// The sender object. + /// RoutedEvent Args. + private void GridViewWindowLoaded(object sender, RoutedEventArgs e) + { + // signal the main thread + this.gridViewWindowLoaded.Set(); + + // Make gridview window as active window + this.gridViewWindow.Activate(); + + // Set up AutomationId and Name + AutomationProperties.SetName(this.gridViewWindow, GraphicalHostResources.OutGridViewWindowObjectName); + AutomationProperties.SetAutomationId(this.gridViewWindow, "OutGridViewWindow"); + } + + #endregion Private methods + } +} diff --git a/src/Microsoft.Management.UI.Internal/commandHelpers/ShowCommandHelper.cs b/src/Microsoft.Management.UI.Internal/commandHelpers/ShowCommandHelper.cs new file mode 100644 index 00000000000..32c68e961c6 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/commandHelpers/ShowCommandHelper.cs @@ -0,0 +1,1266 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Management.Automation; +using System.Reflection; +using System.Threading; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Threading; + +using Microsoft.Management.UI; +using Microsoft.Management.UI.Internal; +using Microsoft.Management.UI.Internal.ShowCommand; +using Microsoft.PowerShell.Commands.ShowCommandExtension; + +namespace Microsoft.PowerShell.Commands.ShowCommandInternal +{ + /// + /// Implements the WPF window part of the show-command cmdlet. + /// + internal class ShowCommandHelper : IDisposable + { + #region fields + + internal const string CommandTypeSegment = " -CommandType Cmdlet, Function, Script, ExternalScript"; + + /// + /// Method that will return the dialog from ShowAllModulesWindow or ShowCommandWindow. + /// This is necessary because the PlainInvokeAndShowDialog thread starter cannot receive parameters + /// + private DispatcherOperationCallback methodThatReturnsDialog; + + /// + /// Event set when the window is closed. + /// + private AutoResetEvent windowClosed = new AutoResetEvent(false); + + /// + /// Event set when help is needed. + /// + private AutoResetEvent helpNeeded = new AutoResetEvent(false); + + /// + /// Event set when it is necessary to import a module. + /// + private AutoResetEvent importModuleNeeded = new AutoResetEvent(false); + + /// + /// Event set when the window is loaded. + /// + private AutoResetEvent windowLoaded = new AutoResetEvent(false); + + /// + /// String with the command that needs help set when helpNeeded is set. + /// + private string commandNeedingHelp; + + /// + /// String with the command name that needs to import a module. + /// + private string commandNeedingImportModule; + + /// + /// String with the module name that needs to be imported. + /// + private string parentModuleNeedingImportModule; + + /// + /// String with the selected module at the time a module needs to be imported. + /// + private string selectedModuleNeedingImportModule; + + /// + /// Keeps the window for the implementation of CloseWindow. + /// + private Window window; + + /// + /// host window, if any. + /// + private Window hostWindow; + + /// + /// ViewModel when showing all modules. + /// + private AllModulesViewModel allModulesViewModel; + + /// + /// ViewModel when showing a single command. + /// + private CommandViewModel commandViewModel; + + /// + /// true when the window is closed with cancel. + /// + private bool dialogCanceled = true; + #endregion fields + + #region GetSerializedCommand script + + private const string ScriptGetSerializedCommand = @" +Function PSGetSerializedShowCommandInfo +{ + Function GetParameterType + { + param ( + [Type] $parameterType) + + $returnParameterType = new-object PSObject + $returnParameterType | Add-Member -MemberType NoteProperty -Name ""FullName"" -Value $parameterType.FullName + $returnParameterType | Add-Member -MemberType NoteProperty -Name ""IsEnum"" -Value $parameterType.IsEnum + $returnParameterType | Add-Member -MemberType NoteProperty -Name ""IsArray"" -Value $parameterType.IsArray + + if ($parameterType.IsEnum) + { + $enumValues = [System.Enum]::GetValues($parameterType) + } + else + { + $enumValues = [string[]] @() + } + $returnParameterType | Add-Member -MemberType NoteProperty -Name ""EnumValues"" -Value $enumValues + + if ($parameterType.IsArray) + { + $hasFlagAttribute = ($parameterType.GetCustomAttributes([System.FlagsAttribute], $true).Length -gt 0) + + # Recurse into array elements. + $elementType = GetParameterType($parameterType.GetElementType()) + } + else + { + $hasFlagAttribute = $false + $elementType = $null + } + $returnParameterType | Add-Member -MemberType NoteProperty -Name ""HasFlagAttribute"" -Value $hasFlagAttribute + $returnParameterType | Add-Member -MemberType NoteProperty -Name ""ElementType"" -Value $elementType + + + if (!($parameterType.IsEnum) -and !($parameterType.IsArray)) + { + $implementsDictionary = [System.Collections.IDictionary].IsAssignableFrom($parameterType) + } + else + { + $implementsDictionary = $false + } + $returnParameterType | Add-Member -MemberType NoteProperty -Name ""ImplementsDictionary"" -Value $implementsDictionary + + return $returnParameterType + } + + Function GetParameterInfo + { + param ( + $parameters) + + [PSObject[]] $parameterInfos = @() + + foreach ($parameter in $parameters) + { + $parameterInfo = new-object PSObject + $parameterInfo | Add-Member -MemberType NoteProperty -Name ""Name"" -Value $parameter.Name + $parameterInfo | Add-Member -MemberType NoteProperty -Name ""IsMandatory"" -Value $parameter.IsMandatory + $parameterInfo | Add-Member -MemberType NoteProperty -Name ""ValueFromPipeline"" -Value $parameter.ValueFromPipeline + $parameterInfo | Add-Member -MemberType NoteProperty -Name ""Position"" -Value $parameter.Position + $parameterInfo | Add-Member -MemberType NoteProperty -Name ""ParameterType"" -Value (GetParameterType($parameter.ParameterType)) + + $hasParameterSet = $false + [string[]] $validValues = @() + if ($PSVersionTable.PSVersion.Major -gt 2) + { + $validateSetAttributes = $parameter.Attributes | Where { + [ValidateSet].IsAssignableFrom($_.GetType()) + } + if (($validateSetAttributes -ne $null) -and ($validateSetAttributes.Count -gt 0)) + { + $hasParameterSet = $true + $validValues = $validateSetAttributes[0].ValidValues + } + } + $parameterInfo | Add-Member -MemberType NoteProperty -Name ""HasParameterSet"" -Value $hasParameterSet + $parameterInfo | Add-Member -MemberType NoteProperty -Name ""ValidParamSetValues"" -Value $validValues + + $parameterInfos += $parameterInfo + } + + return (,$parameterInfos) + } + + Function GetParameterSets + { + param ( + [System.Management.Automation.CommandInfo] $cmdInfo + ) + + $parameterSets = $null + try + { + $parameterSets = $cmdInfo.ParameterSets + } + catch [System.InvalidOperationException] { } + catch [System.Management.Automation.PSNotSupportedException] { } + catch [System.Management.Automation.PSNotImplementedException] { } + + if (($parameterSets -eq $null) -or ($parameterSets.Count -eq 0)) + { + return (,@()) + } + + [PSObject[]] $returnParameterSets = @() + + foreach ($parameterSet in $parameterSets) + { + $parameterSetInfo = new-object PSObject + $parameterSetInfo | Add-Member -MemberType NoteProperty -Name ""Name"" -Value $parameterSet.Name + $parameterSetInfo | Add-Member -MemberType NoteProperty -Name ""IsDefault"" -Value $parameterSet.IsDefault + $parameterSetInfo | Add-Member -MemberType NoteProperty -Name ""Parameters"" -Value (GetParameterInfo($parameterSet.Parameters)) + + $returnParameterSets += $parameterSetInfo + } + + return (,$returnParameterSets) + } + + Function GetModuleInfo + { + param ( + [System.Management.Automation.CommandInfo] $cmdInfo + ) + + if ($cmdInfo.ModuleName -ne $null) + { + $moduleName = $cmdInfo.ModuleName + } + else + { + $moduleName = """" + } + + $moduleInfo = new-object PSObject + $moduleInfo | Add-Member -MemberType NoteProperty -Name ""Name"" -Value $moduleName + + return $moduleInfo + } + + Function ConvertToShowCommandInfo + { + param ( + [System.Management.Automation.CommandInfo] $cmdInfo + ) + + $showCommandInfo = new-object PSObject + $showCommandInfo | Add-Member -MemberType NoteProperty -Name ""Name"" -Value $cmdInfo.Name + $showCommandInfo | Add-Member -MemberType NoteProperty -Name ""ModuleName"" -Value $cmdInfo.ModuleName + $showCommandInfo | Add-Member -MemberType NoteProperty -Name ""Module"" -Value (GetModuleInfo($cmdInfo)) + $showCommandInfo | Add-Member -MemberType NoteProperty -Name ""CommandType"" -Value $cmdInfo.CommandType + $showCommandInfo | Add-Member -MemberType NoteProperty -Name ""Definition"" -Value $cmdInfo.Definition + $showCommandInfo | Add-Member -MemberType NoteProperty -Name ""ParameterSets"" -Value (GetParameterSets($cmdInfo)) + + return $showCommandInfo + } + + $commandList = @(""Cmdlet"", ""Function"", ""Script"", ""ExternalScript"") + if ($PSVersionTable.PSVersion.Major -gt 2) + { + $commandList += ""Workflow"" + } + + foreach ($command in @(Get-Command -CommandType $commandList)) + { + Write-Output (ConvertToShowCommandInfo($command)) + } +}"; + + #endregion + + #region constructor and destructor + /// + /// Prevents a default instance of the ShowCommandHelper class from being created. + /// + private ShowCommandHelper() + { + } + + /// + /// Finalizes an instance of the class. + /// + ~ShowCommandHelper() + { + this.Dispose(false); + } + #endregion constructor and destructor + + #region properties called using reflection + /// + /// Gets the Screen Width. + /// + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called using reflection")] + private static double ScreenWidth + { + get + { + return System.Windows.SystemParameters.PrimaryScreenWidth; + } + } + + /// + /// Gets the Screen Height. + /// + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called using reflection")] + private static double ScreenHeight + { + get + { + return System.Windows.SystemParameters.PrimaryScreenHeight; + } + } + + /// + /// Gets the event set when the show-command window is closed. + /// + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called using reflection")] + private AutoResetEvent WindowClosed + { + get + { + return this.windowClosed; + } + } + + /// + /// Gets the event set when help is needed for a command. + /// + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called using reflection")] + private AutoResetEvent HelpNeeded + { + get + { + return this.helpNeeded; + } + } + + /// + /// Gets the event set when it is necessary to import a module. + /// + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called using reflection")] + private AutoResetEvent ImportModuleNeeded + { + get + { + return this.importModuleNeeded; + } + } + + /// + /// Gets the event set when the window is loaded. + /// + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called using reflection")] + private AutoResetEvent WindowLoaded + { + get + { + return this.windowLoaded; + } + } + + /// + /// Gets the command needing help when HelpNeeded is set. + /// + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called using reflection")] + private string CommandNeedingHelp + { + get + { + return this.commandNeedingHelp; + } + } + + /// + /// Gets the module we want to import. + /// + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called using reflection")] + private string ParentModuleNeedingImportModule + { + get + { + return this.parentModuleNeedingImportModule; + } + } + + /// + /// Gets a value indicating whether there is a host window. + /// + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called using reflection")] + private bool HasHostWindow + { + get + { + return this.hostWindow != null; + } + } + #endregion properties called using reflection + + #region public Dispose + /// + /// Dispose method in IDisposeable. + /// + public void Dispose() + { + this.Dispose(true); + GC.SuppressFinalize(this); + } + #endregion public Dispose + + #region internal static methods called using reflection from show-command + /// + /// Sets the text in the clipboard. + /// + /// Text to set the clipboard to. + internal static void SetClipboardText(string text) + { + try + { + Clipboard.SetText(text); + } + catch (System.Runtime.InteropServices.COMException) + { + // This is the recommended way to set clipboard text + System.Threading.Thread.Sleep(0); + try + { + Clipboard.SetText(text); + } + catch (System.Runtime.InteropServices.COMException) + { + } + } + } + + /// + /// Gets the command to be run to get commands and imported modules. + /// + /// Boolean flag determining whether Show-Command is queried in the local or remote runspace scenario. + /// Boolean flag to indicate that it is the second attempt to query Show-Command data. + /// The command to be run to get commands and imported modules. + internal static string GetShowAllModulesCommand(bool isRemoteRunspace = false, bool isFirstChance = true) + { + string scriptBase; + + if (isRemoteRunspace) + { + if (isFirstChance) + { + // Return command to run. + scriptBase = "@(Get-Command " + ShowCommandHelper.CommandTypeSegment + @" -ShowCommandInfo)"; + } + else + { + // Return script to run. + scriptBase = GetSerializedCommandScript(); + } + } + else + { + scriptBase = "@(Get-Command " + ShowCommandHelper.CommandTypeSegment + ")"; + } + + scriptBase += ShowCommandHelper.GetGetModuleSuffix(); + return scriptBase; + } + + /// + /// Retrieves the script for Get-SerializedCommand from local machine. + /// + /// String representation of the script for Get-SerializedCommand. + private static string GetSerializedCommandScript() + { + return string.Format( + CultureInfo.InvariantCulture, + "@({0};{1};{2})", + ScriptGetSerializedCommand, + @"PSGetSerializedShowCommandInfo", + @"Remove-Item -Path 'function:\PSGetSerializedShowCommandInfo' -Force"); + } + + /// + /// Gets the command to be run in order to show help for a command. + /// + /// Command we want to get help from. + /// The command to be run in order to show help for a command. + internal static string GetHelpCommand(string command) + { + return "Get-Help " + ShowCommandHelper.SingleQuote(command); + } + + /// + /// Constructs a dictionary of imported modules based on the module names. + /// + /// The imported modules. + /// a dictionary of imported modules based on the module names. + internal static Dictionary GetImportedModulesDictionary(object[] moduleObjects) + { + Dictionary returnValue = new Dictionary(StringComparer.OrdinalIgnoreCase); + foreach (PSObject rawModule in moduleObjects) + { + ShowCommandModuleInfo wrappedModule = null; + PSModuleInfo module = rawModule.BaseObject as PSModuleInfo; + if (module != null) + { + wrappedModule = new ShowCommandModuleInfo(module); + } + else + { + wrappedModule = new ShowCommandModuleInfo(rawModule); + } + + // It is probably an issue somewhere else that a module would show up twice in the list, but we want to avoid + // throwing an exception regarding that in returnValue.Add + if (!returnValue.ContainsKey(wrappedModule.Name)) + { + returnValue.Add(wrappedModule.Name, wrappedModule); + } + } + + return returnValue; + } + + /// + /// Constructs a list of commands out of . + /// + /// The results of a get-command command. + /// a list of commands out of . + internal static List GetCommandList(object[] commandObjects) + { + List returnValue = new List(); + foreach (PSObject rawCommand in commandObjects) + { + CommandInfo command = rawCommand.BaseObject as CommandInfo; + if (command != null) + { + returnValue.Add(new ShowCommandCommandInfo(command)); + } + else + { + PSObject obj = rawCommand as PSObject; + if (obj != null) + { + returnValue.Add(new ShowCommandCommandInfo(obj)); + } + } + } + + return returnValue; + } + + /// + /// Constructs an array of objects out of . + /// + /// The result of a get-command command. + /// An array of objects out of . + internal static object[] ObjectArrayFromObjectCollection(object commandObjects) + { + object[] objectArray = commandObjects as object[] ?? ((System.Collections.ArrayList)commandObjects).ToArray(); + + return objectArray; + } + + /// + /// Called after a module in is imported to refresh the view model. + /// Gets a new AllModulesViewModel populated with and . + /// The is used to cleanup event listening in the old view model and to copy NoCommonParameters. + /// The new ViewModel will have the command selected according to , + /// and . + /// + /// The viewModel before the module was imported. + /// The list of imported modules. + /// The list of commands. + /// The name of the module that was selected in . + /// The name of the module that was imported. + /// The name of the command that was selected in . + /// The new ViewModel based on and . + internal static AllModulesViewModel GetNewAllModulesViewModel(AllModulesViewModel oldViewModel, Dictionary importedModules, IEnumerable commands, string selectedModuleNeedingImportModule, string parentModuleNeedingImportModule, string commandNeedingImportModule) + { + string oldFilter = null; + + if (oldViewModel.SelectedModule != null) + { + // this will allow the old view model to stop listening for events before we + // replace it with a new view model + oldViewModel.SelectedModule.SelectedCommand = null; + oldViewModel.SelectedModule = null; + oldFilter = oldViewModel.CommandNameFilter; + } + + AllModulesViewModel returnValue = new AllModulesViewModel(importedModules, commands, oldViewModel.NoCommonParameter); + if (!string.IsNullOrEmpty(oldFilter)) + { + returnValue.CommandNameFilter = oldFilter; + } + + if (selectedModuleNeedingImportModule == null || parentModuleNeedingImportModule == null) + { + return returnValue; + } + + ModuleViewModel moduleToSelect = returnValue.Modules.Find( + new Predicate((module) => + { + return module.Name.Equals(selectedModuleNeedingImportModule, StringComparison.OrdinalIgnoreCase) ? true : false; + })); + + if (moduleToSelect == null) + { + return returnValue; + } + + returnValue.SelectedModule = moduleToSelect; + + CommandViewModel commandToSelect = moduleToSelect.Commands.Find( + new Predicate((command) => + { + return command.ModuleName.Equals(parentModuleNeedingImportModule, StringComparison.OrdinalIgnoreCase) && + command.Name.Equals(commandNeedingImportModule, StringComparison.OrdinalIgnoreCase) ? true : false; + })); + + if (commandToSelect == null) + { + return returnValue; + } + + moduleToSelect.SelectedCommand = commandToSelect; + return returnValue; + } + + /// + /// Gets an error message to be displayed when failed to import a module. + /// + /// Command belonging to the module to import. + /// Module to import. + /// Error importing the module. + /// An error message to be displayed when failed to import a module. + internal static string GetImportModuleFailedMessage(string command, string module, string error) + { + return string.Format( + CultureInfo.CurrentUICulture, + ShowCommandResources.ImportModuleFailedFormat, + command, + module, + error); + } + + /// + /// Single quotes . + /// + /// String to quote. + /// single quoted. + internal static string SingleQuote(string str) + { + if (str == null) + { + str = string.Empty; + } + + return "\'" + System.Management.Automation.Language.CodeGeneration.EscapeSingleQuotedStringContent(str) + "\'"; + } + #endregion internal static methods called using reflection from show-command + + #region internal static methods used internally in this assembly + /// + /// Gets the host window, if it is present or null if it is not. + /// + /// Cmdlet calling this method. + /// The host window, if it is present or null if it is not. + internal static Window GetHostWindow(PSCmdlet cmdlet) + { + PSPropertyInfo windowProperty = cmdlet.Host.PrivateData.Properties["Window"]; + if (windowProperty == null) + { + return null; + } + + try + { + return windowProperty.Value as Window; + } + catch (ExtendedTypeSystemException) + { + return null; + } + } + #endregion internal static methods used internally in this assembly + + #region static private methods used only on this file + + /// + /// Gets a property value using reflection. + /// + /// Type containing the property. + /// Object containing the property (null for a static property). + /// Name of property to get. + /// Flags passed to reflection. + /// + /// Property value or null if it was not able to retrieve it. This method is not suitable to return a property value that might be null. + /// + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called from a method called using reflection")] + private static object GetPropertyValue(Type type, object obj, string propertyName, BindingFlags bindingFlags) + { + PropertyInfo property = type.GetProperty(propertyName, bindingFlags); + if (property == null) + { + return null; + } + + try + { + return property.GetValue(obj, Array.Empty()); + } + catch (ArgumentException) + { + return null; + } + catch (TargetException) + { + return null; + } + catch (TargetParameterCountException) + { + return null; + } + catch (MethodAccessException) + { + return null; + } + catch (TargetInvocationException) + { + return null; + } + } + + /// + /// Sets a property value using reflection. + /// + /// Type containing the property. + /// Object containing the property (null for a static property). + /// Name of property to set. + /// Value to set the property with. + /// Flags passed to reflection. + /// True if it was able to set. + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called from a method called using reflection")] + private static bool SetPropertyValue(Type type, object obj, string propertyName, object value, BindingFlags bindingFlags) + { + PropertyInfo property = type.GetProperty(propertyName, bindingFlags); + if (property == null) + { + return false; + } + + try + { + property.SetValue(obj, value, Array.Empty()); + } + catch (ArgumentException) + { + return false; + } + catch (TargetException) + { + return false; + } + catch (TargetParameterCountException) + { + return false; + } + catch (MethodAccessException) + { + return false; + } + catch (TargetInvocationException) + { + return false; + } + + return true; + } + + /// + /// Gets the suffix that adds imported modules to a command. + /// + /// The suffix that adds imported modules to a command. + private static string GetGetModuleSuffix() + { + return ",@(get-module)"; + } + + #endregion static private methods used only on this file + + #region private methods called using reflection from show-command + /// + /// Gets the command to be run when calling show-command for a particular command. + /// + /// The particular command we are running show-command on. + /// True if we want to include aliases and retrieve modules. + /// The command to be run when calling show-command for a particular command. + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called using reflection")] + private static string GetShowCommandCommand(string commandName, bool includeAliasAndModules) + { + string quotedCommandName = ShowCommandHelper.SingleQuote(commandName); + return "@(get-command " + quotedCommandName + " " + ShowCommandHelper.CommandTypeSegment + + (includeAliasAndModules ? ",Alias" : string.Empty) + ")" + + (includeAliasAndModules ? ShowCommandHelper.GetGetModuleSuffix() : string.Empty); + } + + /// + /// Gets a CommandViewModel of a CommandInfo. + /// + /// Command we want to get a CommandViewModel of. + /// True if we do not want common parameters. + /// The loaded modules. + /// True to qualify command with module name in GetScript. + /// A CommandViewModel of a CommandInfo. + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called using reflection")] + private static object GetCommandViewModel(ShowCommandCommandInfo command, bool noCommonParameter, Dictionary importedModules, bool moduleQualify) + { + CommandViewModel returnValue = CommandViewModel.GetCommandViewModel(new ModuleViewModel(command.ModuleName, importedModules), command, noCommonParameter); + returnValue.ModuleQualifyCommandName = moduleQualify; + return returnValue; + } + + /// + /// Dispatches a message to the window for it to activate. + /// + /// Window to be activated. + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called from ActivateWindow() which is called using reflection")] + private static void ActivateWindow(Window window) + { + window.Dispatcher.Invoke( + new SendOrPostCallback( + (_) => window.Activate()), + string.Empty); + } + + /// + /// Shows the window listing cmdlets. + /// + /// Cmdlet calling this method. + /// All loaded modules. + /// Commands to be listed. + /// True if we should not show common parameters. + /// Window width. + /// Window height. + /// True if the GUI should mention ok instead of run. + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called using reflection")] + private void ShowAllModulesWindow(PSCmdlet cmdlet, Dictionary importedModules, IEnumerable commands, bool noCommonParameter, double windowWidth, double windowHeight, bool passThrough) + { + this.methodThatReturnsDialog = new DispatcherOperationCallback((object ignored) => + { + ShowAllModulesWindow allModulesWindow = new ShowAllModulesWindow(); + this.allModulesViewModel = new AllModulesViewModel(importedModules, commands, noCommonParameter); + + this.SetupButtonEvents(allModulesWindow.Run, allModulesWindow.Copy, allModulesWindow.Cancel, passThrough); + this.SetupWindow(allModulesWindow); + this.SetupViewModel(); + CommonHelper.SetStartingPositionAndSize( + allModulesWindow, + ShowCommandSettings.Default.ShowCommandsTop, + ShowCommandSettings.Default.ShowCommandsLeft, + windowWidth != 0.0 && windowWidth > allModulesWindow.MinWidth ? windowWidth : ShowCommandSettings.Default.ShowCommandsWidth, + windowHeight != 0.0 && windowHeight > allModulesWindow.MinHeight ? windowHeight : ShowCommandSettings.Default.ShowCommandsHeight, + allModulesWindow.Width, + allModulesWindow.Height, + ShowCommandSettings.Default.ShowCommandsWindowMaximized); + + return allModulesWindow; + }); + + this.CallShowDialog(cmdlet); + } + + /// + /// Calls ShowsDialog on methodThatReturnsDialog either in a separate thread or dispatched + /// to the hostWindow thread if there is a hostWindow + /// + /// Cmdlet used to retrieve the host window. + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called from a method called using reflection")] + private void CallShowDialog(PSCmdlet cmdlet) + { + this.hostWindow = ShowCommandHelper.GetHostWindow(cmdlet); + if (this.hostWindow == null) + { + Thread guiThread = new Thread(new ThreadStart(this.PlainInvokeAndShowDialog)); + guiThread.SetApartmentState(ApartmentState.STA); + guiThread.Start(); + return; + } + + this.hostWindow.Dispatcher.Invoke( + new SendOrPostCallback( + (_) => + { + Window childWindow = (Window)this.methodThatReturnsDialog.Invoke(null); + childWindow.Owner = this.hostWindow; + childWindow.Show(); + }), + string.Empty); + } + + /// + /// Called from CallMethodThatShowsDialog as the thtead start when there is no host window. + /// + private void PlainInvokeAndShowDialog() + { + ((Window)this.methodThatReturnsDialog.Invoke(null)).ShowDialog(); + } + + /// + /// Shows the window for the cmdlet. + /// + /// Cmdlet calling this method. + /// Command to show in the window. + /// Window width. + /// Window height. + /// True if the GUI should mention ok instead of run. + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called using reflection")] + private void ShowCommandWindow(PSCmdlet cmdlet, object commandViewModelObj, double windowWidth, double windowHeight, bool passThrough) + { + this.methodThatReturnsDialog = new DispatcherOperationCallback((object ignored) => + { + this.commandViewModel = (CommandViewModel)commandViewModelObj; + ShowCommandWindow showCommandWindow = new ShowCommandWindow(); + + this.commandViewModel.HelpNeeded += this.CommandNeedsHelp; + showCommandWindow.DataContext = this.commandViewModel; + + this.SetupButtonEvents(showCommandWindow.Run, showCommandWindow.Copy, showCommandWindow.Cancel, passThrough); + this.SetupWindow(showCommandWindow); + + CommonHelper.SetStartingPositionAndSize( + showCommandWindow, + ShowCommandSettings.Default.ShowOneCommandTop, + ShowCommandSettings.Default.ShowOneCommandLeft, + windowWidth != 0.0 && windowWidth > showCommandWindow.MinWidth ? windowWidth : ShowCommandSettings.Default.ShowOneCommandWidth, + windowHeight != 0.0 && windowHeight > showCommandWindow.MinHeight ? windowHeight : ShowCommandSettings.Default.ShowOneCommandHeight, + showCommandWindow.Width, + showCommandWindow.Height, + ShowCommandSettings.Default.ShowOneCommandWindowMaximized); + + return showCommandWindow; + }); + + this.CallShowDialog(cmdlet); + } + + /// + /// Called when the module importation is done. + /// + /// All modules currently imported. + /// Commands to be displayed. + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called using reflection")] + private void ImportModuleDone(Dictionary importedModules, IEnumerable commands) + { + this.allModulesViewModel.WaitMessageDisplayed = false; + if (this.window != null) + { + this.window.Dispatcher.Invoke( + new SendOrPostCallback( + delegate(object ignored) + { + this.allModulesViewModel = ShowCommandHelper.GetNewAllModulesViewModel( + this.allModulesViewModel, + importedModules, + commands, + this.selectedModuleNeedingImportModule, + this.parentModuleNeedingImportModule, + this.commandNeedingImportModule); + this.SetupViewModel(); + }), + string.Empty); + } + } + + /// + /// Called when the module importation has failed. + /// + /// Reason why the module importation failed. + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called using reflection")] + private void ImportModuleFailed(Exception reason) + { + this.allModulesViewModel.WaitMessageDisplayed = false; + if (this.window != null) + { + this.window.Dispatcher.Invoke( + new SendOrPostCallback( + (_) => + { + string message = ShowCommandHelper.GetImportModuleFailedMessage( + this.commandNeedingImportModule, + this.parentModuleNeedingImportModule, + reason.Message); + MessageBox.Show(this.window, message, ShowCommandResources.ShowCommandError, MessageBoxButton.OK, MessageBoxImage.Error); + }), + string.Empty); + } + } + + /// + /// Called when the results or get-help are ready in order to display the help window for a command. + /// + /// Results of a get-help call. + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called using reflection")] + private void DisplayHelp(Collection getHelpResults) + { + if (this.window != null && getHelpResults != null && getHelpResults.Count > 0) + { + this.window.Dispatcher.Invoke( + new SendOrPostCallback( + delegate(object ignored) + { + HelpWindow help = new HelpWindow(getHelpResults[0]); + help.Owner = this.window; + help.Show(); + }), + string.Empty); + } + } + + /// + /// Activates this.window. + /// + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called using reflection")] + private void ActivateWindow() + { + if (this.window != null) + { + ShowCommandHelper.ActivateWindow(this.window); + } + } + + /// + /// Returns the script to execute if dialog has not been canceled. + /// + /// The script to execute if dialog has not been canceled. + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called using reflection")] + private string GetScript() + { + if (this.dialogCanceled) + { + return null; + } + + return this.InternalGetScript(); + } + #endregion private methods called using reflection from show-command + + #region instance private methods used only on this file + /// + /// Sets up window settings common between the two flavors of show-command. + /// + /// The window being displayed. + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called from ShowAllModulesWindow and ShowCommandWindow which are called with reflection")] + private void SetupWindow(Window commandWindow) + { + this.window = commandWindow; + this.window.Closed += this.Window_Closed; + this.window.Loaded += this.Window_Loaded; + } + + /// + /// Handles the SelectedCommandInSelectedModuleNeedsImportModule event. + /// + /// Event sender. + /// Event arguments. + private void CommandNeedsImportModule(object sender, ImportModuleEventArgs e) + { + this.commandNeedingImportModule = e.CommandName; + this.parentModuleNeedingImportModule = e.ParentModuleName; + this.selectedModuleNeedingImportModule = e.SelectedModuleName; + this.allModulesViewModel.WaitMessageDisplayed = true; + this.ImportModuleNeeded.Set(); + } + + /// + /// Handles the SelectedCommandInSelectedModuleNeedsHelp event. + /// + /// Event sender. + /// Event arguments. + private void CommandNeedsHelp(object sender, HelpNeededEventArgs e) + { + this.commandNeedingHelp = e.CommandName; + this.HelpNeeded.Set(); + } + + /// + /// Called when the window is closed to set this.dialogCanceled. + /// + /// Event sender. + /// Event arguments. + private void Window_Closed(object sender, EventArgs e) + { + if (this.hostWindow != null) + { + this.hostWindow.Focus(); + } + + this.window = null; + this.windowClosed.Set(); + } + + /// + /// Called when the window is loaded to set this.Window_Loaded. + /// + /// Event sender. + /// Event arguments. + private void Window_Loaded(object sender, RoutedEventArgs e) + { + this.window.Loaded -= this.Window_Loaded; + this.windowLoaded.Set(); + } + + /// + /// Sets up event listening on the buttons. + /// + /// Button to run command. + /// Button to copy command code. + /// Button to close window. + /// True to change the text of Run to OK. + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", Justification = "Called from methods called using reflection")] + private void SetupButtonEvents(Button run, Button copy, Button cancel, bool passThrough) + { + if (passThrough) + { + run.Content = ShowCommandResources.ActionButtons_Button_Ok; + } + + run.Click += this.Buttons_RunClick; + copy.Click += this.Buttons_CopyClick; + cancel.Click += this.Buttons_CancelClick; + } + + /// + /// Sets up event listening for a new viewModel. + /// + private void SetupViewModel() + { + this.allModulesViewModel.SelectedCommandInSelectedModuleNeedsHelp += this.CommandNeedsHelp; + this.allModulesViewModel.SelectedCommandInSelectedModuleNeedsImportModule += this.CommandNeedsImportModule; + this.window.DataContext = this.allModulesViewModel; + } + + /// + /// Copies the script into the clipboard. + /// + /// Event sender. + /// Event arguments. + private void Buttons_CopyClick(object sender, RoutedEventArgs e) + { + string script = this.InternalGetScript(); + if (script == null) + { + return; + } + + this.window.Dispatcher.Invoke(new ThreadStart(delegate { ShowCommandHelper.SetClipboardText(script); })); + } + + /// + /// Sets a successful dialog result and then closes the window. + /// + /// Event sender. + /// Event arguments. + private void Buttons_RunClick(object sender, RoutedEventArgs e) + { + this.dialogCanceled = false; + this.CloseWindow(); + } + + /// + /// Closes the window. + /// + /// Event sender. + /// Event arguments. + private void Buttons_CancelClick(object sender, RoutedEventArgs e) + { + this.CloseWindow(); + } + + /// + /// Closes the window. + /// + private void CloseWindow() + { + if (this.window == null) + { + return; + } + + this.window.Dispatcher.Invoke(new ThreadStart(delegate + { + // This can happen if ISE is closed while show-command is up + if (this.window != null) + { + this.window.Close(); + } + })); + } + + /// + /// Showing a MessageBox when user type a invalidate command name. + /// + /// Error message. + private void ShowErrorString(string errorString) + { + if (errorString != null && errorString.Trim().Length > 0) + { + MessageBox.Show( + string.Format( + CultureInfo.CurrentUICulture, + ShowCommandResources.EndProcessingErrorMessage, + errorString), + "Show-Command", + MessageBoxButton.OK, + MessageBoxImage.Error); + } + } + + /// + /// Returns the script to execute. + /// + /// The script to execute. + private string InternalGetScript() + { + if (this.allModulesViewModel != null) + { + return this.allModulesViewModel.GetScript(); + } + + if (this.commandViewModel == null) + { + return null; + } + + return this.commandViewModel.GetScript(); + } + + /// + /// Implements IDisposable logic. + /// + /// True if being called from Dispose. + private void Dispose(bool isDisposing) + { + if (isDisposing) + { + this.windowClosed.Dispose(); + this.helpNeeded.Dispose(); + this.windowLoaded.Dispose(); + this.importModuleNeeded.Dispose(); + } + } + #endregion instance private methods used only on this file + } +} diff --git a/src/Microsoft.Management.UI.Internal/resources/Graphics/Add16.png b/src/Microsoft.Management.UI.Internal/resources/Graphics/Add16.png new file mode 100644 index 00000000000..e5b964d9199 Binary files /dev/null and b/src/Microsoft.Management.UI.Internal/resources/Graphics/Add16.png differ diff --git a/src/Microsoft.Management.UI.Internal/resources/Graphics/CloseTile16.png b/src/Microsoft.Management.UI.Internal/resources/Graphics/CloseTile16.png new file mode 100644 index 00000000000..bef254260eb Binary files /dev/null and b/src/Microsoft.Management.UI.Internal/resources/Graphics/CloseTile16.png differ diff --git a/src/Microsoft.Management.UI.Internal/resources/Graphics/Delete16.png b/src/Microsoft.Management.UI.Internal/resources/Graphics/Delete16.png new file mode 100644 index 00000000000..1faef5d45ca Binary files /dev/null and b/src/Microsoft.Management.UI.Internal/resources/Graphics/Delete16.png differ diff --git a/src/Microsoft.Management.UI.Internal/resources/Graphics/Error16.png b/src/Microsoft.Management.UI.Internal/resources/Graphics/Error16.png new file mode 100644 index 00000000000..8fce7ba03df Binary files /dev/null and b/src/Microsoft.Management.UI.Internal/resources/Graphics/Error16.png differ diff --git a/src/Microsoft.Management.UI.Internal/resources/Graphics/Help.ico b/src/Microsoft.Management.UI.Internal/resources/Graphics/Help.ico new file mode 100644 index 00000000000..f6b92d0b8c2 Binary files /dev/null and b/src/Microsoft.Management.UI.Internal/resources/Graphics/Help.ico differ diff --git a/src/Microsoft.Management.UI.Internal/resources/Graphics/Rename16.png b/src/Microsoft.Management.UI.Internal/resources/Graphics/Rename16.png new file mode 100644 index 00000000000..fa59ea35618 Binary files /dev/null and b/src/Microsoft.Management.UI.Internal/resources/Graphics/Rename16.png differ diff --git a/src/Microsoft.Management.UI.Internal/resources/Graphics/Save16.png b/src/Microsoft.Management.UI.Internal/resources/Graphics/Save16.png new file mode 100644 index 00000000000..66893c82d66 Binary files /dev/null and b/src/Microsoft.Management.UI.Internal/resources/Graphics/Save16.png differ diff --git a/src/Microsoft.Management.UI.Internal/resources/Graphics/SortAdornerAscending.png b/src/Microsoft.Management.UI.Internal/resources/Graphics/SortAdornerAscending.png new file mode 100644 index 00000000000..a58856e553c Binary files /dev/null and b/src/Microsoft.Management.UI.Internal/resources/Graphics/SortAdornerAscending.png differ diff --git a/src/Microsoft.Management.UI.Internal/resources/Graphics/SortAdornerDescending.png b/src/Microsoft.Management.UI.Internal/resources/Graphics/SortAdornerDescending.png new file mode 100644 index 00000000000..d95bc066e5b Binary files /dev/null and b/src/Microsoft.Management.UI.Internal/resources/Graphics/SortAdornerDescending.png differ diff --git a/src/Microsoft.Management.UI.Internal/resources/Graphics/SpyGlass10.png b/src/Microsoft.Management.UI.Internal/resources/Graphics/SpyGlass10.png new file mode 100644 index 00000000000..71abc2ecf32 Binary files /dev/null and b/src/Microsoft.Management.UI.Internal/resources/Graphics/SpyGlass10.png differ diff --git a/src/Microsoft.Management.UI.Internal/resources/Graphics/SpyGlass16.png b/src/Microsoft.Management.UI.Internal/resources/Graphics/SpyGlass16.png new file mode 100644 index 00000000000..37cf86490d4 Binary files /dev/null and b/src/Microsoft.Management.UI.Internal/resources/Graphics/SpyGlass16.png differ diff --git a/src/Microsoft.Management.UI.Internal/resources/Graphics/down.ico b/src/Microsoft.Management.UI.Internal/resources/Graphics/down.ico new file mode 100644 index 00000000000..98e0339426a Binary files /dev/null and b/src/Microsoft.Management.UI.Internal/resources/Graphics/down.ico differ diff --git a/src/Microsoft.Management.UI.Internal/resources/public.GraphicalHostResources.resx b/src/Microsoft.Management.UI.Internal/resources/public.GraphicalHostResources.resx new file mode 100644 index 00000000000..0a86d51a82e --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/resources/public.GraphicalHostResources.resx @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + OutGridViewWindow Object + + diff --git a/src/Microsoft.Management.UI.Internal/resources/public.HelpWindowResources.resx b/src/Microsoft.Management.UI.Internal/resources/public.HelpWindowResources.resx new file mode 100644 index 00000000000..3bcab7bfb8a --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/resources/public.HelpWindowResources.resx @@ -0,0 +1,230 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Cancel + + + Match Case + + + CommonParameters + Name of a group of parameters common to all cmdlets + + + Description + + + Examples + + + _Find: + + + Help Sections + + + {0} Help + + + Inputs + + + {0} {1} + + + Methods + + + _Next + + + No matches found + + + Notes + + + OK + + + 1 match + + + Outputs + + + Accept wildcard characters? + + + Default value + + + Accept pipeline input? + + + Position? + + + Required? + + + Parameters + + + _Previous + + + Properties + + + RelatedLinks + + + Remarks + + + Search Options + + + Settings + + + {0} matches + + + Synopsis + + + Syntax + + + Help for {0} + {0} is the name of a cmdlet + + + Whole Word + + + {0}% + + + Zoom + + diff --git a/src/Microsoft.Management.UI.Internal/resources/public.InvariantResources.resx b/src/Microsoft.Management.UI.Internal/resources/public.InvariantResources.resx new file mode 100644 index 00000000000..62924692a43 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/resources/public.InvariantResources.resx @@ -0,0 +1,148 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + {0} cannot be modified directly, use {1} instead. + + + Columns + + + {0:G} + The format string that is used by the InnerList in the case where a DateTime type is used. The {0} will be the column value. + + + {0} + The format string that is used by the InnerList in the default case. The {0} will be the column value. + + + {0:N} + The format string that is used by the InnerList in the case where a floating point number is used. The {0} will be the column value. + + + {0:N0} + The format string that is used by the InnerList in the case where a whole number type is used. The {0} will be the column value. + + + {0} does not support adding to the Items collection, use {1} instead. + + + If View is set to a {0}, it should have the type {1}. + + diff --git a/src/Microsoft.Management.UI.Internal/resources/public.ShowCommandResources.resx b/src/Microsoft.Management.UI.Internal/resources/public.ShowCommandResources.resx new file mode 100644 index 00000000000..5853a0a3c01 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/resources/public.ShowCommandResources.resx @@ -0,0 +1,239 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Cancel + + + Co_py + + + _Run + + + All + + + Modules: + + + ? + + + Help + + + Common Parameters + + + Errors + + + Parameters for "{0}": + + + Name: {0} +Module: {1} ({2}) + + + The following errors occurred running the command: +{0} + + + * + Used in MandatoryNameLabelFormat to designate a mandatory parameter with a * + + + {0}:{1} + This is a label for a control, hence the colon. {0} is a parameter name, {1} is MandatoryLabelSegment or an empty string + + + {0}: + This is a label for a control, hence the colon. {0} is a parameter name + + + Name: + + + OK + + + ... + + + Command Name + + + Modules + + + <No module name> + + + Select multiple values for "{0}" + + + Can receive value from pipeline + + + Common to all parameter sets + + + Mandatory + + + Optional + + + Position: {0} + + + Type: {0} + + + Imported + + + Not Imported + + + Show Details + + + Failed to import the module required by command "{0}". Module name: "{1}". Error message: "{2}". + + + To import the "{0}" module and its cmdlets, including "{1}", click {2}. + + + Show Command - Error + + + Please Wait... + + + Refresh + + + There are no parameters. + + + Click after using "{0}" to see the new commands + + diff --git a/src/Microsoft.Management.UI.Internal/resources/public.UICultureResources.resx b/src/Microsoft.Management.UI.Internal/resources/public.UICultureResources.resx new file mode 100644 index 00000000000..7f5730b3f86 --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/resources/public.UICultureResources.resx @@ -0,0 +1,230 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Select Columns... + + + (none) + The group title for items within a column whose value is empty/null. + + + Value should be of Type {0}. + This text represents the error which will be shown when the entered type does not match the type we are expecting. {0} is the expected type. + + + The current selection is empty. + The error validation string to present to the user when they have selected a value out of bounds. + + + contains + A filter rule that indicates a field must contain the specified value. + + + does not contain + A filter rule that indicates a field must not contain the specified value. + + + does not equal + A filter rule that indicates a field must not equal the specified value. + + + equals + A filter rule that indicates a field must equal the specified value. + + + is greater than or equal to + A filter rule that indicates a field must be greater than or equal to the specified value. + + + is between + A filter rule that indicates a field must be between the specified values. + + + is empty + A filter rule that indicates a field must be empty. + + + is not empty + A filter rule that indicates a field must not be empty. + + + is less than or equal to + A filter rule that indicates a field must be less than or equal to the specified value. + + + ends with + A filter rule that indicates a field must end with the specified value. + + + starts with + A filter rule that indicates a field must start with the specified value. + + + Back + The text representing the tool tip and help text for the Back Button in the Back Forward History control when the button is disabled + + + Forward + The text representing the tool tip and help text for the Forward Button in the Back Forward History control when the button is disabled + + + The value must be a valid date in the following format: {0}. + {0} will be filled in with the culture appropriate ShortDatePattern + + + The value must be a valid number. + + + Search + The default background text of the search box. + + + LeftToRight + This value will be loaded at runtime to define the flow direction of WPF application. This value should be set to "RightToLeft" for mirrored language and "LeftToRight" for others. + + + + An ellipsis character. + + + Ctrl+Add + + + Ctrl+Shift+Add + + + Ctrl+Plus + + + Ctrl+Shift+Plus + + + Ctrl+Subtract + + + Ctrl+Shift+Subtract + + + Ctrl+Minus + + + Ctrl+Shift+Minus + + diff --git a/src/Microsoft.Management.UI.Internal/resources/public.XamlLocalizableResources.resx b/src/Microsoft.Management.UI.Internal/resources/public.XamlLocalizableResources.resx new file mode 100644 index 00000000000..caff99962fd --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/resources/public.XamlLocalizableResources.resx @@ -0,0 +1,414 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Available Columns + + + Add + + + Remove + + + Selected Columns + + + Back + Localizable AutomationName for control that is used by accessibility screen readers. + + + Forward + Localizable AutomationName for control that is used by accessibility screen readers. + + + Find in this column + Background text shown in the search box. + + + Expand + + + Name + + + New Query + + + Tasks + This is the title string for the Task Pane. + + + Tasks + AutomationProperties.Name of a SeparatedList. + + + Indeterminate Progress Icon + + + Add criteria + + + Overwrite the existing query or type a different name to save a new query. Each query consists of criteria, sorting, and column customizations. + + + Ok + + + Cancel + + + Click to save a search query + + + Available columns + + + >> + The contents of a button which indicates that items will move from the left column to right column + + + << + The contents of a button which indicates that items will move from the right column to left column + + + Move up + + + Move down + + + OK + + + Cancel + + + Select columns + + + Move selected column to list of visible columns + + + Move selected column to list of hidden columns + + + This column may not be removed. + + + The list must always display at least one column. + + + Selected columns + + + Find in this column + + + Expand + + + Click to clear all filter criteria. + + + Click to add search criteria. + + + Click to expand search criteria. + + + There are currently no saved queries. + + + Queries + + + Delete + + + Rename + + + {0} rule + The text representation of a rule in the filter panel, displayed to accessibility clients. {0} will be the name of the rule. + + + Add + + + Cancel + + + Add Filter Criteria + + + Value + The name for text input fields + + + <Empty> + + + Rules + The name of the panel which contains the filter rules + + + Delete + + + Query + + + Queries + + + Search + + + ({0} of {1}) + The text displayed in the management list title when the list has a filter applied. {0} will be the number of items shown in the list. {1} will be the total number of items in the list before filtering. + + + Searching... + The text displayed in the management list title when the list is processing a filter. + + + ({0}) + The text displayed in the management list title when the list does not have a filter applied. {0} will be the number of items shown in the list. + + + Filter + Localizable AutomationName for control that is used by accessibility screen readers. + + + Filter + + + Shortcut Rules + The name used to indicate custom filter rules which are specific to a particular application. + + + Columns Rules + The name used to indicate filter rules that are based upon the properties of the items in the list. + + + Sort Glyph + + + Sort Glyph + + + Collapse + + + Collapse + + + Sorted ascending + The text used for the accessible ItemStatus property when a column is sorted ascending. + + + Sorted descending + The text used for the accessible ItemStatus property when a column is sorted descending. + + + Collapse + + + Expand + + + Search + + + Cancel + + + Clear All + + + Clear All + + + Clear Search Text + + + Tasks + + + Search + The accessible name of the Search button in the filter panel. + + + Cancel + The accessible name of the Stop Search button in the filter panel. + + + Expand or Collapse Filter Panel + The accessible name of the button that expands/collapses the filter panel. + + + Filter + The background text of the list's search box when filtering is immediate. + + + Click to display saved search queries. + + + Filter applied. + + + and + The first header operator indicates that it is the first item in the list of filter rules. The AND value is used to indicate that it is and'ed with the above SearchBox. + + + and + The header operator indicates that it is the first item in a group of filter rules which are the same. The AND value is used to indicate that it is and'ed with the other groups in the panel. + + + or + The Item operator indicates that it is NOT the first item in a group of filter rules which are the same. The OR value is used to indicate that it is or'ed with the other items in the same group. + + + No matches found. + The text displayed in the ManagementList when the filter has been applied but matching items were found. + + + Collapse + + + Expand + + + Show Children + + + Show Children + + + {0}: {1} + The format string used for the ManagementList title when query has been applied. For example, "Users: My Fancy Query" + + + Cancel + + + OK + + diff --git a/src/Microsoft.Management.UI.Internal/themes/generic.xaml b/src/Microsoft.Management.UI.Internal/themes/generic.xaml new file mode 100644 index 00000000000..36d2354e1af --- /dev/null +++ b/src/Microsoft.Management.UI.Internal/themes/generic.xaml @@ -0,0 +1,2790 @@ + + + + + M 0,0 L 4,3.5 L 0,7 Z + + M7.3391155,8.3084572 L7.3391708,16.109179 10.974259,12.431834 z + + + + + + + + + + + + + + + + + + M0.83647823,8.3277649 L5.9975096,11.519699 11.198949,8.3030383 + + M205.63696,8.9046901 L201.73368,12.456607 205.68294,16.093484 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Microsoft.PowerShell.Commands.Diagnostics/CommonUtils.cs b/src/Microsoft.PowerShell.Commands.Diagnostics/CommonUtils.cs index 182cf59d994..dfe046ec8ca 100644 --- a/src/Microsoft.PowerShell.Commands.Diagnostics/CommonUtils.cs +++ b/src/Microsoft.PowerShell.Commands.Diagnostics/CommonUtils.cs @@ -1,156 +1,100 @@ -using System; -using System.Collections; -using System.Diagnostics; -using System.Globalization; -using System.Runtime.InteropServices; -using System.Text; -using System.Resources; -using System.Reflection; - -#if CORECLR -using System.ComponentModel; -#else -using System.Threading; -#endif - -namespace Microsoft.PowerShell.Commands.Diagnostics.Common -{ - internal static class CommonUtilities - { - // - // StringArrayToString helper converts a string array into a comma-separated string. - // Note this has only limited use, individual strings cannot have commas. - // - public static string StringArrayToString(IEnumerable input) - { - string ret = ""; - foreach (string element in input) - { - ret += element + ", "; - } - - ret = ret.TrimEnd(); - ret = ret.TrimEnd(','); - - return ret; - } - -#if CORECLR - private const string LibraryLoadDllName = "api-ms-win-core-libraryloader-l1-2-0.dll"; - private const string LocalizationDllName = "api-ms-win-core-localization-l1-2-1.dll"; - private const string SysInfoDllName = "api-ms-win-core-sysinfo-l1-2-1.dll"; - - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] - internal struct OSVERSIONINFOEX - { - public int OSVersionInfoSize; - public int MajorVersion; - public int MinorVersion; - public int BuildNumber; - public int PlatformId; - [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 128)] - public string CSDVersion; - public ushort ServicePackMajor; - public ushort ServicePackMinor; - public short SuiteMask; - public byte ProductType; - public byte Reserved; - } - - [DllImport(SysInfoDllName, CharSet = CharSet.Unicode, SetLastError = true)] - internal static extern bool GetVersionEx(ref OSVERSIONINFOEX osVerEx); -#else - private const string LibraryLoadDllName = "kernel32.dll"; - private const string LocalizationDllName = "kernel32.dll"; -#endif - - private const uint FORMAT_MESSAGE_ALLOCATE_BUFFER = 0x00000100; - private const uint FORMAT_MESSAGE_IGNORE_INSERTS = 0x00000200; - private const uint FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000; - private const uint LOAD_LIBRARY_AS_DATAFILE = 0x00000002; - private const uint FORMAT_MESSAGE_FROM_HMODULE = 0x00000800; - - [DllImport(LocalizationDllName, SetLastError = true, CharSet = CharSet.Unicode)] - private static extern uint FormatMessage(uint dwFlags, IntPtr lpSource, - uint dwMessageId, uint dwLanguageId, - [MarshalAs(UnmanagedType.LPWStr)] - StringBuilder lpBuffer, - uint nSize, IntPtr Arguments); - - [DllImport(LibraryLoadDllName, SetLastError = true, CharSet = CharSet.Unicode)] - private static extern IntPtr LoadLibraryEx( - [MarshalAs(UnmanagedType.LPWStr)] string lpFileName, - IntPtr hFile, - uint dwFlags - ); - - [DllImport(LibraryLoadDllName)] - private static extern bool FreeLibrary(IntPtr hModule); - - - [DllImport(LocalizationDllName, EntryPoint = "GetUserDefaultLangID", CallingConvention = CallingConvention.Winapi, SetLastError = true)] - private static extern ushort GetUserDefaultLangID(); - - - public static uint FormatMessageFromModule(uint lastError, string moduleName, out String msg) - { - Debug.Assert(!string.IsNullOrEmpty(moduleName)); - - uint formatError = 0; - msg = String.Empty; - IntPtr moduleHandle = IntPtr.Zero; - - moduleHandle = LoadLibraryEx(moduleName, IntPtr.Zero, LOAD_LIBRARY_AS_DATAFILE); - if (moduleHandle == IntPtr.Zero) - { - return (uint)Marshal.GetLastWin32Error(); - } - - try - { - uint dwFormatFlags = FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_FROM_HMODULE; - uint LANGID = (uint)GetUserDefaultLangID(); - uint langError = (uint)Marshal.GetLastWin32Error(); - if (langError != 0) - { - LANGID = 0; // neutral - } - - StringBuilder outStringBuilder = new StringBuilder(1024); - uint nChars = FormatMessage(dwFormatFlags, - moduleHandle, - lastError, - LANGID, - outStringBuilder, - (uint)outStringBuilder.Capacity, - IntPtr.Zero); - - if (nChars == 0) - { - formatError = (uint)Marshal.GetLastWin32Error(); - //Console.WriteLine("Win32FormatMessage", String.Format(null, "Error formatting message: {0}", formatError)); - } - else - { - msg = outStringBuilder.ToString(); - if (msg.EndsWith(Environment.NewLine, StringComparison.Ordinal)) - { - msg = msg.Substring(0, msg.Length - 2); - } - } - } - finally - { - FreeLibrary(moduleHandle); - } - return formatError; - } - - public static ResourceManager GetResourceManager() - { - // this naming pattern is dictated by the dotnet cli - return new ResourceManager("Microsoft.PowerShell.Commands.Diagnostics.resources.GetEventResources", typeof(CommonUtilities).GetTypeInfo().Assembly); - } - } -} - +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Diagnostics; +using System.Reflection; +using System.Resources; +using System.Runtime.InteropServices; +using System.Text; + +namespace Microsoft.PowerShell.Commands.Diagnostics.Common +{ + internal static class CommonUtilities + { + private const string LibraryLoadDllName = "api-ms-win-core-libraryloader-l1-2-0.dll"; + private const string LocalizationDllName = "api-ms-win-core-localization-l1-2-1.dll"; + + private const uint FORMAT_MESSAGE_IGNORE_INSERTS = 0x00000200; + private const uint FORMAT_MESSAGE_FROM_SYSTEM = 0x00001000; + private const uint LOAD_LIBRARY_AS_DATAFILE = 0x00000002; + private const uint FORMAT_MESSAGE_FROM_HMODULE = 0x00000800; + + [DllImport(LocalizationDllName, SetLastError = true, CharSet = CharSet.Unicode)] + private static extern uint FormatMessage(uint dwFlags, IntPtr lpSource, + uint dwMessageId, uint dwLanguageId, + [MarshalAs(UnmanagedType.LPWStr)] + StringBuilder lpBuffer, + uint nSize, IntPtr Arguments); + + [DllImport(LibraryLoadDllName, SetLastError = true, CharSet = CharSet.Unicode)] + private static extern IntPtr LoadLibraryEx( + [MarshalAs(UnmanagedType.LPWStr)] string lpFileName, + IntPtr hFile, + uint dwFlags + ); + + [DllImport(LibraryLoadDllName)] + private static extern bool FreeLibrary(IntPtr hModule); + + [DllImport(LocalizationDllName, EntryPoint = "GetUserDefaultLangID", CallingConvention = CallingConvention.Winapi, SetLastError = true)] + private static extern ushort GetUserDefaultLangID(); + + public static uint FormatMessageFromModule(uint lastError, string moduleName, out string msg) + { + Debug.Assert(!string.IsNullOrEmpty(moduleName)); + + uint formatError = 0; + msg = string.Empty; + + IntPtr moduleHandle = LoadLibraryEx(moduleName, IntPtr.Zero, LOAD_LIBRARY_AS_DATAFILE); + if (moduleHandle == IntPtr.Zero) + { + return (uint)Marshal.GetLastWin32Error(); + } + + try + { + uint LANGID = (uint)GetUserDefaultLangID(); + uint langError = (uint)Marshal.GetLastWin32Error(); + if (langError != 0) + { + LANGID = 0; // neutral + } + + StringBuilder outStringBuilder = new(1024); + uint nChars = FormatMessage( + dwFlags: FORMAT_MESSAGE_IGNORE_INSERTS | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_FROM_HMODULE, + lpSource: moduleHandle, + dwMessageId: lastError, + dwLanguageId: LANGID, + lpBuffer: outStringBuilder, + nSize: (uint)outStringBuilder.Capacity, + Arguments: IntPtr.Zero); + + if (nChars == 0) + { + formatError = (uint)Marshal.GetLastWin32Error(); + } + else + { + msg = outStringBuilder.ToString(); + if (msg.EndsWith(Environment.NewLine, StringComparison.Ordinal)) + { + msg = msg.Substring(0, msg.Length - 2); + } + } + } + finally + { + FreeLibrary(moduleHandle); + } + return formatError; + } + + public static ResourceManager GetResourceManager() + { + return new ResourceManager("Microsoft.PowerShell.Commands.Diagnostics.resources.GetEventResources", typeof(CommonUtilities).GetTypeInfo().Assembly); + } + } +} diff --git a/src/Microsoft.PowerShell.Commands.Diagnostics/CoreCLR/Stubs.cs b/src/Microsoft.PowerShell.Commands.Diagnostics/CoreCLR/Stubs.cs deleted file mode 100644 index 88b3a6f134f..00000000000 --- a/src/Microsoft.PowerShell.Commands.Diagnostics/CoreCLR/Stubs.cs +++ /dev/null @@ -1,251 +0,0 @@ - -#if CORECLR -using System.ComponentModel; - -namespace System.Diagnostics -{ - /// - /// Indicates whether the performance counter category can have multiple instances. - /// - /// 1 - public enum PerformanceCounterCategoryType - { - /// - /// The instance functionality for the performance counter category is unknown. - /// - Unknown = -1, - - /// - /// The performance counter category can have only a single instance. - /// - SingleInstance, - - /// - /// The performance counter category can have multiple instances. - /// - MultiInstance - } - - /// - /// Specifies the formula used to calculate the - /// method for a instance. - /// - /// 2 - public enum PerformanceCounterType - { - /// - /// An instantaneous counter that shows the most recently observed value. - /// Used, for example, to maintain a simple count of items or operations. - /// - NumberOfItems32 = 65536, - - /// - /// An instantaneous counter that shows the most recently observed value. - /// Used, for example, to maintain a simple count of a very large number - /// of items or operations. It is the same as NumberOfItems32 except that - /// it uses larger fields to accommodate larger values. - /// - NumberOfItems64 = 65792, - - /// - /// An instantaneous counter that shows the most recently observed value - /// in hexadecimal format. Used, for example, to maintain a simple count - /// of items or operations. - NumberOfItemsHEX32 = 0, - - /// - /// An instantaneous counter that shows the most recently observed value. - /// Used, for example, to maintain a simple count of a very large number - /// of items or operations. It is the same as NumberOfItemsHEX32 except - /// that it uses larger fields to accommodate larger values. - /// - NumberOfItemsHEX64 = 256, - - /// - /// A difference counter that shows the average number of operations completed - /// during each second of the sample interval. Counters of this type measure - /// time in ticks of the system clock. - RateOfCountsPerSecond32 = 272696320, - - /// - /// A difference counter that shows the average number of operations completed - /// during each second of the sample interval. Counters of this type measure - /// time in ticks of the system clock. This counter type is the same as the - /// RateOfCountsPerSecond32 type, but it uses larger fields to accommodate - /// larger values to track a high-volume number of items or operations per - /// second, such as a byte-transmission rate. - /// - RateOfCountsPerSecond64 = 272696576, - - /// - /// An average counter designed to monitor the average length of a queue - /// to a resource over time. It shows the difference between the queue - /// lengths observed during the last two sample intervals divided by the - /// duration of the interval. This type of counter is typically used to - /// track the number of items that are queued or waiting. - /// - CountPerTimeInterval32 = 4523008, - - /// - /// An average counter that monitors the average length of a queue to a - /// resource over time. Counters of this type display the difference - /// between the queue lengths observed during the last two sample intervals, - /// divided by the duration of the interval. This counter type is the same - /// as CountPerTimeInterval32 except that it uses larger fields to - /// accommodate larger values. This type of counter is typically used - /// to track a high-volume or very large number of items that are queued or waiting. - /// - CountPerTimeInterval64 = 4523264, - - /// - /// An instantaneous percentage counter that shows the ratio of a subset - /// to its set as a percentage. For example, it compares the number of bytes - /// in use on a disk to the total number of bytes on the disk. - /// Counters of this type display the current percentage only, not an average - /// over time. - /// - RawFraction = 537003008, - - /// - /// A base counter that stores the denominator of a counter that presents a - /// general arithmetic fraction. Check that this value is greater than zero - /// before using it as the denominator in a RawFraction value calculation. - /// - RawBase = 1073939459, - - /// - /// An average counter that measures the time it takes, on average, to - /// complete a process or operation. Counters of this type display a - /// ratio of the total elapsed time of the sample interval to the number - /// of processes or operations completed during that time. This counter - /// type measures time in ticks of the system clock. - /// - AverageTimer32 = 805438464, - - /// - /// A base counter that is used in the calculation of time or count averages, - /// such as AverageTimer32 and AverageCount64. Stores the denominator for - /// calculating a counter to present "time per operation" or "count per operation". - /// - AverageBase = 1073939458, - - /// - /// An average counter that shows how many items are processed, on average, - /// during an operation. Counters of this type display a ratio of the items - /// processed to the number of operations completed. The ratio is calculated - /// by comparing the number of items processed during the last interval to - /// the number of operations completed during the last interval. - /// - AverageCount64 = 1073874176, - - /// - /// A percentage counter that shows the average ratio of hits to all - /// operations during the last two sample intervals. - /// - SampleFraction = 549585920, - - /// - /// An average counter that shows the average number of operations completed - /// in one second. When a counter of this type samples the data, each sampling - /// interrupt returns one or zero. The counter data is the number of ones that - /// were sampled. It measures time in units of ticks of the system performance timer. - /// - SampleCounter = 4260864, - - /// - /// A base counter that stores the number of sampling interrupts taken - /// and is used as a denominator in the sampling fraction. The sampling - /// fraction is the number of samples that were 1 (or true) for a sample - /// interrupt. Check that this value is greater than zero before using - /// it as the denominator in a calculation of SampleFraction. - /// - SampleBase = 1073939457, - - /// - /// A percentage counter that shows the average time that a component is - /// active as a percentage of the total sample time. - /// - CounterTimer = 541132032, - - /// - /// A percentage counter that displays the average percentage of active - /// time observed during sample interval. The value of these counters is - /// calculated by monitoring the percentage of time that the service was - /// inactive and then subtracting that value from 100 percent. - /// - CounterTimerInverse = 557909248, - - /// A percentage counter that shows the active time of a component - /// as a percentage of the total elapsed time of the sample interval. - /// It measures time in units of 100 nanoseconds (ns). Counters of this - /// type are designed to measure the activity of one component at a time. - /// - Timer100Ns = 542180608, - - /// - /// A percentage counter that shows the average percentage of active time - /// observed during the sample interval. - /// - Timer100NsInverse = 558957824, - - /// - /// A difference timer that shows the total time between when the component - /// or process started and the time when this value is calculated. - /// - ElapsedTime = 807666944, - - /// - /// A percentage counter that displays the active time of one or more - /// components as a percentage of the total time of the sample interval. - /// Because the numerator records the active time of components operating - /// simultaneously, the resulting percentage can exceed 100 percent. - /// - CounterMultiTimer = 574686464, - - /// - /// A percentage counter that shows the active time of one or more components - /// as a percentage of the total time of the sample interval. It derives - /// the active time by measuring the time that the components were not - /// active and subtracting the result from 100 percent by the number of - /// objects monitored. - /// - CounterMultiTimerInverse = 591463680, - - /// - /// A percentage counter that shows the active time of one or more components - /// as a percentage of the total time of the sample interval. It measures - /// time in 100 nanosecond (ns) units. - CounterMultiTimer100Ns = 575735040, - - /// - /// A percentage counter that shows the active time of one or more components - /// as a percentage of the total time of the sample interval. Counters of - /// this type measure time in 100 nanosecond (ns) units. They derive the - /// active time by measuring the time that the components were not active - /// and subtracting the result from multiplying 100 percent by the number - /// of objects monitored. - CounterMultiTimer100NsInverse = 592512256, - - /// - /// A base counter that indicates the number of items sampled. It is used - /// as the denominator in the calculations to get an average among the - /// items sampled when taking timings of multiple, but similar items. - /// Used with CounterMultiTimer, CounterMultiTimerInverse, CounterMultiTimer100Ns, - /// and CounterMultiTimer100NsInverse. - CounterMultiBase = 1107494144, - - /// - /// A difference counter that shows the change in the measured attribute - /// between the two most recent sample intervals. - /// - CounterDelta32 = 4195328, - - /// - /// A difference counter that shows the change in the measured attribute - /// between the two most recent sample intervals. It is the same as the - /// CounterDelta32 counter type except that is uses larger fields to - /// accomodate larger values. - CounterDelta64 = 4195584 - } -} -#endif diff --git a/src/Microsoft.PowerShell.Commands.Diagnostics/CounterFileInfo.cs b/src/Microsoft.PowerShell.Commands.Diagnostics/CounterFileInfo.cs index 54c0dd83ea3..2b11b6d3b03 100644 --- a/src/Microsoft.PowerShell.Commands.Diagnostics/CounterFileInfo.cs +++ b/src/Microsoft.PowerShell.Commands.Diagnostics/CounterFileInfo.cs @@ -1,13 +1,12 @@ -// -// Copyright (c) 2008 Microsoft Corporation. All rights reserved. -// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. using System; using System.Collections; -using System.Collections.Specialized; using System.Collections.Generic; -using System.Diagnostics; +using System.Collections.Specialized; using System.ComponentModel; +using System.Diagnostics; namespace Microsoft.PowerShell.Commands.GetCounter { @@ -31,6 +30,7 @@ public DateTime OldestRecord return _oldestRecord; } } + private DateTime _oldestRecord = DateTime.MinValue; public DateTime NewestRecord @@ -40,6 +40,7 @@ public DateTime NewestRecord return _newestRecord; } } + private DateTime _newestRecord = DateTime.MaxValue; public UInt32 SampleCount @@ -49,7 +50,7 @@ public UInt32 SampleCount return _sampleCount; } } + private UInt32 _sampleCount = 0; } } - diff --git a/src/Microsoft.PowerShell.Commands.Diagnostics/CounterSample.cs b/src/Microsoft.PowerShell.Commands.Diagnostics/CounterSample.cs index ac376f7e00f..9089216a485 100644 --- a/src/Microsoft.PowerShell.Commands.Diagnostics/CounterSample.cs +++ b/src/Microsoft.PowerShell.Commands.Diagnostics/CounterSample.cs @@ -1,18 +1,10 @@ -// -// Copyright (c) 2008 Microsoft Corporation. All rights reserved. -// - +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. using System; -using System.Reflection; -using System.Collections.Generic; using System.Diagnostics; -using System.ComponentModel; -using System.Resources; -using Microsoft.Powershell.Commands.GetCounter.PdhNative; -using System.Globalization; using System.Diagnostics.CodeAnalysis; - +using System.Resources; namespace Microsoft.PowerShell.Commands.GetCounter { @@ -35,106 +27,43 @@ internal PerformanceCounterSample(string path, UInt64 timeStamp100nSec, UInt32 status) { - _path = path; - _instanceName = instanceName; - _cookedValue = cookedValue; - _rawValue = rawValue; - _secondValue = secondValue; - _multiCount = multiCount; - _counterType = counterType; - _defaultScale = defaultScale; - _timeBase = timeBase; - _timeStamp = timeStamp; - _timeStamp100nSec = timeStamp100nSec; - _status = status; + Path = path; + InstanceName = instanceName; + CookedValue = cookedValue; + RawValue = rawValue; + SecondValue = secondValue; + MultipleCount = multiCount; + CounterType = counterType; + DefaultScale = defaultScale; + TimeBase = timeBase; + Timestamp = timeStamp; + Timestamp100NSec = timeStamp100nSec; + Status = status; } - public string Path - { - get { return _path; } - set { _path = value; } - } - private string _path = ""; + public string Path { get; set; } = string.Empty; + public string InstanceName { get; set; } = string.Empty; - public string InstanceName - { - get { return _instanceName; } - set { _instanceName = value; } - } - private string _instanceName = ""; + public double CookedValue { get; set; } + public UInt64 RawValue { get; set; } - public double CookedValue - { - get { return _cookedValue; } - set { _cookedValue = value; } - } - private double _cookedValue = 0; + public UInt64 SecondValue { get; set; } - public UInt64 RawValue - { - get { return _rawValue; } - set { _rawValue = value; } - } - private UInt64 _rawValue = 0; + public uint MultipleCount { get; set; } - public UInt64 SecondValue - { - get { return _secondValue; } - set { _secondValue = value; } - } - private UInt64 _secondValue = 0; + public PerformanceCounterType CounterType { get; set; } - public uint MultipleCount - { - get { return _multiCount; } - set { _multiCount = value; } - } - private uint _multiCount = 0; + public DateTime Timestamp { get; set; } = DateTime.MinValue; - public PerformanceCounterType CounterType - { - get { return _counterType; } - set { _counterType = value; } - } - private PerformanceCounterType _counterType = 0; - - public DateTime Timestamp - { - get { return _timeStamp; } - set { _timeStamp = value; } - } - private DateTime _timeStamp = DateTime.MinValue; + public UInt64 Timestamp100NSec { get; set; } + public UInt32 Status { get; set; } - public UInt64 Timestamp100NSec - { - get { return _timeStamp100nSec; } - set { _timeStamp100nSec = value; } - } - private UInt64 _timeStamp100nSec = 0; + public UInt32 DefaultScale { get; set; } - public UInt32 Status - { - get { return _status; } - set { _status = value; } - } - private UInt32 _status = 0; - - public UInt32 DefaultScale - { - get { return _defaultScale; } - set { _defaultScale = value; } - } - private UInt32 _defaultScale = 0; - - public UInt64 TimeBase - { - get { return _timeBase; } - set { _timeBase = value; } - } - private UInt64 _timeBase = 0; + public UInt64 TimeBase { get; set; } } public class PerformanceCounterSampleSet @@ -148,28 +77,18 @@ internal PerformanceCounterSampleSet(DateTime timeStamp, PerformanceCounterSample[] counterSamples, bool firstSet) : this() { - _timeStamp = timeStamp; - _counterSamples = counterSamples; + Timestamp = timeStamp; + CounterSamples = counterSamples; } - public DateTime Timestamp - { - get { return _timeStamp; } - set { _timeStamp = value; } - } - private DateTime _timeStamp = DateTime.MinValue; + public DateTime Timestamp { get; set; } = DateTime.MinValue; [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Scope = "member", Target = "Microsoft.PowerShell.Commands.GetCounter.PerformanceCounterSample.CounterSamples", Justification = "A string[] is required here because that is the type Powershell supports")] - public PerformanceCounterSample[] CounterSamples - { - get { return _counterSamples; } - set { _counterSamples = value; } - } - private PerformanceCounterSample[] _counterSamples = null; + public PerformanceCounterSample[] CounterSamples { get; set; } - private ResourceManager _resourceMgr = null; + private readonly ResourceManager _resourceMgr = null; } } diff --git a/src/Microsoft.PowerShell.Commands.Diagnostics/CounterSet.cs b/src/Microsoft.PowerShell.Commands.Diagnostics/CounterSet.cs index a7160d2ca92..dfc175b604c 100644 --- a/src/Microsoft.PowerShell.Commands.Diagnostics/CounterSet.cs +++ b/src/Microsoft.PowerShell.Commands.Diagnostics/CounterSet.cs @@ -1,13 +1,10 @@ -// -// Copyright (c) 2008 Microsoft Corporation. All rights reserved. -// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. using System; -using System.Collections; -using System.Collections.Specialized; using System.Collections.Generic; +using System.Collections.Specialized; using System.Diagnostics; -using System.ComponentModel; namespace Microsoft.PowerShell.Commands.GetCounter { @@ -19,90 +16,56 @@ internal CounterSet(string setName, string setHelp, ref Dictionary counterInstanceMapping) { - _counterSetName = setName; + CounterSetName = setName; if (machineName == null || machineName.Length == 0) { machineName = "."; } else { - _machineName = machineName; - if (!_machineName.StartsWith(@"\\", StringComparison.OrdinalIgnoreCase)) + MachineName = machineName; + if (!MachineName.StartsWith(@"\\", StringComparison.OrdinalIgnoreCase)) { - _machineName = @"\\" + _machineName; + MachineName = @"\\" + MachineName; } } - _counterSetType = categoryType; - _description = setHelp; - _counterInstanceMapping = counterInstanceMapping; - } - public string CounterSetName - { - get - { - return _counterSetName; - } + CounterSetType = categoryType; + Description = setHelp; + CounterInstanceMapping = counterInstanceMapping; } - private string _counterSetName = ""; - public string MachineName - { - get - { - return _machineName; - } - } - private string _machineName = "."; + public string CounterSetName { get; } = string.Empty; - public PerformanceCounterCategoryType CounterSetType - { - get - { - return _counterSetType; - } - } - private PerformanceCounterCategoryType _counterSetType; + public string MachineName { get; } = "."; + public PerformanceCounterCategoryType CounterSetType { get; } - public string Description - { - get - { - return _description; - } - } - private string _description = ""; + public string Description { get; } = string.Empty; - internal Dictionary CounterInstanceMapping - { - get - { - return _counterInstanceMapping; - } - } - private Dictionary _counterInstanceMapping; + internal Dictionary CounterInstanceMapping { get; } public StringCollection Paths { get { - StringCollection retColl = new StringCollection(); + StringCollection retColl = new(); foreach (string counterName in this.CounterInstanceMapping.Keys) { string path; if (CounterInstanceMapping[counterName].Length != 0) { - path = (_machineName == ".") ? - ("\\" + _counterSetName + "(*)\\" + counterName) : - (_machineName + "\\" + _counterSetName + "(*)\\" + counterName); + path = (MachineName == ".") ? + ("\\" + CounterSetName + "(*)\\" + counterName) : + (MachineName + "\\" + CounterSetName + "(*)\\" + counterName); } else { - path = (_machineName == ".") ? - ("\\" + _counterSetName + "\\" + counterName) : - (_machineName + "\\" + _counterSetName + "\\" + counterName); + path = (MachineName == ".") ? + ("\\" + CounterSetName + "\\" + counterName) : + (MachineName + "\\" + CounterSetName + "\\" + counterName); } + retColl.Add(path); } @@ -114,17 +77,18 @@ public StringCollection PathsWithInstances { get { - StringCollection retColl = new StringCollection(); + StringCollection retColl = new(); foreach (string counterName in CounterInstanceMapping.Keys) { foreach (string instanceName in CounterInstanceMapping[counterName]) { - string path = (_machineName == ".") ? - ("\\" + _counterSetName + "(" + instanceName + ")\\" + counterName) : - (_machineName + "\\" + _counterSetName + "(" + instanceName + ")\\" + counterName); + string path = (MachineName == ".") ? + ("\\" + CounterSetName + "(" + instanceName + ")\\" + counterName) : + (MachineName + "\\" + CounterSetName + "(" + instanceName + ")\\" + counterName); retColl.Add(path); } } + return retColl; } } diff --git a/src/Microsoft.PowerShell.Commands.Diagnostics/ExportCounterCommand.cs b/src/Microsoft.PowerShell.Commands.Diagnostics/ExportCounterCommand.cs index 8ef8fa3d49a..9385e55909f 100644 --- a/src/Microsoft.PowerShell.Commands.Diagnostics/ExportCounterCommand.cs +++ b/src/Microsoft.PowerShell.Commands.Diagnostics/ExportCounterCommand.cs @@ -1,32 +1,30 @@ -// -// Copyright (c) 2008 Microsoft Corporation. All rights reserved. -// - +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. using System; -using System.Text; -using System.IO; -using System.Xml; -using System.Net; -using System.Management.Automation; -using System.ComponentModel; -using System.Reflection; -using System.Globalization; -using System.Management.Automation.Runspaces; using System.Collections; -using System.Collections.Specialized; using System.Collections.Generic; using System.Collections.ObjectModel; +using System.Collections.Specialized; +using System.ComponentModel; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.IO; +using System.Management.Automation; +using System.Management.Automation.Runspaces; +using System.Net; +using System.Reflection; +using System.Resources; using System.Security; using System.Security.Principal; -using System.Resources; +using System.Text; using System.Threading; -using System.Diagnostics.CodeAnalysis; -using Microsoft.Powershell.Commands.GetCounter.PdhNative; -using Microsoft.PowerShell.Commands.GetCounter; -using Microsoft.PowerShell.Commands.Diagnostics.Common; +using System.Xml; +using Microsoft.PowerShell.Commands.Diagnostics.Common; +using Microsoft.PowerShell.Commands.GetCounter; +using Microsoft.Powershell.Commands.GetCounter.PdhNative; namespace Microsoft.PowerShell.Commands { @@ -49,12 +47,13 @@ public sealed class ExportCounterCommand : PSCmdlet public string Path { get { return _path; } + set { _path = value; } } + private string _path; private string _resolvedPath; - // // Format parameter. // Valid strings are "blg", "csv", "tsv" (case-insensitive). @@ -69,11 +68,11 @@ public string Path public string FileFormat { get { return _format; } + set { _format = value; } } - private string _format = "blg"; - + private string _format = "blg"; // // MaxSize parameter @@ -84,10 +83,11 @@ public string FileFormat public UInt32 MaxSize { get { return _maxSize; } + set { _maxSize = value; } } - private UInt32 _maxSize = 0; + private UInt32 _maxSize = 0; // // InputObject parameter @@ -105,10 +105,11 @@ public UInt32 MaxSize public PerformanceCounterSampleSet[] InputObject { get { return _counterSampleSets; } + set { _counterSampleSets = value; } } - private PerformanceCounterSampleSet[] _counterSampleSets = new PerformanceCounterSampleSet[0]; + private PerformanceCounterSampleSet[] _counterSampleSets = new PerformanceCounterSampleSet[0]; // // Force switch @@ -118,8 +119,10 @@ public PerformanceCounterSampleSet[] InputObject public SwitchParameter Force { get { return _force; } + set { _force = value; } } + private SwitchParameter _force; // @@ -130,11 +133,11 @@ public SwitchParameter Force public SwitchParameter Circular { get { return _circular; } + set { _circular = value; } } - private SwitchParameter _circular; - + private SwitchParameter _circular; private ResourceManager _resourceMgr = null; @@ -159,7 +162,7 @@ protected override void BeginProcessing() throw new PlatformNotSupportedException(); } - // PowerShell Core requires at least Windows 7, + // PowerShell 7 requires at least Windows 7, // so no version test is needed _pdhHelper = new PdhHelper(false); #else @@ -213,7 +216,6 @@ protected override void EndProcessing() _pdhHelper.Dispose(); } - /// /// Handle Control-C /// @@ -247,6 +249,7 @@ protected override void ProcessRecord() { res = _pdhHelper.AddRelogCountersPreservingPaths(_counterSampleSets[0]); } + if (res != 0) { ReportPdhError(res, true); @@ -279,7 +282,6 @@ protected override void ProcessRecord() _queryInitialized = true; } - foreach (PerformanceCounterSampleSet set in _counterSampleSets) { _pdhHelper.ResetRelogValues(); @@ -299,6 +301,7 @@ protected override void ProcessRecord() ReportPdhError(res, true); } } + res = _pdhHelper.WriteRelogSample(set.Timestamp); if (res != 0) { @@ -365,6 +368,7 @@ private void ReportPdhError(uint res, bool bTerminate) { msg = string.Format(CultureInfo.InvariantCulture, _resourceMgr.GetString("CounterApiError"), res); } + Exception exc = new Exception(msg); if (bTerminate) { diff --git a/src/Microsoft.PowerShell.Commands.Diagnostics/GetCounterCommand.cs b/src/Microsoft.PowerShell.Commands.Diagnostics/GetCounterCommand.cs index 9331855ad1c..0122ad1941f 100644 --- a/src/Microsoft.PowerShell.Commands.Diagnostics/GetCounterCommand.cs +++ b/src/Microsoft.PowerShell.Commands.Diagnostics/GetCounterCommand.cs @@ -1,39 +1,25 @@ -// -// Copyright (c) 2008 Microsoft Corporation. All rights reserved. -// - +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. using System; -using System.Text; -using System.IO; -using System.Xml; -using System.Net; -using System.Management.Automation; -using System.ComponentModel; -using System.Reflection; -using System.Globalization; -using System.Management.Automation.Runspaces; -using System.Collections; -using System.Collections.Specialized; using System.Collections.Generic; -using System.Collections.ObjectModel; +using System.Collections.Specialized; using System.Diagnostics; -using System.Security; -using System.Security.Principal; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Management.Automation; using System.Resources; using System.Threading; -using System.Diagnostics.CodeAnalysis; using Microsoft.Powershell.Commands.GetCounter.PdhNative; -using Microsoft.PowerShell.Commands.GetCounter; using Microsoft.PowerShell.Commands.Diagnostics.Common; - +using Microsoft.PowerShell.Commands.GetCounter; namespace Microsoft.PowerShell.Commands { - /// + /// /// Class that implements the Get-Counter cmdlet. - /// - [Cmdlet(VerbsCommon.Get, "Counter", DefaultParameterSetName = "GetCounterSet", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=138335")] + /// + [Cmdlet(VerbsCommon.Get, "Counter", DefaultParameterSetName = "GetCounterSet", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2109647")] public sealed class GetCounterCommand : PSCmdlet { // @@ -51,14 +37,7 @@ public sealed class GetCounterCommand : PSCmdlet Scope = "member", Target = "Microsoft.PowerShell.Commands.GetCounterCommand.ListSet", Justification = "A string[] is required here because that is the type Powershell supports")] - - public string[] ListSet - { - get { return _listSet; } - set { _listSet = value; } - } - private string[] _listSet = { "*" }; - + public string[] ListSet { get; set; } = { "*" }; // // Counter parameter @@ -76,23 +55,28 @@ public string[] ListSet Justification = "A string[] is required here because that is the type Powershell supports")] public string[] Counter { - get { return _counter; } + get + { + return _counter; + } + set { _counter = value; _defaultCounters = false; } } + private string[] _counter = {@"\network interface(*)\bytes total/sec", @"\processor(_total)\% processor time", @"\memory\% committed bytes in use", @"\memory\cache faults/sec", @"\physicaldisk(_total)\% disk time", @"\physicaldisk(_total)\current disk queue length"}; - private bool _defaultCounters = true; - private List _accumulatedCounters = new List(); + private bool _defaultCounters = true; + private readonly List _accumulatedCounters = new(); // // SampleInterval parameter. @@ -104,18 +88,13 @@ public string[] Counter ValueFromPipelineByPropertyName = false, HelpMessageBaseName = "GetEventResources")] [ValidateRange((int)1, int.MaxValue)] - public int SampleInterval - { - get { return _sampleInterval; } - set { _sampleInterval = value; } - } - private int _sampleInterval = 1; - + public int SampleInterval { get; set; } = 1; // // MaxSamples parameter // private const Int64 KEEP_ON_SAMPLING = -1; + [Parameter( ParameterSetName = "GetCounterSet", ValueFromPipeline = false, @@ -124,17 +103,21 @@ public int SampleInterval [ValidateRange((Int64)1, Int64.MaxValue)] public Int64 MaxSamples { - get { return _maxSamples; } + get + { + return _maxSamples; + } + set { _maxSamples = value; _maxSamplesSpecified = true; } } + private Int64 _maxSamples = 1; private bool _maxSamplesSpecified = false; - // // Continuous switch // @@ -142,10 +125,11 @@ public Int64 MaxSamples public SwitchParameter Continuous { get { return _continuous; } + set { _continuous = value; } } - private bool _continuous = false; + private bool _continuous = false; // // ComputerName parameter @@ -162,18 +146,13 @@ public SwitchParameter Continuous Scope = "member", Target = "Microsoft.PowerShell.Commands.GetCounterCommand.ComputerName", Justification = "A string[] is required here because that is the type Powershell supports")] - public string[] ComputerName - { - get { return _computerName; } - set { _computerName = value; } - } - private string[] _computerName = new string[0]; + public string[] ComputerName { get; set; } = Array.Empty(); private ResourceManager _resourceMgr = null; private PdhHelper _pdhHelper = null; - private EventWaitHandle _cancelEventArrived = new EventWaitHandle(false, EventResetMode.ManualReset); + private readonly EventWaitHandle _cancelEventArrived = new(false, EventResetMode.ManualReset); // Culture identifier(s) private const string FrenchCultureId = "fr-FR"; @@ -185,17 +164,17 @@ public string[] ComputerName // // With this dictionary, we can add special mapping if we find other special cases in the future. private readonly Dictionary>> _cultureAndSpecialCharacterMap = - new Dictionary>>() + new() { { FrenchCultureId, new List>() { // 'APOSTROPHE' to 'RIGHT SINGLE QUOTATION MARK' - new Tuple((char) 0x0027, (char) 0x2019), + new Tuple((char)0x0027, (char)0x2019), // 'MODIFIER LETTER APOSTROPHE' to 'RIGHT SINGLE QUOTATION MARK' - new Tuple((char) 0x02BC, (char) 0x2019), + new Tuple((char)0x02BC, (char)0x2019), // 'HEAVY SINGLE COMMA QUOTATION MARK ORNAMENT' to 'RIGHT SINGLE QUOTATION MARK' - new Tuple((char) 0x275C, (char) 0x2019), + new Tuple((char)0x275C, (char)0x2019), } } }; @@ -205,33 +184,25 @@ public string[] ComputerName // protected override void BeginProcessing() { - -#if CORECLR if (Platform.IsIoT) { // IoT does not have the '$env:windir\System32\pdh.dll' assembly which is required by this cmdlet. throw new PlatformNotSupportedException(); } - // PowerShell Core requires at least Windows 7, - // so no version test is needed - _pdhHelper = new PdhHelper(false); -#else - _pdhHelper = new PdhHelper(System.Environment.OSVersion.Version.Major < 6); -#endif + _pdhHelper = new PdhHelper(); _resourceMgr = Microsoft.PowerShell.Commands.Diagnostics.Common.CommonUtilities.GetResourceManager(); uint res = _pdhHelper.ConnectToDataSource(); - if (res != 0) + if (res != PdhResults.PDH_CSTATUS_VALID_DATA) { ReportPdhError(res, true); return; } - if (Continuous.IsPresent && _maxSamplesSpecified) { - Exception exc = new Exception(string.Format(CultureInfo.CurrentCulture, _resourceMgr.GetString("CounterContinuousOrMaxSamples"))); + Exception exc = new(string.Format(CultureInfo.CurrentCulture, _resourceMgr.GetString("CounterContinuousOrMaxSamples"))); ThrowTerminatingError(new ErrorRecord(exc, "CounterContinuousOrMaxSamples", ErrorCategory.InvalidArgument, null)); } } @@ -249,7 +220,6 @@ protected override void EndProcessing() _pdhHelper.Dispose(); } - // // Handle Control-C // @@ -278,7 +248,7 @@ protected override void ProcessRecord() break; default: - Debug.Assert(false, string.Format(CultureInfo.InvariantCulture, "Invalid parameter set name: {0}", ParameterSetName)); + Debug.Fail(string.Create(CultureInfo.InvariantCulture, $"Invalid parameter set name: {ParameterSetName}")); break; } } @@ -302,12 +272,12 @@ private void AccumulatePipelineCounters() // private void ProcessListSet() { - if (_computerName.Length == 0) + if (ComputerName.Length == 0) { ProcessListSetPerMachine(null); } else - foreach (string machine in _computerName) + foreach (string machine in ComputerName) { ProcessListSetPerMachine(machine); } @@ -319,24 +289,24 @@ private void ProcessListSet() // private void ProcessListSetPerMachine(string machine) { - StringCollection counterSets = new StringCollection(); + StringCollection counterSets = new(); uint res = _pdhHelper.EnumObjects(machine, ref counterSets); - if (res != 0) + if (res != PdhResults.PDH_CSTATUS_VALID_DATA) { - //add an error message + // add an error message string msg = string.Format(CultureInfo.InvariantCulture, _resourceMgr.GetString("NoCounterSetsOnComputer"), machine, res); - Exception exc = new Exception(msg); + Exception exc = new(msg); WriteError(new ErrorRecord(exc, "NoCounterSetsOnComputer", ErrorCategory.InvalidResult, machine)); return; } CultureInfo culture = GetCurrentCulture(); List> characterReplacementList = null; - StringCollection validPaths = new StringCollection(); + StringCollection validPaths = new(); _cultureAndSpecialCharacterMap.TryGetValue(culture.Name, out characterReplacementList); - foreach (string pattern in _listSet) + foreach (string pattern in ListSet) { bool bMatched = false; string normalizedPattern = pattern; @@ -349,7 +319,7 @@ private void ProcessListSetPerMachine(string machine) } } - WildcardPattern wildLogPattern = new WildcardPattern(normalizedPattern, WildcardOptions.IgnoreCase); + WildcardPattern wildLogPattern = new(normalizedPattern, WildcardOptions.IgnoreCase); foreach (string counterSet in counterSets) { @@ -358,18 +328,18 @@ private void ProcessListSetPerMachine(string machine) continue; } - StringCollection counterSetCounters = new StringCollection(); - StringCollection counterSetInstances = new StringCollection(); + StringCollection counterSetCounters = new(); + StringCollection counterSetInstances = new(); res = _pdhHelper.EnumObjectItems(machine, counterSet, ref counterSetCounters, ref counterSetInstances); if (res == PdhResults.PDH_ACCESS_DENIED) { string msg = string.Format(CultureInfo.InvariantCulture, _resourceMgr.GetString("CounterSetEnumAccessDenied"), counterSet); - Exception exc = new Exception(msg); + Exception exc = new(msg); WriteError(new ErrorRecord(exc, "CounterSetEnumAccessDenied", ErrorCategory.InvalidResult, null)); continue; } - else if (res != 0) + else if (res != PdhResults.PDH_CSTATUS_VALID_DATA) { ReportPdhError(res, false); continue; @@ -391,13 +361,10 @@ private void ProcessListSetPerMachine(string machine) instanceArray[0] = "*"; } - Dictionary counterInstanceMapping = new Dictionary(); + Dictionary counterInstanceMapping = new(); foreach (string counter in counterSetCounters) { - if (!counterInstanceMapping.ContainsKey(counter)) - { - counterInstanceMapping.Add(counter, instanceArray); - } + counterInstanceMapping.TryAdd(counter, instanceArray); } PerformanceCounterCategoryType categoryType = PerformanceCounterCategoryType.Unknown; @@ -405,14 +372,14 @@ private void ProcessListSetPerMachine(string machine) { categoryType = PerformanceCounterCategoryType.MultiInstance; } - else //if (counterSetInstances.Count == 1) //??? + else // if (counterSetInstances.Count == 1) //??? { categoryType = PerformanceCounterCategoryType.SingleInstance; } string setHelp = _pdhHelper.GetCounterSetHelp(machine, counterSet); - CounterSet setObj = new CounterSet(counterSet, machine, categoryType, setHelp, ref counterInstanceMapping); + CounterSet setObj = new(counterSet, machine, categoryType, setHelp, ref counterInstanceMapping); WriteObject(setObj); bMatched = true; } @@ -420,7 +387,7 @@ private void ProcessListSetPerMachine(string machine) if (!bMatched) { string msg = _resourceMgr.GetString("NoMatchingCounterSetsFound"); - Exception exc = new Exception(string.Format(CultureInfo.InvariantCulture, msg, + Exception exc = new(string.Format(CultureInfo.InvariantCulture, msg, machine ?? "localhost", normalizedPattern)); WriteError(new ErrorRecord(exc, "NoMatchingCounterSetsFound", ErrorCategory.ObjectNotFound, null)); } @@ -442,25 +409,24 @@ private void ProcessGetCounter() CultureInfo culture = GetCurrentCulture(); List> characterReplacementList = null; List paths = CombineMachinesAndCounterPaths(); - uint res = 0; if (!_defaultCounters) { _cultureAndSpecialCharacterMap.TryGetValue(culture.Name, out characterReplacementList); } - - StringCollection allExpandedPaths = new StringCollection(); + StringCollection allExpandedPaths = new(); + uint res; foreach (string path in paths) { string localizedPath = path; if (_defaultCounters) { res = _pdhHelper.TranslateLocalCounterPath(path, out localizedPath); - if (res != 0) + if (res != PdhResults.PDH_CSTATUS_VALID_DATA) { string msg = string.Format(CultureInfo.CurrentCulture, _resourceMgr.GetString("CounterPathTranslationFailed"), res); - Exception exc = new Exception(msg); + Exception exc = new(msg); WriteError(new ErrorRecord(exc, "CounterPathTranslationFailed", ErrorCategory.InvalidResult, null)); localizedPath = path; @@ -476,7 +442,7 @@ private void ProcessGetCounter() StringCollection expandedPaths; res = _pdhHelper.ExpandWildCardPath(localizedPath, out expandedPaths); - if (res != 0) + if (res != PdhResults.PDH_CSTATUS_VALID_DATA) { WriteDebug("Could not expand path " + localizedPath); ReportPdhError(res, false); @@ -488,26 +454,29 @@ private void ProcessGetCounter() if (!_pdhHelper.IsPathValid(expandedPath)) { string msg = string.Format(CultureInfo.CurrentCulture, _resourceMgr.GetString("CounterPathIsInvalid"), localizedPath); - Exception exc = new Exception(msg); + Exception exc = new(msg); WriteError(new ErrorRecord(exc, "CounterPathIsInvalid", ErrorCategory.InvalidResult, null)); continue; } + allExpandedPaths.Add(expandedPath); } } + if (allExpandedPaths.Count == 0) { return; } res = _pdhHelper.OpenQuery(); - if (res != 0) + if (res != PdhResults.PDH_CSTATUS_VALID_DATA) { ReportPdhError(res, false); } + res = _pdhHelper.AddCounters(ref allExpandedPaths, true); - if (res != 0) + if (res != PdhResults.PDH_CSTATUS_VALID_DATA) { ReportPdhError(res, true); @@ -529,16 +498,16 @@ private void ProcessGetCounter() // read the first set just to get the initial values res = _pdhHelper.ReadNextSet(out nextSet, bSkip); - if (res == 0) + if (res == PdhResults.PDH_CSTATUS_VALID_DATA) { - //Display valid data + // Display valid data if (!bSkip) { WriteSampleSetObject(nextSet); sampleReads++; } - //Don't need to skip anymore + // Don't need to skip anymore bSkip = false; } else if (res == PdhResults.PDH_NO_DATA || res == PdhResults.PDH_INVALID_DATA) @@ -565,12 +534,7 @@ private void ProcessGetCounter() break; } -#if CORECLR - // CoreCLR has no overload of WaitOne with (interval, exitContext) - bool cancelled = _cancelEventArrived.WaitOne((int)_sampleInterval * 1000); -#else - bool cancelled = _cancelEventArrived.WaitOne((int)_sampleInterval * 1000, true); -#endif + bool cancelled = _cancelEventArrived.WaitOne((int)SampleInterval * 1000, true); if (cancelled) { break; @@ -586,7 +550,8 @@ private void ReportPdhError(uint res, bool bTerminate) { msg = string.Format(CultureInfo.InvariantCulture, _resourceMgr.GetString("CounterApiError"), res); } - Exception exc = new Exception(msg); + + Exception exc = new(msg); if (bTerminate) { ThrowTerminatingError(new ErrorRecord(exc, "CounterApiError", ErrorCategory.InvalidResult, null)); @@ -597,7 +562,6 @@ private void ReportPdhError(uint res, bool bTerminate) } } - // // CombineMachinesAndCounterPaths() helper. // For paths that do not contain machine names, creates a path for each machine in machineNames. @@ -605,9 +569,9 @@ private void ReportPdhError(uint res, bool bTerminate) // private List CombineMachinesAndCounterPaths() { - List retColl = new List(); + List retColl = new(); - if (_computerName.Length == 0) + if (ComputerName.Length == 0) { retColl.AddRange(_accumulatedCounters); return retColl; @@ -615,21 +579,22 @@ private List CombineMachinesAndCounterPaths() foreach (string path in _accumulatedCounters) { - if (path.StartsWith("\\\\", StringComparison.OrdinalIgnoreCase)) //NOTE: can we do anything smarter here? + if (path.StartsWith("\\\\", StringComparison.OrdinalIgnoreCase)) // NOTE: can we do anything smarter here? { retColl.Add(path); } else { - foreach (string machine in _computerName) + foreach (string machine in ComputerName) { + string slashBeforePath = path.Length > 0 && path[0] == '\\' ? string.Empty : "\\"; if (machine.StartsWith("\\\\", StringComparison.OrdinalIgnoreCase)) { - retColl.Add(machine + "\\" + path); + retColl.Add(machine + slashBeforePath + path); } else { - retColl.Add("\\\\" + machine + "\\" + path); + retColl.Add("\\\\" + machine + slashBeforePath + path); } } } @@ -650,22 +615,18 @@ private void WriteSampleSetObject(PerformanceCounterSampleSet set) if (sample.Status != 0) { string msg = string.Format(CultureInfo.InvariantCulture, _resourceMgr.GetString("CounterSampleDataInvalid")); - Exception exc = new Exception(msg); + Exception exc = new(msg); WriteError(new ErrorRecord(exc, "CounterApiError", ErrorCategory.InvalidResult, null)); break; } } + WriteObject(set); } private static CultureInfo GetCurrentCulture() { -#if CORECLR - return CultureInfo.CurrentCulture; -#else return Thread.CurrentThread.CurrentUICulture; -#endif } } } - diff --git a/src/Microsoft.PowerShell.Commands.Diagnostics/GetEventCommand.cs b/src/Microsoft.PowerShell.Commands.Diagnostics/GetEventCommand.cs index c44e17d1772..c7a07bab6ec 100644 --- a/src/Microsoft.PowerShell.Commands.Diagnostics/GetEventCommand.cs +++ b/src/Microsoft.PowerShell.Commands.Diagnostics/GetEventCommand.cs @@ -1,35 +1,36 @@ -// -// Copyright (c) 2007 Microsoft Corporation. All rights reserved. -// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. using System; -using System.Xml; -using System.Net; -using System.Management.Automation; -using System.Reflection; -using System.Globalization; using System.Collections; -using System.Collections.Specialized; using System.Collections.Generic; using System.Collections.ObjectModel; +using System.Collections.Specialized; +using System.Diagnostics.CodeAnalysis; using System.Diagnostics.Eventing.Reader; -using System.Security.Principal; +using System.Globalization; +using System.Management.Automation; +using System.Net; using System.Resources; -using System.Diagnostics.CodeAnalysis; +using System.Security.Principal; using System.Text; +using System.Xml; [assembly: CLSCompliant(false)] namespace Microsoft.PowerShell.Commands { - /// + /// /// Class that implements the Get-WinEvent cmdlet. - /// - [Cmdlet(VerbsCommon.Get, "WinEvent", DefaultParameterSetName = "GetLogSet", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=138336")] + /// + [OutputType(typeof(EventRecord), ParameterSetName = new string[] { "GetLogSet", "GetProviderSet", "FileSet", "HashQuerySet", "XmlQuerySet" })] + [OutputType(typeof(ProviderMetadata), ParameterSetName = new string[] { "ListProviderSet" })] + [OutputType(typeof(EventLogConfiguration), ParameterSetName = new string[] { "ListLogSet" })] + [Cmdlet(VerbsCommon.Get, "WinEvent", DefaultParameterSetName = "GetLogSet", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2096581")] public sealed class GetWinEventCommand : PSCmdlet { /// - /// ListLog parameter + /// ListLog parameter. /// [Parameter( Position = 0, @@ -44,16 +45,10 @@ public sealed class GetWinEventCommand : PSCmdlet Scope = "member", Target = "Microsoft.PowerShell.Commands.GetEvent.ListLog", Justification = "A string[] is required here because that is the type Powershell supports")] - - public string[] ListLog - { - get { return _listLog; } - set { _listLog = value; } - } - private string[] _listLog = { "*" }; + public string[] ListLog { get; set; } = { "*" }; /// - /// GetLog parameter + /// GetLog parameter. /// [Parameter( Position = 0, @@ -66,16 +61,10 @@ public string[] ListLog Scope = "member", Target = "Microsoft.PowerShell.Commands.GetEvent.LogName", Justification = "A string[] is required here because that is the type Powershell supports")] - public string[] LogName - { - get { return _logName; } - set { _logName = value; } - } - private string[] _logName = { "*" }; - + public string[] LogName { get; set; } = { "*" }; /// - /// ListProvider parameter + /// ListProvider parameter. /// [Parameter( Position = 0, @@ -86,22 +75,14 @@ public string[] LogName HelpMessageBaseName = "GetEventResources", HelpMessageResourceId = "ListProviderParamHelp")] [AllowEmptyCollection] - [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Scope = "member", Target = "Microsoft.PowerShell.Commands.GetEvent.ListProvider", Justification = "A string[] is required here because that is the type Powershell supports")] - - public string[] ListProvider - { - get { return _listProvider; } - set { _listProvider = value; } - } - private string[] _listProvider = { "*" }; - + public string[] ListProvider { get; set; } = { "*" }; /// - /// ProviderName parameter + /// ProviderName parameter. /// [Parameter( Position = 0, @@ -110,22 +91,14 @@ public string[] ListProvider ValueFromPipelineByPropertyName = true, HelpMessageBaseName = "GetEventResources", HelpMessageResourceId = "GetProviderParamHelp")] - [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Scope = "member", Target = "Microsoft.PowerShell.Commands.GetEvent.ProviderName", Justification = "A string[] is required here because that is the type Powershell supports")] - - public string[] ProviderName - { - get { return _providerName; } - set { _providerName = value; } - } - private string[] _providerName; - + public string[] ProviderName { get; set; } /// - /// Path parameter + /// Path parameter. /// [Parameter( Position = 0, @@ -134,22 +107,15 @@ public string[] ProviderName ValueFromPipelineByPropertyName = true, HelpMessageBaseName = "GetEventResources", HelpMessageResourceId = "PathParamHelp")] - [Alias("PSPath")] [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Scope = "member", Target = "Microsoft.PowerShell.Commands.GetEvent.Path", Justification = "A string[] is required here because that is the type Powershell supports")] - public string[] Path - { - get { return _path; } - set { _path = value; } - } - private string[] _path; - + public string[] Path { get; set; } /// - /// MaxEvents parameter + /// MaxEvents parameter. /// [Parameter( ParameterSetName = "FileSet", @@ -181,16 +147,11 @@ public string[] Path ValueFromPipelineByPropertyName = false, HelpMessageBaseName = "GetEventResources", HelpMessageResourceId = "MaxEventsParamHelp")] - [ValidateRange((Int64)1, Int64.MaxValue)] - public Int64 MaxEvents - { - get { return _maxEvents; } - set { _maxEvents = value; } - } - private Int64 _maxEvents = -1; + [ValidateRange((long)1, long.MaxValue)] + public long MaxEvents { get; set; } = -1; /// - /// ComputerName parameter + /// ComputerName parameter. /// [Parameter( ParameterSetName = "ListProviderSet", @@ -216,18 +177,12 @@ public Int64 MaxEvents ParameterSetName = "XmlQuerySet", HelpMessageBaseName = "GetEventResources", HelpMessageResourceId = "ComputerNameParamHelp")] - [ValidateNotNull] [Alias("Cn")] - public string ComputerName - { - get { return _computerName; } - set { _computerName = value; } - } - private string _computerName = string.Empty; + public string ComputerName { get; set; } = string.Empty; /// - /// Credential parameter + /// Credential parameter. /// [Parameter(ParameterSetName = "ListProviderSet")] [Parameter(ParameterSetName = "GetProviderSet")] @@ -237,16 +192,10 @@ public string ComputerName [Parameter(ParameterSetName = "XmlQuerySet")] [Parameter(ParameterSetName = "FileSet")] [Credential] - public PSCredential Credential - { - get { return _credential; } - set { _credential = value; } - } - private PSCredential _credential = PSCredential.Empty; - + public PSCredential Credential { get; set; } = PSCredential.Empty; /// - /// FilterXPath parameter + /// FilterXPath parameter. /// [Parameter( ParameterSetName = "FileSet", @@ -264,15 +213,10 @@ public PSCredential Credential ValueFromPipelineByPropertyName = false, HelpMessageBaseName = "GetEventResources")] [ValidateNotNull] - public string FilterXPath - { - get { return _filter; } - set { _filter = value; } - } - private string _filter = "*"; + public string FilterXPath { get; set; } = "*"; /// - /// FilterXml parameter + /// FilterXml parameter. /// [Parameter( Position = 0, @@ -281,22 +225,10 @@ public string FilterXPath ValueFromPipelineByPropertyName = false, ParameterSetName = "XmlQuerySet", HelpMessageBaseName = "GetEventResources")] - - [SuppressMessage("Microsoft.Design", "CA1059:MembersShouldNotExposeCertainConcreteTypes", - Scope = "member", - Target = "Microsoft.PowerShell.Commands.GetEvent.FilterXml", - Justification = "An XmlDocument is required here because that is the type Powershell supports")] - - public XmlDocument FilterXml - { - get { return _xmlQuery; } - set { _xmlQuery = value; } - } - private XmlDocument _xmlQuery = null; - + public XmlDocument FilterXml { get; set; } /// - /// FilterHashtable parameter + /// FilterHashtable parameter. /// [Parameter( Position = 0, @@ -305,50 +237,37 @@ public XmlDocument FilterXml ValueFromPipelineByPropertyName = false, ParameterSetName = "HashQuerySet", HelpMessageBaseName = "GetEventResources")] - [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Scope = "member", Target = "Microsoft.PowerShell.Commands.GetEvent.FilterHashtable", Justification = "A string[] is required here because that is the type Powershell supports")] - - public Hashtable[] FilterHashtable - { - get { return _selector; } - set { _selector = value; } - } - private Hashtable[] _selector; + public Hashtable[] FilterHashtable { get; set; } /// - /// Force switch + /// Force switch. /// [Parameter(ParameterSetName = "ListLogSet")] [Parameter(ParameterSetName = "GetProviderSet")] [Parameter(ParameterSetName = "GetLogSet")] - [Parameter(ParameterSetName = "HashQuerySet")] - public SwitchParameter Force - { - get { return _force; } - set { _force = value; } - } - private SwitchParameter _force; + public SwitchParameter Force { get; set; } /// - /// Oldest switch + /// Oldest switch. /// [Parameter(ParameterSetName = "FileSet")] [Parameter(ParameterSetName = "GetProviderSet")] [Parameter(ParameterSetName = "GetLogSet")] - [Parameter(ParameterSetName = "HashQuerySet")] [Parameter(ParameterSetName = "XmlQuerySet")] public SwitchParameter Oldest { get { return _oldest; } + set { _oldest = value; } } - private bool _oldest = false; + private bool _oldest = false; // // Query builder constant strings @@ -361,8 +280,8 @@ public SwitchParameter Oldest private const string SelectCloser = ""; private const string suppressOpener = "*"; private const string suppressCloser = ""; - private const string propOpen = "["; - private const string propClose = "]"; + private const char propOpen = '['; + private const char propClose = ']'; private const string filePrefix = "file://"; private const string NamedDataTemplate = "((EventData[Data[@Name='{0}']='{1}']) or (UserData/*/{0}='{1}'))"; private const string DataTemplate = "(EventData/Data='{0}')"; @@ -378,14 +297,14 @@ public SwitchParameter Oldest // Other private members and constants // private ResourceManager _resourceMgr = null; - private Dictionary _providersByLogMap = new Dictionary(); + private readonly Dictionary _providersByLogMap = new(); private StringCollection _logNamesMatchingWildcard = null; - private StringCollection _resolvedPaths = new StringCollection(); + private readonly StringCollection _resolvedPaths = new(); - private List _accumulatedLogNames = new List(); - private List _accumulatedProviderNames = new List(); - private List _accumulatedFileNames = new List(); + private readonly List _accumulatedLogNames = new(); + private readonly List _accumulatedProviderNames = new(); + private readonly List _accumulatedFileNames = new(); private const uint MAX_EVENT_BATCH = 100; @@ -404,18 +323,16 @@ public SwitchParameter Oldest private const string hashkey_data_lc = "data"; private const string hashkey_supress_lc = "suppresshashfilter"; - /// - /// BeginProcessing() is invoked once per pipeline: we will load System.Core.dll here + /// BeginProcessing() is invoked once per pipeline: we will load System.Core.dll here. /// protected override void BeginProcessing() { _resourceMgr = Microsoft.PowerShell.Commands.Diagnostics.Common.CommonUtilities.GetResourceManager(); } - /// - /// EndProcessing() is invoked once per pipeline + /// EndProcessing() is invoked once per pipeline. /// protected override void EndProcessing() { @@ -438,7 +355,6 @@ protected override void EndProcessing() } } - /// /// ProcessRecord() override. /// This is the main entry point for the cmdlet. @@ -476,19 +392,18 @@ protected override void ProcessRecord() break; default: - WriteDebug(string.Format(CultureInfo.InvariantCulture, "Invalid parameter set name: {0}", ParameterSetName)); + WriteDebug(string.Create(CultureInfo.InvariantCulture, $"Invalid parameter set name: {ParameterSetName}")); break; } } - // // AccumulatePipelineCounters() accumulates log names in the pipeline scenario: // we do not want to construct a query until all the log names are supplied. // private void AccumulatePipelineLogNames() { - _accumulatedLogNames.AddRange(_logName); + _accumulatedLogNames.AddRange(LogName); } // @@ -497,7 +412,7 @@ private void AccumulatePipelineLogNames() // private void AccumulatePipelineProviderNames() { - _accumulatedProviderNames.AddRange(_logName); + _accumulatedProviderNames.AddRange(LogName); } // @@ -506,7 +421,7 @@ private void AccumulatePipelineProviderNames() // private void AccumulatePipelineFileNames() { - _accumulatedFileNames.AddRange(_logName); + _accumulatedFileNames.AddRange(LogName); } // @@ -531,8 +446,9 @@ private void ProcessGetLog() } else { - logQuery = new EventLogQuery(_logNamesMatchingWildcard[0], PathType.LogName, _filter); + logQuery = new EventLogQuery(_logNamesMatchingWildcard[0], PathType.LogName, FilterXPath); } + logQuery.Session = eventLogSession; logQuery.ReverseDirection = !_oldest; @@ -540,7 +456,6 @@ private void ProcessGetLog() } } - // // Process GetProviderSet parameter set // @@ -548,7 +463,7 @@ private void ProcessGetProvider() { using (EventLogSession eventLogSession = CreateSession()) { - FindProvidersByLogForWildcardPatterns(eventLogSession, _providerName); + FindProvidersByLogForWildcardPatterns(eventLogSession, ProviderName); if (_providersByLogMap.Count == 0) { @@ -558,7 +473,6 @@ private void ProcessGetProvider() return; } - EventLogQuery logQuery = null; if (_providersByLogMap.Count > 1) { @@ -574,17 +488,17 @@ private void ProcessGetProvider() foreach (string log in _providersByLogMap.Keys) { logQuery = new EventLogQuery(log, PathType.LogName, AddProviderPredicatesToFilter(_providersByLogMap[log])); - WriteVerbose(string.Format(CultureInfo.InvariantCulture, "Log {0} will be queried", log)); + WriteVerbose(string.Create(CultureInfo.InvariantCulture, $"Log {log} will be queried")); } } + logQuery.Session = eventLogSession; - logQuery.ReverseDirection = !_oldest; ; + logQuery.ReverseDirection = !_oldest; ReadEvents(logQuery); } } - // // Process ListLog parameter set // @@ -592,22 +506,21 @@ private void ProcessListLog() { using (EventLogSession eventLogSession = CreateSession()) { - foreach (string logPattern in _listLog) + foreach (string logPattern in ListLog) { bool bMatchFound = false; + WildcardPattern wildLogPattern = new(logPattern, WildcardOptions.IgnoreCase); foreach (string logName in eventLogSession.GetLogNames()) { - WildcardPattern wildLogPattern = new WildcardPattern(logPattern, WildcardOptions.IgnoreCase); - if (((!WildcardPattern.ContainsWildcardCharacters(logPattern)) - && string.Equals(logPattern, logName, StringComparison.CurrentCultureIgnoreCase)) + && string.Equals(logPattern, logName, StringComparison.OrdinalIgnoreCase)) || (wildLogPattern.IsMatch(logName))) { try { - EventLogConfiguration logObj = new EventLogConfiguration(logName, eventLogSession); + EventLogConfiguration logObj = new(logName, eventLogSession); // // Skip direct channels matching the wildcard unless -Force is present. @@ -622,7 +535,7 @@ private void ProcessListLog() EventLogInformation logInfoObj = eventLogSession.GetLogInformation(logName, PathType.LogName); - PSObject outputObj = new PSObject(logObj); + PSObject outputObj = new(logObj); outputObj.Properties.Add(new PSNoteProperty("FileSize", logInfoObj.FileSize)); outputObj.Properties.Add(new PSNoteProperty("IsLogFull", logInfoObj.IsLogFull)); @@ -639,16 +552,17 @@ private void ProcessListLog() string msg = string.Format(CultureInfo.InvariantCulture, _resourceMgr.GetString("LogInfoUnavailable"), logName, exc.Message); - Exception outerExc = new Exception(msg, exc); + Exception outerExc = new(msg, exc); WriteError(new ErrorRecord(outerExc, "LogInfoUnavailable", ErrorCategory.NotSpecified, null)); continue; } } } + if (!bMatchFound) { string msg = _resourceMgr.GetString("NoMatchingLogsFound"); - Exception exc = new Exception(string.Format(CultureInfo.InvariantCulture, msg, _computerName, logPattern)); + Exception exc = new(string.Format(CultureInfo.InvariantCulture, msg, ComputerName, logPattern)); WriteError(new ErrorRecord(exc, "NoMatchingLogsFound", ErrorCategory.ObjectNotFound, null)); } } @@ -662,22 +576,21 @@ private void ProcessListProvider() { using (EventLogSession eventLogSession = CreateSession()) { - foreach (string provPattern in _listProvider) + foreach (string provPattern in ListProvider) { bool bMatchFound = false; + WildcardPattern wildProvPattern = new(provPattern, WildcardOptions.IgnoreCase); foreach (string provName in eventLogSession.GetProviderNames()) { - WildcardPattern wildProvPattern = new WildcardPattern(provPattern, WildcardOptions.IgnoreCase); - if (((!WildcardPattern.ContainsWildcardCharacters(provPattern)) - && string.Equals(provPattern, provName, StringComparison.CurrentCultureIgnoreCase)) + && string.Equals(provPattern, provName, StringComparison.OrdinalIgnoreCase)) || (wildProvPattern.IsMatch(provName))) { try { - ProviderMetadata provObj = new ProviderMetadata(provName, eventLogSession, CultureInfo.CurrentCulture); + ProviderMetadata provObj = new(provName, eventLogSession, CultureInfo.CurrentCulture); WriteObject(provObj); bMatchFound = true; } @@ -686,7 +599,7 @@ private void ProcessListProvider() string msg = string.Format(CultureInfo.InvariantCulture, _resourceMgr.GetString("ProviderMetadataUnavailable"), provName, exc.Message); - Exception outerExc = new Exception(msg, exc); + Exception outerExc = new(msg, exc); WriteError(new ErrorRecord(outerExc, "ProviderMetadataUnavailable", ErrorCategory.NotSpecified, null)); continue; } @@ -696,8 +609,8 @@ private void ProcessListProvider() if (!bMatchFound) { string msg = string.Format(CultureInfo.InvariantCulture, _resourceMgr.GetString("NoMatchingProvidersFound"), - _computerName, provPattern); - Exception exc = new Exception(msg); + ComputerName, provPattern); + Exception exc = new(msg); WriteError(new ErrorRecord(exc, "NoMatchingProvidersFound", ErrorCategory.ObjectNotFound, null)); } } @@ -716,7 +629,7 @@ private void ProcessFilterXml() // // Do minimal parsing of xmlQuery to determine if any direct channels or ETL files are in it. // - XmlElement root = _xmlQuery.DocumentElement; + XmlElement root = FilterXml.DocumentElement; XmlNodeList queryNodes = root.SelectNodes("//Query//Select"); foreach (XmlNode queryNode in queryNodes) { @@ -738,7 +651,7 @@ private void ProcessFilterXml() } } - EventLogQuery logQuery = new EventLogQuery(null, PathType.LogName, _xmlQuery.InnerXml); + EventLogQuery logQuery = new(null, PathType.LogName, FilterXml.InnerXml); logQuery.Session = eventLogSession; logQuery.ReverseDirection = !_oldest; @@ -746,7 +659,6 @@ private void ProcessFilterXml() } } - // // Process FileSet parameter set // @@ -758,13 +670,13 @@ private void ProcessFile() // At this point, _path array contains paths that might have wildcards, // environment variables or PS drives. Let's resolve those. // - for (int i = 0; i < _path.Length; i++) + for (int i = 0; i < Path.Length; i++) { - StringCollection resolvedPaths = ValidateAndResolveFilePath(_path[i]); + StringCollection resolvedPaths = ValidateAndResolveFilePath(Path[i]); foreach (string resolvedPath in resolvedPaths) { _resolvedPaths.Add(resolvedPath); - WriteVerbose(string.Format(CultureInfo.InvariantCulture, "Found file {0}", resolvedPath)); + WriteVerbose(string.Create(CultureInfo.InvariantCulture, $"Found file {resolvedPath}")); } } @@ -781,8 +693,9 @@ private void ProcessFile() } else { - logQuery = new EventLogQuery(_resolvedPaths[0], PathType.FilePath, _filter); + logQuery = new EventLogQuery(_resolvedPaths[0], PathType.FilePath, FilterXPath); } + logQuery.Session = eventLogSession; logQuery.ReverseDirection = !_oldest; @@ -799,14 +712,13 @@ private void ProcessHashQuery() using (EventLogSession eventLogSession = CreateSession()) { - string query = BuildStructuredQuery(eventLogSession); if (query.Length == 0) { return; } - EventLogQuery logQuery = new EventLogQuery(null, PathType.FilePath, query); + EventLogQuery logQuery = new(null, PathType.FilePath, query); logQuery.Session = eventLogSession; logQuery.TolerateQueryErrors = true; logQuery.ReverseDirection = !_oldest; @@ -823,49 +735,48 @@ private EventLogSession CreateSession() { EventLogSession eventLogSession = null; - if (_computerName == string.Empty) + if (ComputerName == string.Empty) { // Set _computerName to "localhost" for future error messages, // but do not use it for the connection to avoid RPC overhead. - _computerName = "localhost"; + ComputerName = "localhost"; - if (_credential == PSCredential.Empty) + if (Credential == PSCredential.Empty) { return new EventLogSession(); } } - else if (_credential == PSCredential.Empty) + else if (Credential == PSCredential.Empty) { - return new EventLogSession(_computerName); + return new EventLogSession(ComputerName); } // If we are here, either both computer name and credential were passed initially, // or credential only - we will use it with "localhost" - NetworkCredential netCred = (NetworkCredential)_credential; - eventLogSession = new EventLogSession(_computerName, + NetworkCredential netCred = (NetworkCredential)Credential; + eventLogSession = new EventLogSession(ComputerName, netCred.Domain, netCred.UserName, - _credential.Password, + Credential.Password, SessionAuthentication.Default ); // // Force the destruction of cached password // - netCred.Password = ""; + netCred.Password = string.Empty; return eventLogSession; } - // // ReadEvents helper. // private void ReadEvents(EventLogQuery logQuery) { - using (EventLogReader readerObj = new EventLogReader(logQuery)) + using (EventLogReader readerObj = new(logQuery)) { - Int64 numEvents = 0; + long numEvents = 0; EventRecord evtObj = null; while (true) @@ -879,16 +790,18 @@ private void ReadEvents(EventLogQuery logQuery) WriteError(new ErrorRecord(exc, exc.Message, ErrorCategory.NotSpecified, null)); continue; } + if (evtObj == null) { break; } - if (_maxEvents != -1 && numEvents >= _maxEvents) + + if (MaxEvents != -1 && numEvents >= MaxEvents) { break; } - PSObject outputObj = new PSObject(evtObj); + PSObject outputObj = new(evtObj); string evtMessage = _resourceMgr.GetString("NoEventMessage"); try @@ -899,8 +812,8 @@ private void ReadEvents(EventLogQuery logQuery) { WriteError(new ErrorRecord(exc, exc.Message, ErrorCategory.NotSpecified, null)); } - outputObj.Properties.Add(new PSNoteProperty("Message", evtMessage)); + outputObj.Properties.Add(new PSNoteProperty("Message", evtMessage)); // // Enumerate the object one level to get to event payload @@ -912,20 +825,18 @@ private void ReadEvents(EventLogQuery logQuery) if (numEvents == 0) { string msg = _resourceMgr.GetString("NoMatchingEventsFound"); - Exception exc = new Exception(msg); + Exception exc = new(msg); WriteError(new ErrorRecord(exc, "NoMatchingEventsFound", ErrorCategory.ObjectNotFound, null)); } } } - - // // BuildStructuredQuery() builds a structured query from cmdlet arguments. // private string BuildStructuredQuery(EventLogSession eventLogSession) { - StringBuilder result = new StringBuilder(); + StringBuilder result = new(); switch (ParameterSetName) { @@ -945,20 +856,32 @@ private string BuildStructuredQuery(EventLogSession eventLogSession) string providerFilter = AddProviderPredicatesToFilter(_providersByLogMap[log]); result.AppendFormat(CultureInfo.InvariantCulture, queryTemplate, new object[] { queryId++, log, providerFilter }); } + result.Append(queryListClose); } + break; case "GetLogSet": { + const int WindowsEventLogAPILimit = 256; + if (_logNamesMatchingWildcard.Count > WindowsEventLogAPILimit) + { + string msg = _resourceMgr.GetString("LogCountLimitExceeded"); + Exception exc = new(string.Format(CultureInfo.InvariantCulture, msg, _logNamesMatchingWildcard.Count, WindowsEventLogAPILimit)); + ThrowTerminatingError(new ErrorRecord(exc, "LogCountLimitExceeded", ErrorCategory.LimitsExceeded, null)); + } + result.Append(queryListOpen); uint queryId = 0; foreach (string log in _logNamesMatchingWildcard) { - result.AppendFormat(CultureInfo.InvariantCulture, queryTemplate, new object[] { queryId++, log, _filter }); + result.AppendFormat(CultureInfo.InvariantCulture, queryTemplate, new object[] { queryId++, log, FilterXPath }); } + result.Append(queryListClose); } + break; case "FileSet": @@ -968,10 +891,12 @@ private string BuildStructuredQuery(EventLogSession eventLogSession) foreach (string filePath in _resolvedPaths) { string properFilePath = filePrefix + filePath; - result.AppendFormat(CultureInfo.InvariantCulture, queryTemplate, new object[] { queryId++, properFilePath, _filter }); + result.AppendFormat(CultureInfo.InvariantCulture, queryTemplate, new object[] { queryId++, properFilePath, FilterXPath }); } + result.Append(queryListClose); } + break; case "HashQuerySet": @@ -979,7 +904,7 @@ private string BuildStructuredQuery(EventLogSession eventLogSession) break; default: - WriteDebug(string.Format(CultureInfo.InvariantCulture, "Invalid parameter set name: {0}", ParameterSetName)); + WriteDebug(string.Create(CultureInfo.InvariantCulture, $"Invalid parameter set name: {ParameterSetName}")); break; } @@ -993,12 +918,12 @@ private string BuildStructuredQuery(EventLogSession eventLogSession) // private string BuildXPathFromHashTable(Hashtable hash) { - StringBuilder xpathString = new StringBuilder(""); + StringBuilder xpathString = new(string.Empty); bool bDateTimeHandled = false; foreach (string key in hash.Keys) { - string added = ""; + string added = string.Empty; switch (key.ToLowerInvariant()) { @@ -1057,8 +982,8 @@ private string BuildXPathFromHashTable(Hashtable hash) // // Fix Issue #2327 added = HandleNamedDataHashValue(key, hash[key]); - } + break; } @@ -1068,9 +993,9 @@ private string BuildXPathFromHashTable(Hashtable hash) { xpathString.Append(" and "); } + xpathString.Append(added); } - } return xpathString.ToString(); @@ -1082,16 +1007,16 @@ private string BuildXPathFromHashTable(Hashtable hash) // private string BuildStructuredQueryFromHashTable(EventLogSession eventLogSession) { - StringBuilder result = new StringBuilder(""); + StringBuilder result = new(string.Empty); result.Append(queryListOpen); uint queryId = 0; - foreach (Hashtable hash in _selector) + foreach (Hashtable hash in FilterHashtable) { - string xpathString = ""; - string xpathStringSuppress = ""; + string xpathString = string.Empty; + string xpathStringSuppress = string.Empty; CheckHashTableForQueryPathPresence(hash); @@ -1099,12 +1024,12 @@ private string BuildStructuredQueryFromHashTable(EventLogSession eventLogSession // Local queriedLogsQueryMap will hold names of logs or files to be queried // mapped to the actual query strings being built up. // - Dictionary queriedLogsQueryMap = new Dictionary(); + Dictionary queriedLogsQueryMap = new(); // // queriedLogsQueryMapSuppress is the same as queriedLogsQueryMap but for // - Dictionary queriedLogsQueryMapSuppress = new Dictionary(); + Dictionary queriedLogsQueryMapSuppress = new(); // // Process log, _path, or provider parameters first @@ -1115,10 +1040,10 @@ private string BuildStructuredQueryFromHashTable(EventLogSession eventLogSession // if (hash.ContainsKey(hashkey_logname_lc)) { - List logPatterns = new List(); + List logPatterns = new(); if (hash[hashkey_logname_lc] is Array) { - foreach (Object elt in (Array)hash[hashkey_logname_lc]) + foreach (object elt in (Array)hash[hashkey_logname_lc]) { logPatterns.Add(elt.ToString()); } @@ -1138,11 +1063,12 @@ private string BuildStructuredQueryFromHashTable(EventLogSession eventLogSession string.Format(CultureInfo.InvariantCulture, suppressOpener, queryId++, logName)); } } + if (hash.ContainsKey(hashkey_path_lc)) { if (hash[hashkey_path_lc] is Array) { - foreach (Object elt in (Array)hash[hashkey_path_lc]) + foreach (object elt in (Array)hash[hashkey_path_lc]) { StringCollection resolvedPaths = ValidateAndResolveFilePath(elt.ToString()); foreach (string resolvedPath in resolvedPaths) @@ -1166,12 +1092,13 @@ private string BuildStructuredQueryFromHashTable(EventLogSession eventLogSession } } } + if (hash.ContainsKey(hashkey_providername_lc)) { - List provPatterns = new List(); + List provPatterns = new(); if (hash[hashkey_providername_lc] is Array) { - foreach (Object elt in (Array)hash[hashkey_providername_lc]) + foreach (object elt in (Array)hash[hashkey_providername_lc]) { provPatterns.Add(elt.ToString()); } @@ -1201,7 +1128,7 @@ private string BuildStructuredQueryFromHashTable(EventLogSession eventLogSession } else { - List keysList = new List(queriedLogsQueryMap.Keys); + List keysList = new(queriedLogsQueryMap.Keys); bool bRemovedIrrelevantLogs = false; foreach (string queriedLog in keysList) { @@ -1231,7 +1158,7 @@ private string BuildStructuredQueryFromHashTable(EventLogSession eventLogSession if (bRemovedIrrelevantLogs && (queriedLogsQueryMap.Count == 0)) { string msg = string.Format(CultureInfo.InvariantCulture, _resourceMgr.GetString("LogsAndProvidersDontOverlap")); - Exception exc = new Exception(msg); + Exception exc = new(msg); WriteError(new ErrorRecord(exc, "LogsAndProvidersDontOverlap", ErrorCategory.InvalidArgument, null)); continue; } @@ -1256,8 +1183,7 @@ private string BuildStructuredQueryFromHashTable(EventLogSession eventLogSession // // Build xpath for // - Hashtable suppresshash = hash[hashkey_supress_lc] as Hashtable; - if (suppresshash != null) + if (hash[hashkey_supress_lc] is Hashtable suppresshash) { xpathStringSuppress = BuildXPathFromHashTable(suppresshash); } @@ -1274,7 +1200,7 @@ private string BuildStructuredQueryFromHashTable(EventLogSession eventLogSession string query = queriedLogsQueryMap[keyLogName]; result.Append(query); - if (query.EndsWith("*", StringComparison.OrdinalIgnoreCase)) + if (query.EndsWith('*')) { // // No provider predicate: just add the XPath string @@ -1293,6 +1219,7 @@ private string BuildStructuredQueryFromHashTable(EventLogSession eventLogSession { result.Append(" and ").Append(xpathString); } + result.Append(propClose); } @@ -1309,8 +1236,7 @@ private string BuildStructuredQueryFromHashTable(EventLogSession eventLogSession result.Append(queryCloser); } - } //end foreach hashtable - + } result.Append(queryListClose); @@ -1321,26 +1247,26 @@ private string BuildStructuredQueryFromHashTable(EventLogSession eventLogSession // HandleEventIdHashValue helper for hashtable structured query builder. // Constructs and returns EventId XPath portion as a string. // - private string HandleEventIdHashValue(Object value) + private static string HandleEventIdHashValue(object value) { - StringBuilder ret = new StringBuilder(); - Array idsArray = value as Array; - if (idsArray != null) + StringBuilder ret = new(); + if (value is Array idsArray) { - ret.Append("("); + ret.Append('('); for (int i = 0; i < idsArray.Length; i++) { - ret.Append(SystemEventIDTemplate).Append(idsArray.GetValue(i).ToString()).Append(")"); + ret.Append(SystemEventIDTemplate).Append(idsArray.GetValue(i).ToString()).Append(')'); if (i < (idsArray.Length - 1)) { ret.Append(" or "); } } - ret.Append(")"); + + ret.Append(')'); } else { - ret.Append(SystemEventIDTemplate).Append(value).Append(")"); + ret.Append(SystemEventIDTemplate).Append(value).Append(')'); } return ret.ToString(); @@ -1350,26 +1276,26 @@ private string HandleEventIdHashValue(Object value) // HandleLevelHashValue helper for hashtable structured query builder. // Constructs and returns Level XPath portion as a string. // - private string HandleLevelHashValue(Object value) + private static string HandleLevelHashValue(object value) { - StringBuilder ret = new StringBuilder(); - Array levelsArray = value as Array; - if (levelsArray != null) + StringBuilder ret = new(); + if (value is Array levelsArray) { - ret.Append("("); + ret.Append('('); for (int i = 0; i < levelsArray.Length; i++) { - ret.Append(SystemLevelTemplate).Append(levelsArray.GetValue(i).ToString()).Append(")"); + ret.Append(SystemLevelTemplate).Append(levelsArray.GetValue(i).ToString()).Append(')'); if (i < (levelsArray.Length - 1)) { ret.Append(" or "); } } - ret.Append(")"); + + ret.Append(')'); } else { - ret.Append(SystemLevelTemplate).Append(value).Append(")"); + ret.Append(SystemLevelTemplate).Append(value).Append(')'); } return ret.ToString(); @@ -1379,15 +1305,14 @@ private string HandleLevelHashValue(Object value) // HandleKeywordHashValue helper for hashtable structured query builder. // Constructs and returns Keyword XPath portion as a string. // - private string HandleKeywordHashValue(Object value) + private string HandleKeywordHashValue(object value) { - Int64 keywordsMask = 0; - Int64 keywordLong = 0; + long keywordsMask = 0; + long keywordLong = 0; - Array keywordArray = value as Array; - if (keywordArray != null) + if (value is Array keywordArray) { - foreach (Object keyword in keywordArray) + foreach (object keyword in keywordArray) { if (KeywordStringToInt64(keyword.ToString(), ref keywordLong)) { @@ -1399,8 +1324,9 @@ private string HandleKeywordHashValue(Object value) { if (!KeywordStringToInt64(value.ToString(), ref keywordLong)) { - return ""; + return string.Empty; } + keywordsMask |= keywordLong; } @@ -1413,7 +1339,7 @@ private string HandleKeywordHashValue(Object value) // Handles both SIDs and domain account names. // Writes an error and returns an empty string if the SID or account names are not valid. // - private string HandleContextHashValue(Object value) + private string HandleContextHashValue(object value) { SecurityIdentifier sidCandidate = null; try @@ -1429,34 +1355,33 @@ private string HandleContextHashValue(Object value) { try { - NTAccount acct = new NTAccount(value.ToString()); + NTAccount acct = new(value.ToString()); sidCandidate = (SecurityIdentifier)acct.Translate(typeof(SecurityIdentifier)); } catch (ArgumentException exc) { string msg = string.Format(CultureInfo.InvariantCulture, _resourceMgr.GetString("InvalidContext"), value.ToString()); - Exception outerExc = new Exception(msg, exc); + Exception outerExc = new(msg, exc); WriteError(new ErrorRecord(outerExc, "InvalidContext", ErrorCategory.InvalidArgument, null)); - return ""; + return string.Empty; } } return string.Format(CultureInfo.InvariantCulture, SystemSecurityTemplate, sidCandidate.ToString()); } - // // HandleStartTimeHashValue helper for hashtable structured query builder. // Constructs and returns TimeCreated XPath portion as a string. // NOTE that it also handles the hashtable "endtime" value (if supplied). // - private string HandleStartTimeHashValue(Object value, Hashtable hash) + private string HandleStartTimeHashValue(object value, Hashtable hash) { - StringBuilder ret = new StringBuilder(); - DateTime startTime = new DateTime(); + StringBuilder ret = new(); + DateTime startTime = new(); if (!StringToDateTime(value.ToString(), ref startTime)) { - return ""; + return string.Empty; } startTime = startTime.ToUniversalTime(); @@ -1464,10 +1389,10 @@ private string HandleStartTimeHashValue(Object value, Hashtable hash) if (hash.ContainsKey(hashkey_endtime_lc)) { - DateTime endTime = new DateTime(); + DateTime endTime = new(); if (!StringToDateTime(hash[hashkey_endtime_lc].ToString(), ref endTime)) { - return ""; + return string.Empty; } endTime = endTime.ToUniversalTime(); @@ -1488,19 +1413,18 @@ private string HandleStartTimeHashValue(Object value, Hashtable hash) return ret.ToString(); } - // // HandleEndTimeHashValue helper for hashtable structured query builder. // Constructs and returns TimeCreated XPath portion as a string. // NOTE that it also handles the hashtable "starttime" value (if supplied). // - private string HandleEndTimeHashValue(Object value, Hashtable hash) + private string HandleEndTimeHashValue(object value, Hashtable hash) { - StringBuilder ret = new StringBuilder(); - DateTime endTime = new DateTime(); + StringBuilder ret = new(); + DateTime endTime = new(); if (!StringToDateTime(value.ToString(), ref endTime)) { - return ""; + return string.Empty; } endTime = endTime.ToUniversalTime(); @@ -1509,10 +1433,10 @@ private string HandleEndTimeHashValue(Object value, Hashtable hash) if (hash.ContainsKey(hashkey_starttime_lc)) { - DateTime startTime = new DateTime(); + DateTime startTime = new(); if (!StringToDateTime(hash[hashkey_starttime_lc].ToString(), ref startTime)) { - return ""; + return string.Empty; } startTime = startTime.ToUniversalTime(); @@ -1538,13 +1462,12 @@ private string HandleEndTimeHashValue(Object value, Hashtable hash) // HandleDataHashValue helper for hashtable structured query builder. // Constructs and returns EventData/Data XPath portion as a string. // - private string HandleDataHashValue(Object value) + private static string HandleDataHashValue(object value) { - StringBuilder ret = new StringBuilder(); - Array dataArray = value as Array; - if (dataArray != null) + StringBuilder ret = new(); + if (value is Array dataArray) { - ret.Append("("); + ret.Append('('); for (int i = 0; i < dataArray.Length; i++) { ret.AppendFormat(CultureInfo.InvariantCulture, DataTemplate, dataArray.GetValue(i).ToString()); @@ -1553,7 +1476,8 @@ private string HandleDataHashValue(Object value) ret.Append(" or "); } } - ret.Append(")"); + + ret.Append(')'); } else { @@ -1563,19 +1487,17 @@ private string HandleDataHashValue(Object value) return ret.ToString(); } - // // HandleNamedDataHashValue helper for hashtable structured query builder. // Constructs and returns named event data field XPath portion as a string. // Fix Issue #2327 // - private string HandleNamedDataHashValue(String key, Object value) + private static string HandleNamedDataHashValue(string key, object value) { - StringBuilder ret = new StringBuilder(); - Array dataArray = value as Array; - if (dataArray != null) + StringBuilder ret = new(); + if (value is Array dataArray) { - ret.Append("("); + ret.Append('('); for (int i = 0; i < dataArray.Length; i++) { ret.AppendFormat(CultureInfo.InvariantCulture, @@ -1586,7 +1508,8 @@ private string HandleNamedDataHashValue(String key, Object value) ret.Append(" or "); } } - ret.Append(")"); + + ret.Append(')'); } else { @@ -1598,7 +1521,6 @@ private string HandleNamedDataHashValue(String key, Object value) return ret.ToString(); } - // // Helper checking whether at least one of log, _path, provider is specified. // It will ThrowTerminatingError in case none of those keys are present. @@ -1612,7 +1534,7 @@ private void CheckHashTableForQueryPathPresence(Hashtable hash) if (!isLogHash && !isProviderHash && !isPathHash) { string msg = _resourceMgr.GetString("LogProviderOrPathNeeded"); - Exception exc = new Exception(msg); + Exception exc = new(msg); ThrowTerminatingError(new ErrorRecord(exc, "LogProviderOrPathNeeded", ErrorCategory.InvalidArgument, null)); } } @@ -1628,7 +1550,7 @@ private void TerminateForNonEvtxFileWithoutOldest(string fileName) System.IO.Path.GetExtension(fileName).Equals(".evt", StringComparison.OrdinalIgnoreCase)) { string msg = _resourceMgr.GetString("SpecifyOldestForEtlEvt"); - Exception exc = new Exception(string.Format(CultureInfo.InvariantCulture, msg, fileName)); + Exception exc = new(string.Format(CultureInfo.InvariantCulture, msg, fileName)); ThrowTerminatingError(new ErrorRecord(exc, "SpecifyOldestForEtlEvt", ErrorCategory.InvalidArgument, fileName)); } } @@ -1648,7 +1570,7 @@ private bool ValidateLogName(string logName, EventLogSession eventLogSession) catch (EventLogNotFoundException) { string msg = _resourceMgr.GetString("NoMatchingLogsFound"); - Exception exc = new Exception(string.Format(CultureInfo.InvariantCulture, msg, _computerName, logName)); + Exception exc = new(string.Format(CultureInfo.InvariantCulture, msg, ComputerName, logName)); WriteError(new ErrorRecord(exc, "NoMatchingLogsFound", ErrorCategory.ObjectNotFound, logName)); return false; } @@ -1657,29 +1579,30 @@ private bool ValidateLogName(string logName, EventLogSession eventLogSession) string msg = string.Format(CultureInfo.InvariantCulture, _resourceMgr.GetString("LogInfoUnavailable"), logName, exc.Message); - Exception outerExc = new Exception(msg, exc); + Exception outerExc = new(msg, exc); WriteError(new ErrorRecord(outerExc, "LogInfoUnavailable", ErrorCategory.NotSpecified, null)); return false; } + if (!Oldest.IsPresent) { if (logObj.LogType == EventLogType.Debug || logObj.LogType == EventLogType.Analytical) { string msg = _resourceMgr.GetString("SpecifyOldestForLog"); - Exception exc = new Exception(string.Format(CultureInfo.InvariantCulture, msg, logName)); + Exception exc = new(string.Format(CultureInfo.InvariantCulture, msg, logName)); ThrowTerminatingError(new ErrorRecord(exc, "SpecifyOldestForLog", ErrorCategory.InvalidArgument, logName)); } } + return true; } - // // KeywordStringToInt64 helper converts a string to Int64. // Returns true and keyLong ref if successful. // Writes an error and returns false if keyString cannot be converted. // - private bool KeywordStringToInt64(string keyString, ref Int64 keyLong) + private bool KeywordStringToInt64(string keyString, ref long keyLong) { try { @@ -1688,7 +1611,7 @@ private bool KeywordStringToInt64(string keyString, ref Int64 keyLong) catch (Exception exc) { string msg = _resourceMgr.GetString("KeywordLongExpected"); - Exception outerExc = new Exception(string.Format(CultureInfo.InvariantCulture, msg, keyString), exc); + Exception outerExc = new(string.Format(CultureInfo.InvariantCulture, msg, keyString), exc); WriteError(new ErrorRecord(outerExc, "KeywordLongExpected", ErrorCategory.InvalidArgument, null)); return false; } @@ -1710,7 +1633,7 @@ private bool StringToDateTime(string dtString, ref DateTime dt) catch (FormatException exc) { string msg = _resourceMgr.GetString("DateTimeExpected"); - Exception outerExc = new Exception(string.Format(CultureInfo.InvariantCulture, msg, dtString), exc); + Exception outerExc = new(string.Format(CultureInfo.InvariantCulture, msg, dtString), exc); WriteError(new ErrorRecord(outerExc, "DateTimeExpected", ErrorCategory.InvalidArgument, null)); return false; } @@ -1726,7 +1649,7 @@ private bool StringToDateTime(string dtString, ref DateTime dt) // private StringCollection ValidateAndResolveFilePath(string path) { - StringCollection retColl = new StringCollection(); + StringCollection retColl = new(); Collection resolvedPathSubset = null; try @@ -1735,27 +1658,27 @@ private StringCollection ValidateAndResolveFilePath(string path) } catch (PSNotSupportedException notSupported) { - WriteError(new ErrorRecord(notSupported, "", ErrorCategory.ObjectNotFound, path)); + WriteError(new ErrorRecord(notSupported, string.Empty, ErrorCategory.ObjectNotFound, path)); return retColl; } catch (System.Management.Automation.DriveNotFoundException driveNotFound) { - WriteError(new ErrorRecord(driveNotFound, "", ErrorCategory.ObjectNotFound, path)); + WriteError(new ErrorRecord(driveNotFound, string.Empty, ErrorCategory.ObjectNotFound, path)); return retColl; } catch (ProviderNotFoundException providerNotFound) { - WriteError(new ErrorRecord(providerNotFound, "", ErrorCategory.ObjectNotFound, path)); + WriteError(new ErrorRecord(providerNotFound, string.Empty, ErrorCategory.ObjectNotFound, path)); return retColl; } catch (ItemNotFoundException pathNotFound) { - WriteError(new ErrorRecord(pathNotFound, "", ErrorCategory.ObjectNotFound, path)); + WriteError(new ErrorRecord(pathNotFound, string.Empty, ErrorCategory.ObjectNotFound, path)); return retColl; } catch (Exception exc) { - WriteError(new ErrorRecord(exc, "", ErrorCategory.ObjectNotFound, path)); + WriteError(new ErrorRecord(exc, string.Empty, ErrorCategory.ObjectNotFound, path)); return retColl; } @@ -1767,7 +1690,7 @@ private StringCollection ValidateAndResolveFilePath(string path) if (pi.Provider.Name != "FileSystem") { string msg = _resourceMgr.GetString("NotAFileSystemPath"); - Exception exc = new Exception(string.Format(CultureInfo.InvariantCulture, msg, path)); + Exception exc = new(string.Format(CultureInfo.InvariantCulture, msg, path)); WriteError(new ErrorRecord(exc, "NotAFileSystemPath", ErrorCategory.InvalidArgument, path)); continue; } @@ -1784,9 +1707,10 @@ private StringCollection ValidateAndResolveFilePath(string path) if (!WildcardPattern.ContainsWildcardCharacters(path)) { string msg = _resourceMgr.GetString("NotALogFile"); - Exception exc = new Exception(string.Format(CultureInfo.InvariantCulture, msg, pi.ProviderPath)); + Exception exc = new(string.Format(CultureInfo.InvariantCulture, msg, pi.ProviderPath)); WriteError(new ErrorRecord(exc, "NotALogFile", ErrorCategory.InvalidArgument, path)); } + continue; } @@ -1805,28 +1729,27 @@ private StringCollection ValidateAndResolveFilePath(string path) // private void CheckHashTablesForNullValues() { - foreach (Hashtable hash in _selector) + foreach (Hashtable hash in FilterHashtable) { foreach (string key in hash.Keys) { - Object value = hash[key]; + object value = hash[key]; if (value == null) { string msg = _resourceMgr.GetString("NullNotAllowedInHashtable"); - Exception exc = new Exception(string.Format(CultureInfo.InvariantCulture, msg, key)); + Exception exc = new(string.Format(CultureInfo.InvariantCulture, msg, key)); ThrowTerminatingError(new ErrorRecord(exc, "NullNotAllowedInHashtable", ErrorCategory.InvalidArgument, key)); } else { - Array eltArray = value as Array; - if (eltArray != null) + if (value is Array eltArray) { - foreach (Object elt in eltArray) + foreach (object elt in eltArray) { if (elt == null) { string msg = _resourceMgr.GetString("NullNotAllowedInHashtable"); - Exception exc = new Exception(string.Format(CultureInfo.InvariantCulture, msg, key)); + Exception exc = new(string.Format(CultureInfo.InvariantCulture, msg, key)); ThrowTerminatingError(new ErrorRecord(exc, "NullNotAllowedInHashtable", ErrorCategory.InvalidArgument, key)); } } @@ -1847,13 +1770,13 @@ private string AddProviderPredicatesToFilter(StringCollection providers) { if (providers.Count == 0) { - return _filter; + return FilterXPath; } - string ret = _filter; + string ret = FilterXPath; string predicate = BuildProvidersPredicate(providers); - if (_filter.Equals("*", StringComparison.OrdinalIgnoreCase)) + if (FilterXPath.Equals("*", StringComparison.OrdinalIgnoreCase)) { ret += "[" + predicate + "]"; } @@ -1862,7 +1785,7 @@ private string AddProviderPredicatesToFilter(StringCollection providers) // // Extend the XPath provided in the _filter // - int lastPredClose = _filter.LastIndexOf(']'); + int lastPredClose = FilterXPath.LastIndexOf(']'); if (lastPredClose == -1) { ret += "[" + predicate + "]"; @@ -1876,34 +1799,33 @@ private string AddProviderPredicatesToFilter(StringCollection providers) return ret; } - // // BuildProvidersPredicate() builds a predicate expression like: // "System/Provider[@Name='a' or @Name='b']" // for all provider names specified in the "providers" argument. // - private string BuildProvidersPredicate(StringCollection providers) + private static string BuildProvidersPredicate(StringCollection providers) { if (providers.Count == 0) { - return ""; + return string.Empty; } - StringBuilder predicate = new StringBuilder("System/Provider["); + StringBuilder predicate = new("System/Provider["); for (int i = 0; i < providers.Count; i++) { - predicate.Append("@Name='").Append(providers[i]).Append("'"); + predicate.Append("@Name='").Append(providers[i]).Append('\''); if (i < (providers.Count - 1)) { predicate.Append(" or "); } } - predicate.Append("]"); + + predicate.Append(']'); return predicate.ToString(); } - // // BuildAllProvidersPredicate() builds a predicate expression like: // "System/Provider[@Name='a' or @Name='b']" @@ -1915,12 +1837,12 @@ private string BuildAllProvidersPredicate() { if (_providersByLogMap.Count == 0) { - return ""; + return string.Empty; } - StringBuilder predicate = new StringBuilder("System/Provider["); + StringBuilder predicate = new("System/Provider["); - List uniqueProviderNames = new List(); + List uniqueProviderNames = new(); foreach (string logKey in _providersByLogMap.Keys) { @@ -1936,19 +1858,18 @@ private string BuildAllProvidersPredicate() for (int i = 0; i < uniqueProviderNames.Count; i++) { - predicate.Append("@Name='").Append(uniqueProviderNames[i]).Append("'"); + predicate.Append("@Name='").Append(uniqueProviderNames[i]).Append('\''); if (i < uniqueProviderNames.Count - 1) { predicate.Append(" or "); } } - predicate.Append("]"); + predicate.Append(']'); return predicate.ToString(); } - // // AddLogsForProviderToInternalMap helper. // Retrieves log names to which _providerName writes. @@ -1960,7 +1881,7 @@ private void AddLogsForProviderToInternalMap(EventLogSession eventLogSession, st { try { - ProviderMetadata providerMetadata = new ProviderMetadata(providerName, eventLogSession, CultureInfo.CurrentCulture); + ProviderMetadata providerMetadata = new(providerName, eventLogSession, CultureInfo.CurrentCulture); System.Collections.IEnumerable logLinks = providerMetadata.LogLinks; @@ -1972,7 +1893,7 @@ private void AddLogsForProviderToInternalMap(EventLogSession eventLogSession, st // Skip direct ETW channels unless -force is present. // Error out for direct channels unless -oldest is present. // - EventLogConfiguration logObj = new EventLogConfiguration(logLink.LogName, eventLogSession); + EventLogConfiguration logObj = new(logLink.LogName, eventLogSession); if (logObj.LogType == EventLogType.Debug || logObj.LogType == EventLogType.Analytical) { if (!Force.IsPresent) @@ -1985,13 +1906,12 @@ private void AddLogsForProviderToInternalMap(EventLogSession eventLogSession, st WriteVerbose(string.Format(CultureInfo.InvariantCulture, _resourceMgr.GetString("ProviderLogLink"), providerName, logLink.LogName)); - StringCollection provColl = new StringCollection(); + StringCollection provColl = new(); provColl.Add(providerName.ToLowerInvariant()); _providersByLogMap.Add(logLink.LogName.ToLowerInvariant(), provColl); } else - { // // Log is there: add provider, if needed @@ -2012,7 +1932,7 @@ private void AddLogsForProviderToInternalMap(EventLogSession eventLogSession, st string msg = string.Format(CultureInfo.InvariantCulture, _resourceMgr.GetString("ProviderMetadataUnavailable"), providerName, exc.Message); - Exception outerExc = new Exception(msg, exc); + Exception outerExc = new(msg, exc); WriteError(new ErrorRecord(outerExc, "ProviderMetadataUnavailable", ErrorCategory.NotSpecified, null)); return; } @@ -2038,12 +1958,12 @@ private void FindLogNamesMatchingWildcards(EventLogSession eventLogSession, IEnu foreach (string logPattern in logPatterns) { bool bMatched = false; + WildcardPattern wildLogPattern = new(logPattern, WildcardOptions.IgnoreCase); + foreach (string actualLogName in eventLogSession.GetLogNames()) { - WildcardPattern wildLogPattern = new WildcardPattern(logPattern, WildcardOptions.IgnoreCase); - if (((!WildcardPattern.ContainsWildcardCharacters(logPattern)) - && (logPattern.Equals(actualLogName, StringComparison.CurrentCultureIgnoreCase))) + && (logPattern.Equals(actualLogName, StringComparison.OrdinalIgnoreCase))) || (wildLogPattern.IsMatch(actualLogName))) { @@ -2061,7 +1981,7 @@ private void FindLogNamesMatchingWildcards(EventLogSession eventLogSession, IEnu string msg = string.Format(CultureInfo.InvariantCulture, _resourceMgr.GetString("LogInfoUnavailable"), actualLogName, exc.Message); - Exception outerExc = new Exception(msg, exc); + Exception outerExc = new(msg, exc); WriteError(new ErrorRecord(outerExc, "LogInfoUnavailable", ErrorCategory.NotSpecified, null)); continue; } @@ -2080,13 +2000,15 @@ private void FindLogNamesMatchingWildcards(EventLogSession eventLogSession, IEnu { _logNamesMatchingWildcard.Add(actualLogName.ToLowerInvariant()); } + bMatched = true; } } + if (!bMatched) { string msg = _resourceMgr.GetString("NoMatchingLogsFound"); - Exception exc = new Exception(string.Format(CultureInfo.InvariantCulture, msg, _computerName, logPattern)); + Exception exc = new(string.Format(CultureInfo.InvariantCulture, msg, ComputerName, logPattern)); WriteError(new ErrorRecord(exc, "NoMatchingLogsFound", ErrorCategory.ObjectNotFound, logPattern)); } } @@ -2105,29 +2027,28 @@ private void FindProvidersByLogForWildcardPatterns(EventLogSession eventLogSessi foreach (string provPattern in providerPatterns) { bool bMatched = false; + WildcardPattern wildProvPattern = new(provPattern, WildcardOptions.IgnoreCase); + foreach (string provName in eventLogSession.GetProviderNames()) { - WildcardPattern wildProvPattern = new WildcardPattern(provPattern, WildcardOptions.IgnoreCase); - if (((!WildcardPattern.ContainsWildcardCharacters(provPattern)) - && (provPattern.Equals(provName, StringComparison.CurrentCultureIgnoreCase))) + && (provPattern.Equals(provName, StringComparison.OrdinalIgnoreCase))) || (wildProvPattern.IsMatch(provName))) { - WriteVerbose(string.Format(CultureInfo.InvariantCulture, "Found matching provider: {0}", provName)); + WriteVerbose(string.Create(CultureInfo.InvariantCulture, $"Found matching provider: {provName}")); AddLogsForProviderToInternalMap(eventLogSession, provName); bMatched = true; } } + if (!bMatched) { string msg = _resourceMgr.GetString("NoMatchingProvidersFound"); - Exception exc = new Exception(string.Format(CultureInfo.InvariantCulture, msg, _computerName, provPattern)); + Exception exc = new(string.Format(CultureInfo.InvariantCulture, msg, ComputerName, provPattern)); WriteError(new ErrorRecord(exc, "NoMatchingProvidersFound", ErrorCategory.ObjectNotFound, provPattern)); } } } } } - - diff --git a/src/Microsoft.PowerShell.Commands.Diagnostics/GetEventSnapin.cs b/src/Microsoft.PowerShell.Commands.Diagnostics/GetEventSnapin.cs index da6a77d4dd8..a4693c454c0 100644 --- a/src/Microsoft.PowerShell.Commands.Diagnostics/GetEventSnapin.cs +++ b/src/Microsoft.PowerShell.Commands.Diagnostics/GetEventSnapin.cs @@ -1,13 +1,11 @@ -// -// Copyright (c) 2007 Microsoft Corporation. All rights reserved. -// - +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. using System; using System.Collections.Generic; -using System.Text; -using System.Management.Automation; using System.ComponentModel; +using System.Management.Automation; +using System.Text; namespace Microsoft.PowerShell.Commands { @@ -83,7 +81,7 @@ public override string DescriptionResource } /// - /// Get type files to be used for this mshsnapin. + /// Get type files to be used for this PSSnapin. /// public override string[] Types { @@ -92,10 +90,11 @@ public override string[] Types return _types; } } + private string[] _types = new string[] { "getevent.types.ps1xml" }; /// - /// Get format files to be used for this mshsnapin. + /// Get format files to be used for this PSSnapin. /// public override string[] Formats { diff --git a/src/Microsoft.PowerShell.Commands.Diagnostics/ImportCounterCommand.cs b/src/Microsoft.PowerShell.Commands.Diagnostics/ImportCounterCommand.cs index b95103d8a38..729334d0605 100644 --- a/src/Microsoft.PowerShell.Commands.Diagnostics/ImportCounterCommand.cs +++ b/src/Microsoft.PowerShell.Commands.Diagnostics/ImportCounterCommand.cs @@ -1,32 +1,30 @@ -// -// Copyright (c) 2008 Microsoft Corporation. All rights reserved. -// - +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. using System; -using System.Text; -using System.IO; -using System.Xml; -using System.Net; -using System.Management.Automation; -using System.ComponentModel; -using System.Reflection; -using System.Globalization; -using System.Management.Automation.Runspaces; using System.Collections; -using System.Collections.Specialized; using System.Collections.Generic; using System.Collections.ObjectModel; +using System.Collections.Specialized; +using System.ComponentModel; using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.IO; +using System.Management.Automation; +using System.Management.Automation.Runspaces; +using System.Net; +using System.Reflection; +using System.Resources; using System.Security; using System.Security.Principal; -using System.Resources; +using System.Text; using System.Threading; -using System.Diagnostics.CodeAnalysis; -using Microsoft.Powershell.Commands.GetCounter.PdhNative; -using Microsoft.PowerShell.Commands.GetCounter; -using Microsoft.PowerShell.Commands.Diagnostics.Common; +using System.Xml; +using Microsoft.PowerShell.Commands.Diagnostics.Common; +using Microsoft.PowerShell.Commands.GetCounter; +using Microsoft.Powershell.Commands.GetCounter.PdhNative; namespace Microsoft.PowerShell.Commands { @@ -54,15 +52,16 @@ public sealed class ImportCounterCommand : PSCmdlet public string[] Path { get { return _path; } + set { _path = value; } } + private string[] _path; private StringCollection _resolvedPaths = new StringCollection(); private List _accumulatedFileNames = new List(); - // // ListSet parameter // @@ -80,10 +79,11 @@ public string[] Path public string[] ListSet { get { return _listSet; } + set { _listSet = value; } } - private string[] _listSet = new string[0]; + private string[] _listSet = Array.Empty(); // // StartTime parameter @@ -96,10 +96,11 @@ public string[] ListSet public DateTime StartTime { get { return _startTime; } + set { _startTime = value; } } - private DateTime _startTime = DateTime.MinValue; + private DateTime _startTime = DateTime.MinValue; // // EndTime parameter @@ -112,10 +113,11 @@ public DateTime StartTime public DateTime EndTime { get { return _endTime; } + set { _endTime = value; } } - private DateTime _endTime = DateTime.MaxValue; + private DateTime _endTime = DateTime.MaxValue; // // Counter parameter @@ -133,9 +135,11 @@ public DateTime EndTime public string[] Counter { get { return _counter; } + set { _counter = value; } } - private string[] _counter = new string[0]; + + private string[] _counter = Array.Empty(); // // Summary switch @@ -144,8 +148,10 @@ public string[] Counter public SwitchParameter Summary { get { return _summary; } + set { _summary = value; } } + private SwitchParameter _summary; // @@ -161,10 +167,11 @@ public SwitchParameter Summary public Int64 MaxSamples { get { return _maxSamples; } + set { _maxSamples = value; } } - private Int64 _maxSamples = KEEP_ON_SAMPLING; + private Int64 _maxSamples = KEEP_ON_SAMPLING; private ResourceManager _resourceMgr = null; @@ -172,7 +179,6 @@ public Int64 MaxSamples private bool _stopping = false; - // // AccumulatePipelineFileNames() accumulates counter file paths in the pipeline scenario: // we do not want to construct a Pdh query until all the file names are supplied. @@ -182,7 +188,6 @@ private void AccumulatePipelineFileNames() _accumulatedFileNames.AddRange(_path); } - // // BeginProcessing() is invoked once per pipeline // @@ -196,7 +201,7 @@ protected override void BeginProcessing() throw new PlatformNotSupportedException(); } - // PowerShell Core requires at least Windows 7, + // PowerShell 7 requires at least Windows 7, // so no version test is needed _pdhHelper = new PdhHelper(false); #else @@ -217,6 +222,7 @@ protected override void EndProcessing() { return; } + ValidateFilePaths(); switch (ParameterSetName) @@ -234,14 +240,13 @@ protected override void EndProcessing() break; default: - Debug.Assert(false, string.Format(CultureInfo.InvariantCulture, "Invalid parameter set name: {0}", ParameterSetName)); + Debug.Assert(false, $"Invalid parameter set name: {ParameterSetName}"); break; } _pdhHelper.Dispose(); } - // // Handle Control-C // @@ -251,7 +256,6 @@ protected override void StopProcessing() _pdhHelper.Dispose(); } - // // ProcessRecord() override. // This is the main entry point for the cmdlet. @@ -331,7 +335,6 @@ private void ProcessListSet() continue; } - StringCollection counterSetCounters = new StringCollection(); StringCollection counterSetInstances = new StringCollection(); @@ -360,7 +363,7 @@ private void ProcessListSet() { categoryType = PerformanceCounterCategoryType.MultiInstance; } - else //if (counterSetInstances.Count == 1) //??? + else // if (counterSetInstances.Count == 1) //??? { categoryType = PerformanceCounterCategoryType.SingleInstance; } @@ -371,6 +374,7 @@ private void ProcessListSet() WriteObject(setObj); bMatched = true; } + if (!bMatched) { string msg = _resourceMgr.GetString("NoMatchingCounterSetsInFile"); @@ -432,9 +436,11 @@ private void ProcessGetCounter() continue; } + validPaths.Add(expandedPath); } } + if (validPaths.Count == 0) { return; @@ -488,6 +494,7 @@ private void ProcessGetCounter() { break; } + if (res != 0 && res != PdhResults.PDH_INVALID_DATA) { ReportPdhError(res, false); @@ -508,7 +515,6 @@ private void ProcessGetCounter() } } - // // ValidateFilePaths() helper. // Validates the _resolvedPaths: present for all parametersets. @@ -525,9 +531,9 @@ private void ValidateFilePaths() WriteVerbose(fileName); string curExtension = System.IO.Path.GetExtension(fileName); - if (!curExtension.Equals(".blg", StringComparison.CurrentCultureIgnoreCase) - && !curExtension.Equals(".csv", StringComparison.CurrentCultureIgnoreCase) - && !curExtension.Equals(".tsv", StringComparison.CurrentCultureIgnoreCase)) + if (!curExtension.Equals(".blg", StringComparison.OrdinalIgnoreCase) + && !curExtension.Equals(".csv", StringComparison.OrdinalIgnoreCase) + && !curExtension.Equals(".tsv", StringComparison.OrdinalIgnoreCase)) { string msg = string.Format(CultureInfo.InvariantCulture, _resourceMgr.GetString("CounterNotALogFile"), fileName); Exception exc = new Exception(msg); @@ -535,7 +541,7 @@ private void ValidateFilePaths() return; } - if (!curExtension.Equals(firstExt, StringComparison.CurrentCultureIgnoreCase)) + if (!curExtension.Equals(firstExt, StringComparison.OrdinalIgnoreCase)) { string msg = string.Format(CultureInfo.InvariantCulture, _resourceMgr.GetString("CounterNoMixedLogTypes"), fileName); Exception exc = new Exception(msg); @@ -544,7 +550,7 @@ private void ValidateFilePaths() } } - if (firstExt.Equals(".blg", StringComparison.CurrentCultureIgnoreCase)) + if (firstExt.Equals(".blg", StringComparison.OrdinalIgnoreCase)) { if (_resolvedPaths.Count > 32) { @@ -563,7 +569,6 @@ private void ValidateFilePaths() } } - // // ResolveFilePath helper. // Returns a string collection of resolved file paths. @@ -583,27 +588,27 @@ private bool ResolveFilePaths() } catch (PSNotSupportedException notSupported) { - WriteError(new ErrorRecord(notSupported, "", ErrorCategory.ObjectNotFound, origPath)); + WriteError(new ErrorRecord(notSupported, string.Empty, ErrorCategory.ObjectNotFound, origPath)); continue; } catch (System.Management.Automation.DriveNotFoundException driveNotFound) { - WriteError(new ErrorRecord(driveNotFound, "", ErrorCategory.ObjectNotFound, origPath)); + WriteError(new ErrorRecord(driveNotFound, string.Empty, ErrorCategory.ObjectNotFound, origPath)); continue; } catch (ProviderNotFoundException providerNotFound) { - WriteError(new ErrorRecord(providerNotFound, "", ErrorCategory.ObjectNotFound, origPath)); + WriteError(new ErrorRecord(providerNotFound, string.Empty, ErrorCategory.ObjectNotFound, origPath)); continue; } catch (ItemNotFoundException pathNotFound) { - WriteError(new ErrorRecord(pathNotFound, "", ErrorCategory.ObjectNotFound, origPath)); + WriteError(new ErrorRecord(pathNotFound, string.Empty, ErrorCategory.ObjectNotFound, origPath)); continue; } catch (Exception exc) { - WriteError(new ErrorRecord(exc, "", ErrorCategory.ObjectNotFound, origPath)); + WriteError(new ErrorRecord(exc, string.Empty, ErrorCategory.ObjectNotFound, origPath)); continue; } @@ -635,6 +640,7 @@ private void ReportPdhError(uint res, bool bTerminate) { msg = string.Format(CultureInfo.InvariantCulture, _resourceMgr.GetString("CounterApiError"), res); } + Exception exc = new Exception(msg); if (bTerminate) { @@ -673,5 +679,3 @@ private void WriteSampleSetObject(PerformanceCounterSampleSet set, bool firstSet } } } - - diff --git a/src/Microsoft.PowerShell.Commands.Diagnostics/Microsoft.PowerShell.Commands.Diagnostics.csproj b/src/Microsoft.PowerShell.Commands.Diagnostics/Microsoft.PowerShell.Commands.Diagnostics.csproj index 667dc977d9a..f63196d3645 100644 --- a/src/Microsoft.PowerShell.Commands.Diagnostics/Microsoft.PowerShell.Commands.Diagnostics.csproj +++ b/src/Microsoft.PowerShell.Commands.Diagnostics/Microsoft.PowerShell.Commands.Diagnostics.csproj @@ -1,15 +1,14 @@ - - - - + + - PowerShell Core's Microsoft.PowerShell.Commands.Diagnostics project - $(NoWarn);CS1591 + PowerShell's Microsoft.PowerShell.Commands.Diagnostics project + $(NoWarn);CS1591;CA1416 Microsoft.PowerShell.Commands.Diagnostics + @@ -17,28 +16,21 @@ + + + + + + + - - - - + + - - portable - - - - $(DefineConstants);UNIX - - - - full - - diff --git a/src/Microsoft.PowerShell.Commands.Diagnostics/NewWinEventCommand.cs b/src/Microsoft.PowerShell.Commands.Diagnostics/NewWinEventCommand.cs index 16529125f3d..cdddba939f3 100644 --- a/src/Microsoft.PowerShell.Commands.Diagnostics/NewWinEventCommand.cs +++ b/src/Microsoft.PowerShell.Commands.Diagnostics/NewWinEventCommand.cs @@ -1,26 +1,24 @@ -// -// Copyright (c) 2007 Microsoft Corporation. All rights reserved. -// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. using System; -using System.Management.Automation; -using System.Globalization; -using System.Reflection; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Diagnostics.Eventing; using System.Diagnostics.Eventing.Reader; +using System.Globalization; +using System.IO; +using System.Management.Automation; using System.Resources; -using System.Diagnostics.CodeAnalysis; -using System.Collections.Generic; using System.Xml; -using System.IO; namespace Microsoft.PowerShell.Commands { - /// + /// /// Class that implements the New-WinEvent cmdlet. /// This cmdlet writes a new Etw event using the provider specified in parameter. - /// - [Cmdlet(VerbsCommon.New, "WinEvent", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=217469")] + /// + [Cmdlet(VerbsCommon.New, "WinEvent", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2096808")] public sealed class NewWinEventCommand : PSCmdlet { private ProviderMetadata _providerMetadata; @@ -28,28 +26,17 @@ public sealed class NewWinEventCommand : PSCmdlet private const string TemplateTag = "template"; private const string DataTag = "data"; - private ResourceManager _resourceMgr = Microsoft.PowerShell.Commands.Diagnostics.Common.CommonUtilities.GetResourceManager(); + private readonly ResourceManager _resourceMgr = Microsoft.PowerShell.Commands.Diagnostics.Common.CommonUtilities.GetResourceManager(); /// - /// ProviderName + /// ProviderName. /// [Parameter( Position = 0, Mandatory = true, ParameterSetName = ParameterAttribute.AllParameterSets)] - public string ProviderName - { - get - { - return _providerName; - } - set - { - _providerName = value; - } - } - private string _providerName; + public string ProviderName { get; set; } /// /// Id (EventId defined in manifest file) @@ -64,16 +51,17 @@ public int Id { return _id; } + set { _id = value; _idSpecified = true; } } + private int _id; private bool _idSpecified = false; - /// /// Version (event version) /// @@ -86,18 +74,19 @@ public byte Version { return _version; } + set { _version = value; _versionSpecified = true; } } + private byte _version; private bool _versionSpecified = false; - /// - /// Event Payload + /// Event Payload. /// [Parameter( Position = 2, @@ -107,21 +96,10 @@ public byte Version SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Target = "Microsoft.PowerShell.Commands", Justification = "A string[] is required here because that is the type Powershell supports")] - public object[] Payload - { - get - { - return _payload; - } - set - { - _payload = value; - } - } - private object[] _payload; + public object[] Payload { get; set; } /// - /// BeginProcessing + /// BeginProcessing. /// protected override void BeginProcessing() { @@ -133,16 +111,16 @@ protected override void BeginProcessing() private void LoadProvider() { - if (string.IsNullOrEmpty(_providerName)) + if (string.IsNullOrEmpty(ProviderName)) { throw new ArgumentException(string.Format(CultureInfo.InvariantCulture, _resourceMgr.GetString("ProviderNotSpecified")), "ProviderName"); } - using (EventLogSession session = new EventLogSession()) + using (EventLogSession session = new()) { foreach (string providerName in session.GetProviderNames()) { - if (string.Equals(providerName, _providerName, StringComparison.OrdinalIgnoreCase)) + if (string.Equals(providerName, ProviderName, StringComparison.OrdinalIgnoreCase)) { try { @@ -153,6 +131,7 @@ private void LoadProvider() string msg = string.Format(CultureInfo.InvariantCulture, _resourceMgr.GetString("ProviderMetadataUnavailable"), providerName, exc.Message); throw new Exception(msg, exc); } + break; } } @@ -160,7 +139,7 @@ private void LoadProvider() if (_providerMetadata == null) { - string msg = string.Format(CultureInfo.InvariantCulture, _resourceMgr.GetString("NoProviderFound"), _providerName); + string msg = string.Format(CultureInfo.InvariantCulture, _resourceMgr.GetString("NoProviderFound"), ProviderName); throw new ArgumentException(msg); } } @@ -169,7 +148,7 @@ private void LoadEventDescriptor() { if (_idSpecified) { - List matchedEvents = new List(); + List matchedEvents = new(); foreach (EventMetadata emd in _providerMetadata.Events) { if (emd.Id == _id) @@ -183,7 +162,7 @@ private void LoadEventDescriptor() string msg = string.Format(CultureInfo.InvariantCulture, _resourceMgr.GetString("IncorrectEventId"), _id, - _providerName); + ProviderName); throw new EventWriteException(msg); } @@ -204,13 +183,14 @@ private void LoadEventDescriptor() break; } } + if (matchedEvent == null) { string msg = string.Format(CultureInfo.InvariantCulture, _resourceMgr.GetString("IncorrectEventVersion"), _version, _id, - _providerName); + ProviderName); throw new EventWriteException(msg); } @@ -220,7 +200,7 @@ private void LoadEventDescriptor() string msg = string.Format(CultureInfo.InvariantCulture, _resourceMgr.GetString("VersionNotSpecified"), _id, - _providerName); + ProviderName); throw new EventWriteException(msg); } @@ -239,16 +219,14 @@ private bool VerifyTemplate(EventMetadata emd) { if (emd.Template != null) { - XmlReaderSettings readerSettings = new XmlReaderSettings + XmlReaderSettings readerSettings = new() { CheckCharacters = false, IgnoreComments = true, IgnoreProcessingInstructions = true, MaxCharactersInDocument = 0, // no limit ConformanceLevel = ConformanceLevel.Fragment, -#if !CORECLR - XmlResolver = null, -#endif + XmlResolver = null }; int definedParameterCount = 0; @@ -265,8 +243,8 @@ private bool VerifyTemplate(EventMetadata emd) } } - if ((_payload == null && definedParameterCount != 0) - || ((_payload != null) && _payload.Length != definedParameterCount)) + if ((Payload == null && definedParameterCount != 0) + || ((Payload != null) && Payload.Length != definedParameterCount)) { string warning = string.Format(CultureInfo.InvariantCulture, _resourceMgr.GetString("PayloadMismatch"), _id, emd.Template); WriteWarning(warning); @@ -274,6 +252,7 @@ private bool VerifyTemplate(EventMetadata emd) return false; } } + return true; } @@ -304,46 +283,47 @@ private static EventDescriptor CreateEventDescriptor(ProviderMetadata providerMe } /// - /// ProcessRecord + /// ProcessRecord. /// protected override void ProcessRecord() { - using (EventProvider provider = new EventProvider(_providerMetadata.Id)) + using (EventProvider provider = new(_providerMetadata.Id)) { EventDescriptor ed = _eventDescriptor.Value; - if (_payload != null && _payload.Length > 0) + if (Payload != null && Payload.Length > 0) { - for (int i = 0; i < _payload.Length; i++) + for (int i = 0; i < Payload.Length; i++) { - if (_payload[i] == null) + if (Payload[i] == null) { - _payload[i] = string.Empty; + Payload[i] = string.Empty; } } - provider.WriteEvent(ref ed, _payload); + + provider.WriteEvent(in ed, Payload); } else { - provider.WriteEvent(ref ed); + provider.WriteEvent(in ed); } } + base.ProcessRecord(); } /// - /// EndProcessing + /// EndProcessing. /// protected override void EndProcessing() { - if (_providerMetadata != null) - _providerMetadata.Dispose(); + _providerMetadata?.Dispose(); base.EndProcessing(); } } - internal class EventWriteException : Exception + internal sealed class EventWriteException : Exception { internal EventWriteException(string msg, Exception innerException) : base(msg, innerException) @@ -353,4 +333,4 @@ internal EventWriteException(string msg) : base(msg) { } } -} \ No newline at end of file +} diff --git a/src/Microsoft.PowerShell.Commands.Diagnostics/PdhHelper.cs b/src/Microsoft.PowerShell.Commands.Diagnostics/PdhHelper.cs index 97432530c27..6f5d8f6e5ec 100644 --- a/src/Microsoft.PowerShell.Commands.Diagnostics/PdhHelper.cs +++ b/src/Microsoft.PowerShell.Commands.Diagnostics/PdhHelper.cs @@ -1,15 +1,13 @@ -// -// Copyright (C) Microsoft. All rights reserved. -// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. using System; +using System.Collections.Generic; +using System.Collections.Specialized; using System.Diagnostics; using System.Globalization; using System.Runtime.InteropServices; -using System.Collections; -using System.Collections.Generic; -using System.Collections.Specialized; -using System.Runtime.InteropServices.ComTypes; + using Microsoft.PowerShell.Commands.GetCounter; using Microsoft.Win32; @@ -17,96 +15,94 @@ namespace Microsoft.Powershell.Commands.GetCounter.PdhNative { internal static class PdhResults { - public const long PDH_CSTATUS_VALID_DATA = 0x0L; - public const long PDH_CSTATUS_NEW_DATA = 0x1L; - public const long PDH_CSTATUS_NO_MACHINE = 0x800007D0L; - public const long PDH_CSTATUS_NO_INSTANCE = 0x800007D1L; - public const long PDH_MORE_DATA = 0x800007D2L; - public const long PDH_CSTATUS_ITEM_NOT_VALIDATED = 0x800007D3L; - public const long PDH_RETRY = 0x800007D4L; - public const long PDH_NO_DATA = 0x800007D5L; - public const long PDH_CALC_NEGATIVE_DENOMINATOR = 0x800007D6L; - public const long PDH_CALC_NEGATIVE_TIMEBASE = 0x800007D7L; - public const long PDH_CALC_NEGATIVE_VALUE = 0x800007D8L; - public const long PDH_DIALOG_CANCELLED = 0x800007D9L; - public const long PDH_END_OF_LOG_FILE = 0x800007DAL; - public const long PDH_ASYNC_QUERY_TIMEOUT = 0x800007DBL; - public const long PDH_CANNOT_SET_DEFAULT_REALTIME_DATASOURCE = 0x800007DCL; - public const long PDH_UNABLE_MAP_NAME_FILES = 0x80000BD5L; - public const long PDH_PLA_VALIDATION_WARNING = 0x80000BF3L; - public const long PDH_CSTATUS_NO_OBJECT = 0xC0000BB8L; - public const long PDH_CSTATUS_NO_COUNTER = 0xC0000BB9L; - public const long PDH_CSTATUS_INVALID_DATA = 0xC0000BBAL; - public const long PDH_MEMORY_ALLOCATION_FAILURE = 0xC0000BBBL; - public const long PDH_INVALID_HANDLE = 0xC0000BBCL; - public const long PDH_INVALID_ARGUMENT = 0xC0000BBDL; - public const long PDH_FUNCTION_NOT_FOUND = 0xC0000BBEL; - public const long PDH_CSTATUS_NO_COUNTERNAME = 0xC0000BBFL; - public const long PDH_CSTATUS_BAD_COUNTERNAME = 0xC0000BC0L; - public const long PDH_INVALID_BUFFER = 0xC0000BC1L; - public const long PDH_INSUFFICIENT_BUFFER = 0xC0000BC2L; - public const long PDH_CANNOT_CONNECT_MACHINE = 0xC0000BC3L; - public const long PDH_INVALID_PATH = 0xC0000BC4L; - public const long PDH_INVALID_INSTANCE = 0xC0000BC5L; - public const long PDH_INVALID_DATA = 0xC0000BC6L; - public const long PDH_NO_DIALOG_DATA = 0xC0000BC7L; - public const long PDH_CANNOT_READ_NAME_STRINGS = 0xC0000BC8L; - public const long PDH_LOG_FILE_CREATE_ERROR = 0xC0000BC9L; - public const long PDH_LOG_FILE_OPEN_ERROR = 0xC0000BCAL; - public const long PDH_LOG_TYPE_NOT_FOUND = 0xC0000BCBL; - public const long PDH_NO_MORE_DATA = 0xC0000BCCL; - public const long PDH_ENTRY_NOT_IN_LOG_FILE = 0xC0000BCDL; - public const long PDH_DATA_SOURCE_IS_LOG_FILE = 0xC0000BCEL; - public const long PDH_DATA_SOURCE_IS_REAL_TIME = 0xC0000BCFL; - public const long PDH_UNABLE_READ_LOG_HEADER = 0xC0000BD0L; - public const long PDH_FILE_NOT_FOUND = 0xC0000BD1L; - public const long PDH_FILE_ALREADY_EXISTS = 0xC0000BD2L; - public const long PDH_NOT_IMPLEMENTED = 0xC0000BD3L; - public const long PDH_STRING_NOT_FOUND = 0xC0000BD4L; - public const long PDH_UNKNOWN_LOG_FORMAT = 0xC0000BD6L; - public const long PDH_UNKNOWN_LOGSVC_COMMAND = 0xC0000BD7L; - public const long PDH_LOGSVC_QUERY_NOT_FOUND = 0xC0000BD8L; - public const long PDH_LOGSVC_NOT_OPENED = 0xC0000BD9L; - public const long PDH_WBEM_ERROR = 0xC0000BDAL; - public const long PDH_ACCESS_DENIED = 0xC0000BDBL; - public const long PDH_LOG_FILE_TOO_SMALL = 0xC0000BDCL; - public const long PDH_INVALID_DATASOURCE = 0xC0000BDDL; - public const long PDH_INVALID_SQLDB = 0xC0000BDEL; - public const long PDH_NO_COUNTERS = 0xC0000BDFL; - public const long PDH_SQL_ALLOC_FAILED = 0xC0000BE0L; - public const long PDH_SQL_ALLOCCON_FAILED = 0xC0000BE1L; - public const long PDH_SQL_EXEC_DIRECT_FAILED = 0xC0000BE2L; - public const long PDH_SQL_FETCH_FAILED = 0xC0000BE3L; - public const long PDH_SQL_ROWCOUNT_FAILED = 0xC0000BE4L; - public const long PDH_SQL_MORE_RESULTS_FAILED = 0xC0000BE5L; - public const long PDH_SQL_CONNECT_FAILED = 0xC0000BE6L; - public const long PDH_SQL_BIND_FAILED = 0xC0000BE7L; - public const long PDH_CANNOT_CONNECT_WMI_SERVER = 0xC0000BE8L; - public const long PDH_PLA_COLLECTION_ALREADY_RUNNING = 0xC0000BE9L; - public const long PDH_PLA_ERROR_SCHEDULE_OVERLAP = 0xC0000BEAL; - public const long PDH_PLA_COLLECTION_NOT_FOUND = 0xC0000BEBL; - public const long PDH_PLA_ERROR_SCHEDULE_ELAPSED = 0xC0000BECL; - public const long PDH_PLA_ERROR_NOSTART = 0xC0000BEDL; - public const long PDH_PLA_ERROR_ALREADY_EXISTS = 0xC0000BEEL; - public const long PDH_PLA_ERROR_TYPE_MISMATCH = 0xC0000BEFL; - public const long PDH_PLA_ERROR_FILEPATH = 0xC0000BF0L; - public const long PDH_PLA_SERVICE_ERROR = 0xC0000BF1L; - public const long PDH_PLA_VALIDATION_ERROR = 0xC0000BF2L; - public const long PDH_PLA_ERROR_NAME_TOO_LONG = 0xC0000BF4L; - public const long PDH_INVALID_SQL_LOG_FORMAT = 0xC0000BF5L; - public const long PDH_COUNTER_ALREADY_IN_QUERY = 0xC0000BF6L; - public const long PDH_BINARY_LOG_CORRUPT = 0xC0000BF7L; - public const long PDH_LOG_SAMPLE_TOO_SMALL = 0xC0000BF8L; - public const long PDH_OS_LATER_VERSION = 0xC0000BF9L; - public const long PDH_OS_EARLIER_VERSION = 0xC0000BFAL; - public const long PDH_INCORRECT_APPEND_TIME = 0xC0000BFBL; - public const long PDH_UNMATCHED_APPEND_COUNTER = 0xC0000BFCL; - public const long PDH_SQL_ALTER_DETAIL_FAILED = 0xC0000BFDL; - public const long PDH_QUERY_PERF_DATA_TIMEOUT = 0xC0000BFEL; + public const uint PDH_CSTATUS_VALID_DATA = 0x0; + public const uint PDH_CSTATUS_NEW_DATA = 0x1; + public const uint PDH_CSTATUS_NO_MACHINE = 0x800007D0; + public const uint PDH_CSTATUS_NO_INSTANCE = 0x800007D1; + public const uint PDH_MORE_DATA = 0x800007D2; + public const uint PDH_CSTATUS_ITEM_NOT_VALIDATED = 0x800007D3; + public const uint PDH_RETRY = 0x800007D4; + public const uint PDH_NO_DATA = 0x800007D5; + public const uint PDH_CALC_NEGATIVE_DENOMINATOR = 0x800007D6; + public const uint PDH_CALC_NEGATIVE_TIMEBASE = 0x800007D7; + public const uint PDH_CALC_NEGATIVE_VALUE = 0x800007D8; + public const uint PDH_DIALOG_CANCELLED = 0x800007D9; + public const uint PDH_END_OF_LOG_FILE = 0x800007DA; + public const uint PDH_ASYNC_QUERY_TIMEOUT = 0x800007DB; + public const uint PDH_CANNOT_SET_DEFAULT_REALTIME_DATASOURCE = 0x800007DC; + public const uint PDH_UNABLE_MAP_NAME_FILES = 0x80000BD5; + public const uint PDH_PLA_VALIDATION_WARNING = 0x80000BF3; + public const uint PDH_CSTATUS_NO_OBJECT = 0xC0000BB8; + public const uint PDH_CSTATUS_NO_COUNTER = 0xC0000BB9; + public const uint PDH_CSTATUS_INVALID_DATA = 0xC0000BBA; + public const uint PDH_MEMORY_ALLOCATION_FAILURE = 0xC0000BBB; + public const uint PDH_INVALID_HANDLE = 0xC0000BBC; + public const uint PDH_INVALID_ARGUMENT = 0xC0000BBD; + public const uint PDH_FUNCTION_NOT_FOUND = 0xC0000BBE; + public const uint PDH_CSTATUS_NO_COUNTERNAME = 0xC0000BBF; + public const uint PDH_CSTATUS_BAD_COUNTERNAME = 0xC0000BC0; + public const uint PDH_INVALID_BUFFER = 0xC0000BC1; + public const uint PDH_INSUFFICIENT_BUFFER = 0xC0000BC2; + public const uint PDH_CANNOT_CONNECT_MACHINE = 0xC0000BC3; + public const uint PDH_INVALID_PATH = 0xC0000BC4; + public const uint PDH_INVALID_INSTANCE = 0xC0000BC5; + public const uint PDH_INVALID_DATA = 0xC0000BC6; + public const uint PDH_NO_DIALOG_DATA = 0xC0000BC7; + public const uint PDH_CANNOT_READ_NAME_STRINGS = 0xC0000BC8; + public const uint PDH_LOG_FILE_CREATE_ERROR = 0xC0000BC9; + public const uint PDH_LOG_FILE_OPEN_ERROR = 0xC0000BCA; + public const uint PDH_LOG_TYPE_NOT_FOUND = 0xC0000BCB; + public const uint PDH_NO_MORE_DATA = 0xC0000BCC; + public const uint PDH_ENTRY_NOT_IN_LOG_FILE = 0xC0000BCD; + public const uint PDH_DATA_SOURCE_IS_LOG_FILE = 0xC0000BCE; + public const uint PDH_DATA_SOURCE_IS_REAL_TIME = 0xC0000BCF; + public const uint PDH_UNABLE_READ_LOG_HEADER = 0xC0000BD0; + public const uint PDH_FILE_NOT_FOUND = 0xC0000BD1; + public const uint PDH_FILE_ALREADY_EXISTS = 0xC0000BD2; + public const uint PDH_NOT_IMPLEMENTED = 0xC0000BD3; + public const uint PDH_STRING_NOT_FOUND = 0xC0000BD4; + public const uint PDH_UNKNOWN_LOG_FORMAT = 0xC0000BD6; + public const uint PDH_UNKNOWN_LOGSVC_COMMAND = 0xC0000BD7; + public const uint PDH_LOGSVC_QUERY_NOT_FOUND = 0xC0000BD8; + public const uint PDH_LOGSVC_NOT_OPENED = 0xC0000BD9; + public const uint PDH_WBEM_ERROR = 0xC0000BDA; + public const uint PDH_ACCESS_DENIED = 0xC0000BDB; + public const uint PDH_LOG_FILE_TOO_SMALL = 0xC0000BDC; + public const uint PDH_INVALID_DATASOURCE = 0xC0000BDD; + public const uint PDH_INVALID_SQLDB = 0xC0000BDE; + public const uint PDH_NO_COUNTERS = 0xC0000BDF; + public const uint PDH_SQL_ALLOC_FAILED = 0xC0000BE0; + public const uint PDH_SQL_ALLOCCON_FAILED = 0xC0000BE1; + public const uint PDH_SQL_EXEC_DIRECT_FAILED = 0xC0000BE2; + public const uint PDH_SQL_FETCH_FAILED = 0xC0000BE3; + public const uint PDH_SQL_ROWCOUNT_FAILED = 0xC0000BE4; + public const uint PDH_SQL_MORE_RESULTS_FAILED = 0xC0000BE5; + public const uint PDH_SQL_CONNECT_FAILED = 0xC0000BE6; + public const uint PDH_SQL_BIND_FAILED = 0xC0000BE7; + public const uint PDH_CANNOT_CONNECT_WMI_SERVER = 0xC0000BE8; + public const uint PDH_PLA_COLLECTION_ALREADY_RUNNING = 0xC0000BE9; + public const uint PDH_PLA_ERROR_SCHEDULE_OVERLAP = 0xC0000BEA; + public const uint PDH_PLA_COLLECTION_NOT_FOUND = 0xC0000BEB; + public const uint PDH_PLA_ERROR_SCHEDULE_ELAPSED = 0xC0000BEC; + public const uint PDH_PLA_ERROR_NOSTART = 0xC0000BED; + public const uint PDH_PLA_ERROR_ALREADY_EXISTS = 0xC0000BEE; + public const uint PDH_PLA_ERROR_TYPE_MISMATCH = 0xC0000BEF; + public const uint PDH_PLA_ERROR_FILEPATH = 0xC0000BF0; + public const uint PDH_PLA_SERVICE_ERROR = 0xC0000BF1; + public const uint PDH_PLA_VALIDATION_ERROR = 0xC0000BF2; + public const uint PDH_PLA_ERROR_NAME_TOO_LONG = 0xC0000BF4; + public const uint PDH_INVALID_SQL_LOG_FORMAT = 0xC0000BF5; + public const uint PDH_COUNTER_ALREADY_IN_QUERY = 0xC0000BF6; + public const uint PDH_BINARY_LOG_CORRUPT = 0xC0000BF7; + public const uint PDH_LOG_SAMPLE_TOO_SMALL = 0xC0000BF8; + public const uint PDH_OS_LATER_VERSION = 0xC0000BF9; + public const uint PDH_OS_EARLIER_VERSION = 0xC0000BFA; + public const uint PDH_INCORRECT_APPEND_TIME = 0xC0000BFB; + public const uint PDH_UNMATCHED_APPEND_COUNTER = 0xC0000BFC; + public const uint PDH_SQL_ALTER_DETAIL_FAILED = 0xC0000BFD; + public const uint PDH_QUERY_PERF_DATA_TIMEOUT = 0xC0000BFE; } - - internal static class PerfDetail { public const uint PERF_DETAIL_NOVICE = 100; // The uninformed can understand it @@ -194,16 +190,8 @@ internal struct CounterHandleNInstance public string InstanceName; } - internal class PdhHelper : IDisposable + internal sealed class PdhHelper : IDisposable { - private bool _isPreVista = false; - - public PdhHelper(bool isPreVista) - { - _isPreVista = isPreVista; - } - - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] private struct PDH_COUNTER_PATH_ELEMENTS { @@ -232,12 +220,12 @@ private struct PDH_FMT_COUNTERVALUE_LARGE public Int64 largeValue; - //[FieldOffset (4), MarshalAs(UnmanagedType.LPStr)] - //public string AnsiStringValue; + // [FieldOffset (4), MarshalAs(UnmanagedType.LPStr)] + // public string AnsiStringValue; - //[FieldOffset(4), MarshalAs(UnmanagedType.LPWStr)] - //public string WideStringValue; - }; + // [FieldOffset(4), MarshalAs(UnmanagedType.LPWStr)] + // public string WideStringValue; + } [StructLayout(LayoutKind.Sequential)] private struct PDH_FMT_COUNTERVALUE_DOUBLE @@ -245,7 +233,7 @@ private struct PDH_FMT_COUNTERVALUE_DOUBLE public uint CStatus; public double doubleValue; - }; + } [StructLayout(LayoutKind.Sequential)] private struct PDH_FMT_COUNTERVALUE_UNICODE @@ -254,7 +242,7 @@ private struct PDH_FMT_COUNTERVALUE_UNICODE [MarshalAs(UnmanagedType.LPWStr)] public string WideStringValue; - }; + } [StructLayout(LayoutKind.Sequential)] private struct PDH_RAW_COUNTER @@ -274,36 +262,71 @@ private struct PDH_TIME_INFO public UInt32 SampleCount; } - - /* // // This is the structure returned by PdhGetCounterInfo(). // We only need dwType and lDefaultScale fields from this structure. // We access those fields directly. The struct is here for reference only. // - [StructLayout(LayoutKind.Explicit, CharSet = CharSet.Unicode)] - struct PDH_COUNTER_INFO { - [FieldOffset(0)] public UInt32 dwLength; - [FieldOffset(4)] public UInt32 dwType; - [FieldOffset(8)] public UInt32 CVersion; - [FieldOffset(12)] public UInt32 CStatus; - [FieldOffset(16)] public UInt32 lScale; - [FieldOffset(20)] public UInt32 lDefaultScale; - [FieldOffset(24)] public IntPtr dwUserData; - [FieldOffset(32)] public IntPtr dwQueryUserData; - [FieldOffset(40)] public string szFullPath; - - [FieldOffset(48)] public string szMachineName; - [FieldOffset(56)] public string szObjectName; - [FieldOffset(64)] public string szInstanceName; - [FieldOffset(72)] public string szParentInstance; - [FieldOffset(80)] public UInt32 dwInstanceIndex; - [FieldOffset(88)] public string szCounterName; - - [FieldOffset(96)] public string szExplainText; - [FieldOffset(104)]public IntPtr DataBuffer; - }*/ + [StructLayout(LayoutKind.Sequential)] + private unsafe struct PDH_COUNTER_INFO + { + public uint Length; + public uint Type; + public uint CVersion; + public uint CStatus; + public int Scale; + public int DefaultScale; + public ulong UserData; + public ulong QueryUserData; + public ushort* FullPath; + public _Anonymous_e__Union Anonymous; + public ushort* ExplainText; + public fixed uint DataBuffer[1]; + + [StructLayout(LayoutKind.Explicit)] + internal struct _Anonymous_e__Union + { + [FieldOffset(0)] + public PDH_DATA_ITEM_PATH_ELEMENTS_blittable DataItemPath; + [FieldOffset(0)] + public PDH_COUNTER_PATH_ELEMENTS_blittable CounterPath; + + [FieldOffset(0)] + public _Anonymous_e__Struct Anonymous; + + [StructLayout(LayoutKind.Sequential)] + internal struct PDH_DATA_ITEM_PATH_ELEMENTS_blittable + { + public ushort* MachineName; + public Guid ObjectGUID; + public uint ItemId; + public ushort* InstanceName; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct PDH_COUNTER_PATH_ELEMENTS_blittable + { + public ushort* MachineName; + public ushort* ObjectName; + public ushort* InstanceName; + public ushort* ParentInstance; + public uint InstanceIndex; + public ushort* CounterName; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct _Anonymous_e__Struct + { + public ushort* MachineName; + public ushort* ObjectName; + public ushort* InstanceName; + public ushort* ParentInstance; + public uint InstanceIndex; + public ushort* CounterName; + } + } + } [DllImport("pdh.dll", CharSet = CharSet.Unicode)] private static extern uint PdhBindInputDataSource(out PdhSafeDataSourceHandle phDataSource, string szLogFileNameList); @@ -314,13 +337,7 @@ struct PDH_COUNTER_INFO { [DllImport("pdh.dll", CharSet = CharSet.Unicode)] private static extern uint PdhAddCounter(PdhSafeQueryHandle queryHandle, string counterPath, IntPtr userData, out IntPtr counterHandle); - //Win7+ only - [DllImport("pdh.dll", CharSet = CharSet.Unicode)] - private static extern uint PdhAddRelogCounter(PdhSafeQueryHandle queryHandle, string counterPath, - UInt32 counterType, UInt32 counterDefaultScale, - UInt64 timeBase, out IntPtr counterHandle); - - //not on XP + // not on XP [DllImport("pdh.dll")] private static extern uint PdhCollectQueryDataWithTime(PdhSafeQueryHandle queryHandle, ref Int64 pllTimeStamp); @@ -343,24 +360,6 @@ private static extern uint PdhOpenLog(string szLogFileName, out PdhSafeLogHandle phLog ); - //Win7+ only - [DllImport("pdh.dll", CharSet = CharSet.Unicode)] - private static extern void PdhResetRelogCounterValues(PdhSafeLogHandle LogHandle); - - - //Win7+ only - [DllImport("pdh.dll", CharSet = CharSet.Unicode)] - private static extern uint PdhSetCounterValue(IntPtr CounterHandle, - ref PDH_RAW_COUNTER Value, /*PPDH_RAW_COUNTER */ - string InstanceName - ); - - //Win7+ only - [DllImport("pdh.dll")] - private static extern uint PdhWriteRelogSample(PdhSafeLogHandle LogHandle, - Int64 Timestamp - ); - [DllImport("pdh.dll", CharSet = CharSet.Unicode)] private static extern uint PdhGetFormattedCounterValue(IntPtr counterHandle, uint dwFormat, out IntPtr lpdwType, out PDH_FMT_COUNTERVALUE_DOUBLE pValue); @@ -392,11 +391,10 @@ private static extern uint PdhMakeCounterPath(ref PDH_COUNTER_PATH_ELEMENTS pCou [DllImport("pdh.dll", CharSet = CharSet.Unicode)] private static extern uint PdhParseCounterPath(string szFullPathBuffer, - IntPtr pCounterPathElements, //PDH_COUNTER_PATH_ELEMENTS + IntPtr pCounterPathElements, // PDH_COUNTER_PATH_ELEMENTS ref IntPtr pdwBufferSize, uint dwFlags); - [DllImport("pdh.dll", CharSet = CharSet.Unicode)] private static extern uint PdhExpandWildCardPathH(PdhSafeDataSourceHandle hDataSource, string szWildCardPath, @@ -404,19 +402,15 @@ private static extern uint PdhExpandWildCardPathH(PdhSafeDataSourceHandle hDataS ref IntPtr pcchPathListLength, uint dwFlags); - //not available on XP + // not available on XP [DllImport("pdh.dll", CharSet = CharSet.Unicode)] private static extern uint PdhValidatePathEx(PdhSafeDataSourceHandle hDataSource, string szFullPathBuffer); [DllImport("pdh.dll", CharSet = CharSet.Unicode)] private static extern uint PdhValidatePath(string szFullPathBuffer); - //not available on XP - [DllImport("pdh.dll", CharSet = CharSet.Unicode, CallingConvention = CallingConvention.StdCall, PreserveSig = true)] //private export - private static extern IntPtr PdhGetExplainText(string szMachineName, string szObjectName, string szCounterName); - [DllImport("pdh.dll", CharSet = CharSet.Unicode)] - private static extern uint PdhGetCounterInfo(IntPtr hCounter, [MarshalAs(UnmanagedType.U1)]bool bRetrieveExplainText, ref IntPtr pdwBufferSize, IntPtr lpBuffer); + private static extern uint PdhGetCounterInfo(IntPtr hCounter, [MarshalAs(UnmanagedType.U1)] bool bRetrieveExplainText, ref IntPtr pdwBufferSize, IntPtr lpBuffer); [DllImport("pdh.dll", CharSet = CharSet.Unicode)] private static extern uint PdhGetCounterTimeBase(IntPtr hCounter, out UInt64 pTimeBase); @@ -427,11 +421,9 @@ private static extern uint PdhExpandWildCardPathH(PdhSafeDataSourceHandle hDataS [DllImport("pdh.dll", CharSet = CharSet.Unicode)] private static extern uint PdhSetQueryTimeRange(PdhSafeQueryHandle hQuery, ref PDH_TIME_INFO pInfo); - [DllImport("pdh.dll", CharSet = CharSet.Unicode)] private static extern uint PdhLookupPerfNameByIndex(string szMachineName, UInt32 dwNameIndex, IntPtr szNameBuffer, ref int pcchNameBufferSize); - private PdhSafeDataSourceHandle _hDataSource = null; private PdhSafeQueryHandle _hQuery = null; @@ -466,13 +458,7 @@ public void Dispose() // // m_ConsumerPathToHandleAndInstanceMap map is used for reading counter date (live or from files). // - private Dictionary _consumerPathToHandleAndInstanceMap = new Dictionary(); - - - // - // m_ReloggerPathToHandleAndInstanceMap map is used for writing relog counters. - // - private Dictionary _reloggerPathToHandleAndInstanceMap = new Dictionary(); + private readonly Dictionary _consumerPathToHandleAndInstanceMap = new(); /// /// A helper reading in a Unicode string with embedded NULLs and splitting it into a StringCollection. @@ -480,12 +466,11 @@ public void Dispose() /// /// /// - - private void ReadPdhMultiString(ref IntPtr strNative, Int32 strSize, ref StringCollection strColl) + private static void ReadPdhMultiString(ref IntPtr strNative, Int32 strSize, ref StringCollection strColl) { Debug.Assert(strSize >= 2); int offset = 0; - string allSubstringsWithNulls = ""; + string allSubstringsWithNulls = string.Empty; while (offset <= ((strSize * sizeof(char)) - 4)) { Int32 next4 = Marshal.ReadInt32(strNative, offset); @@ -504,19 +489,16 @@ private void ReadPdhMultiString(ref IntPtr strNative, Int32 strSize, ref StringC strColl.AddRange(allSubstringsWithNulls.Split('\0')); } - - - private uint GetCounterInfoPlus(IntPtr hCounter, out UInt32 counterType, out UInt32 defaultScale, out UInt64 timeBase) + private static uint GetCounterInfoPlus(IntPtr hCounter, out UInt32 counterType, out UInt32 defaultScale, out UInt64 timeBase) { - uint res = 0; counterType = 0; defaultScale = 0; timeBase = 0; - Debug.Assert(hCounter != null); + Debug.Assert(hCounter != IntPtr.Zero); - IntPtr pBufferSize = new IntPtr(0); - res = PdhGetCounterInfo(hCounter, false, ref pBufferSize, IntPtr.Zero); + IntPtr pBufferSize = new(0); + uint res = PdhGetCounterInfo(hCounter, false, ref pBufferSize, IntPtr.Zero); if (res != PdhResults.PDH_MORE_DATA) { return res; @@ -528,12 +510,11 @@ private uint GetCounterInfoPlus(IntPtr hCounter, out UInt32 counterType, out UIn try { res = PdhGetCounterInfo(hCounter, false, ref pBufferSize, bufCounterInfo); - if (res == 0 && bufCounterInfo != IntPtr.Zero) + if (res == PdhResults.PDH_CSTATUS_VALID_DATA && bufCounterInfo != IntPtr.Zero) { - //PDH_COUNTER_INFO pdhCounterInfo = (PDH_COUNTER_INFO)Marshal.PtrToStructure(bufCounterInfo, typeof(PDH_COUNTER_INFO)); - - counterType = (uint)Marshal.ReadInt32(bufCounterInfo, 4); - defaultScale = (uint)Marshal.ReadInt32(bufCounterInfo, 20); + PDH_COUNTER_INFO pdhCounterInfo = (PDH_COUNTER_INFO)Marshal.PtrToStructure(bufCounterInfo, typeof(PDH_COUNTER_INFO)); + counterType = pdhCounterInfo.Type; + defaultScale = (uint)pdhCounterInfo.DefaultScale; } } finally @@ -542,7 +523,7 @@ private uint GetCounterInfoPlus(IntPtr hCounter, out UInt32 counterType, out UIn } res = PdhGetCounterTimeBase(hCounter, out timeBase); - if (res != 0) + if (res != PdhResults.PDH_CSTATUS_VALID_DATA) { return res; } @@ -558,13 +539,13 @@ public uint ConnectToDataSource() } uint res = PdhHelper.PdhBindInputDataSource(out _hDataSource, null); - if (res != 0) + if (res != PdhResults.PDH_CSTATUS_VALID_DATA) { - //Console.WriteLine("error in PdhBindInputDataSource: " + res); + // Console.WriteLine("error in PdhBindInputDataSource: " + res); return res; } - return 0; + return PdhResults.PDH_CSTATUS_VALID_DATA; } /// /// Connects to a single named datasource, initializing m_hDataSource variable. @@ -579,14 +560,14 @@ public uint ConnectToDataSource(string dataSourceName) } uint res = PdhHelper.PdhBindInputDataSource(out _hDataSource, dataSourceName); - if (res != 0) + if (res != PdhResults.PDH_CSTATUS_VALID_DATA) { - //Console.WriteLine("error in PdhBindInputDataSource: " + res); + // Console.WriteLine("error in PdhBindInputDataSource: " + res); } + return res; } - public uint ConnectToDataSource(StringCollection blgFileNames) { if (blgFileNames.Count == 1) @@ -594,7 +575,7 @@ public uint ConnectToDataSource(StringCollection blgFileNames) return ConnectToDataSource(blgFileNames[0]); } - string doubleNullTerminated = ""; + string doubleNullTerminated = string.Empty; foreach (string fileName in blgFileNames) { doubleNullTerminated += fileName + '\0'; @@ -609,10 +590,11 @@ public uint OpenQuery() { uint res = PdhOpenQueryH(_hDataSource, IntPtr.Zero, out _hQuery); - if (res != 0) + if (res != PdhResults.PDH_CSTATUS_VALID_DATA) { - //Console.WriteLine("error in PdhOpenQueryH: " + res); + // Console.WriteLine("error in PdhOpenQueryH: " + res); } + return res; } @@ -635,24 +617,25 @@ public uint OpenLogForWriting(string logName, PdhLogFileType logFileType, bool b return res; } - public uint SetQueryTimeRange(DateTime startTime, DateTime endTime) { Debug.Assert(_hQuery != null); Debug.Assert(endTime >= startTime); - PDH_TIME_INFO pTimeInfo = new PDH_TIME_INFO(); + PDH_TIME_INFO pTimeInfo = new(); if (startTime != DateTime.MinValue && startTime.Kind == DateTimeKind.Local) { startTime = new DateTime(startTime.Ticks, DateTimeKind.Utc); } + pTimeInfo.StartTime = (startTime == DateTime.MinValue) ? 0 : startTime.ToFileTimeUtc(); if (endTime != DateTime.MaxValue && endTime.Kind == DateTimeKind.Local) { endTime = new DateTime(endTime.Ticks, DateTimeKind.Utc); } + pTimeInfo.EndTime = (endTime == DateTime.MaxValue) ? Int64.MaxValue : endTime.ToFileTimeUtc(); pTimeInfo.SampleCount = 0; @@ -662,20 +645,20 @@ public uint SetQueryTimeRange(DateTime startTime, DateTime endTime) public uint EnumBlgFilesMachines(ref StringCollection machineNames) { - IntPtr MachineListTcharSizePtr = new IntPtr(0); + IntPtr MachineListTcharSizePtr = new(0); uint res = PdhHelper.PdhEnumMachinesH(_hDataSource, IntPtr.Zero, ref MachineListTcharSizePtr); if (res != PdhResults.PDH_MORE_DATA) { return res; } - Int32 cChars = MachineListTcharSizePtr.ToInt32(); //should be ok on 64 bit + Int32 cChars = MachineListTcharSizePtr.ToInt32(); // should be ok on 64 bit IntPtr strMachineList = Marshal.AllocHGlobal(cChars * sizeof(char)); try { res = PdhHelper.PdhEnumMachinesH(_hDataSource, (IntPtr)strMachineList, ref MachineListTcharSizePtr); - if (res == 0) + if (res == PdhResults.PDH_CSTATUS_VALID_DATA) { ReadPdhMultiString(ref strMachineList, MachineListTcharSizePtr.ToInt32(), ref machineNames); } @@ -690,7 +673,7 @@ public uint EnumBlgFilesMachines(ref StringCollection machineNames) public uint EnumObjects(string machineName, ref StringCollection objectNames) { - IntPtr pBufferSize = new IntPtr(0); + IntPtr pBufferSize = new(0); uint res = PdhEnumObjectsH(_hDataSource, machineName, IntPtr.Zero, ref pBufferSize, PerfDetail.PERF_DETAIL_WIZARD, false); if (res != PdhResults.PDH_MORE_DATA) { @@ -703,7 +686,7 @@ public uint EnumObjects(string machineName, ref StringCollection objectNames) try { res = PdhEnumObjectsH(_hDataSource, machineName, (IntPtr)strObjectList, ref pBufferSize, PerfDetail.PERF_DETAIL_WIZARD, false); - if (res == 0) + if (res == PdhResults.PDH_CSTATUS_VALID_DATA) { ReadPdhMultiString(ref strObjectList, pBufferSize.ToInt32(), ref objectNames); } @@ -718,8 +701,8 @@ public uint EnumObjects(string machineName, ref StringCollection objectNames) public uint EnumObjectItems(string machineName, string objectName, ref StringCollection counterNames, ref StringCollection instanceNames) { - IntPtr pCounterBufferSize = new IntPtr(0); - IntPtr pInstanceBufferSize = new IntPtr(0); + IntPtr pCounterBufferSize = new(0); + IntPtr pInstanceBufferSize = new(0); uint res = PdhEnumObjectItemsH(_hDataSource, machineName, objectName, IntPtr.Zero, ref pCounterBufferSize, @@ -728,23 +711,23 @@ public uint EnumObjectItems(string machineName, string objectName, ref StringCol if (res == PdhResults.PDH_CSTATUS_NO_INSTANCE) { instanceNames.Clear(); - return 0; //masking the error + return PdhResults.PDH_CSTATUS_VALID_DATA; // masking the error } else if (res == PdhResults.PDH_CSTATUS_NO_OBJECT) { counterNames.Clear(); - return 0; //masking the error + return PdhResults.PDH_CSTATUS_VALID_DATA; // masking the error } else if (res != PdhResults.PDH_MORE_DATA) { - //Console.WriteLine("error in PdhEnumObjectItemsH 1st call: " + res); + // Console.WriteLine("error in PdhEnumObjectItemsH 1st call: " + res); return res; } Int32 cChars = pCounterBufferSize.ToInt32(); IntPtr strCountersList = (cChars > 0) ? Marshal.AllocHGlobal((cChars) * sizeof(char)) : IntPtr.Zero; - //re-set count to 0 if it is lte 2 + // re-set count to 0 if it is lte 2 if (cChars < 0) { pCounterBufferSize = new IntPtr(0); @@ -754,7 +737,7 @@ public uint EnumObjectItems(string machineName, string objectName, ref StringCol IntPtr strInstancesList = (cChars > 0) ? Marshal.AllocHGlobal((cChars) * sizeof(char)) : IntPtr.Zero; - //re-set count to 0 if it is lte 2 + // re-set count to 0 if it is lte 2 if (cChars < 0) { pInstanceBufferSize = new IntPtr(0); @@ -766,9 +749,9 @@ public uint EnumObjectItems(string machineName, string objectName, ref StringCol strCountersList, ref pCounterBufferSize, strInstancesList, ref pInstanceBufferSize, PerfDetail.PERF_DETAIL_WIZARD, 0); - if (res != 0) + if (res != PdhResults.PDH_CSTATUS_VALID_DATA) { - //Console.WriteLine("error in PdhEnumObjectItemsH 2nd call: " + res + "\n Counter buffer size is " + // Console.WriteLine("error in PdhEnumObjectItemsH 2nd call: " + res + "\n Counter buffer size is " // + pCounterBufferSize.ToInt32() + "\n Instance buffer size is " + pInstanceBufferSize.ToInt32()); } else @@ -786,6 +769,7 @@ public uint EnumObjectItems(string machineName, string objectName, ref StringCol { Marshal.FreeHGlobal(strCountersList); } + if (strInstancesList != IntPtr.Zero) { Marshal.FreeHGlobal(strInstancesList); @@ -799,51 +783,51 @@ public uint GetValidPathsFromFiles(ref StringCollection validPaths) { Debug.Assert(_hDataSource != null && !_hDataSource.IsInvalid, "Call ConnectToDataSource before GetValidPathsFromFiles"); - - StringCollection machineNames = new StringCollection(); + StringCollection machineNames = new(); uint res = this.EnumBlgFilesMachines(ref machineNames); - if (res != 0) + if (res != PdhResults.PDH_CSTATUS_VALID_DATA) { return res; } foreach (string machine in machineNames) { - StringCollection counterSets = new StringCollection(); + StringCollection counterSets = new(); res = this.EnumObjects(machine, ref counterSets); - if (res != 0) + if (res != PdhResults.PDH_CSTATUS_VALID_DATA) { return res; } foreach (string counterSet in counterSets) { - //Console.WriteLine("Counter set " + counterSet); + // Console.WriteLine("Counter set " + counterSet); - StringCollection counterSetCounters = new StringCollection(); - StringCollection counterSetInstances = new StringCollection(); + StringCollection counterSetCounters = new(); + StringCollection counterSetInstances = new(); res = this.EnumObjectItems(machine, counterSet, ref counterSetCounters, ref counterSetInstances); - if (res != 0) + if (res != PdhResults.PDH_CSTATUS_VALID_DATA) { return res; } res = this.GetValidPaths(machine, counterSet, ref counterSetCounters, ref counterSetInstances, ref validPaths); - if (res != 0) + if (res != PdhResults.PDH_CSTATUS_VALID_DATA) { return res; } } } + return res; } private bool IsPathValid(ref PDH_COUNTER_PATH_ELEMENTS pathElts, out string outPath) { bool ret = false; - outPath = ""; - IntPtr pPathBufferSize = new IntPtr(0); + outPath = string.Empty; + IntPtr pPathBufferSize = new(0); uint res = PdhMakeCounterPath(ref pathElts, IntPtr.Zero, ref pPathBufferSize, 0); if (res != PdhResults.PDH_MORE_DATA) @@ -857,18 +841,11 @@ private bool IsPathValid(ref PDH_COUNTER_PATH_ELEMENTS pathElts, out string outP try { res = PdhMakeCounterPath(ref pathElts, strPath, ref pPathBufferSize, 0); - if (res == 0) + if (res == PdhResults.PDH_CSTATUS_VALID_DATA) { outPath = Marshal.PtrToStringUni(strPath); - if (!_isPreVista) - { - ret = (PdhValidatePathEx(_hDataSource, outPath) == 0); - } - else - { - ret = (PdhValidatePath(outPath) == 0); - } + ret = (PdhValidatePathEx(_hDataSource, outPath) == PdhResults.PDH_CSTATUS_VALID_DATA); } } finally @@ -881,24 +858,13 @@ private bool IsPathValid(ref PDH_COUNTER_PATH_ELEMENTS pathElts, out string outP public bool IsPathValid(string path) { - if (!_isPreVista) - { - return (PdhValidatePathEx(_hDataSource, path) == 0); - } - else - { - // - // Note: this assumes the paths already contain machine names - // - return (PdhValidatePath(path) == 0); - } + return (PdhValidatePathEx(_hDataSource, path) == PdhResults.PDH_CSTATUS_VALID_DATA); } - - private uint MakePath(PDH_COUNTER_PATH_ELEMENTS pathElts, out string outPath, bool bWildcardInstances) + private static uint MakePath(PDH_COUNTER_PATH_ELEMENTS pathElts, out string outPath, bool bWildcardInstances) { - outPath = ""; - IntPtr pPathBufferSize = new IntPtr(0); + outPath = string.Empty; + IntPtr pPathBufferSize = new(0); if (bWildcardInstances) { @@ -919,7 +885,7 @@ private uint MakePath(PDH_COUNTER_PATH_ELEMENTS pathElts, out string outPath, bo try { res = PdhMakeCounterPath(ref pathElts, strPath, ref pPathBufferSize, 0); - if (res == 0) + if (res == PdhResults.PDH_CSTATUS_VALID_DATA) { outPath = Marshal.PtrToStringUni(strPath); } @@ -932,14 +898,14 @@ private uint MakePath(PDH_COUNTER_PATH_ELEMENTS pathElts, out string outPath, bo return res; } - private uint MakeAllInstancePath(string origPath, out string unifiedPath) + private static uint MakeAllInstancePath(string origPath, out string unifiedPath) { unifiedPath = origPath; - PDH_COUNTER_PATH_ELEMENTS elts = new PDH_COUNTER_PATH_ELEMENTS(); + PDH_COUNTER_PATH_ELEMENTS elts = new(); uint res = ParsePath(origPath, ref elts); - if (res != 0) + if (res != PdhResults.PDH_CSTATUS_VALID_DATA) { return res; } @@ -947,18 +913,17 @@ private uint MakeAllInstancePath(string origPath, out string unifiedPath) return MakePath(elts, out unifiedPath, true); } - - private uint ParsePath(string fullPath, ref PDH_COUNTER_PATH_ELEMENTS pCounterPathElements) + private static uint ParsePath(string fullPath, ref PDH_COUNTER_PATH_ELEMENTS pCounterPathElements) { - IntPtr bufSize = new IntPtr(0); + IntPtr bufSize = new(0); uint res = PdhParseCounterPath(fullPath, IntPtr.Zero, ref bufSize, 0); - if (res != PdhResults.PDH_MORE_DATA && res != 0) + if (res != PdhResults.PDH_MORE_DATA && res != PdhResults.PDH_CSTATUS_VALID_DATA) { - //Console.WriteLine("error in PdhParseCounterPath: " + res); + // Console.WriteLine("error in PdhParseCounterPath: " + res); return res; } @@ -970,7 +935,7 @@ private uint ParsePath(string fullPath, ref PDH_COUNTER_PATH_ELEMENTS pCounterPa structPtr, ref bufSize, 0); - if (res == 0) + if (res == PdhResults.PDH_CSTATUS_VALID_DATA) { // // Marshal.PtrToStructure will allocate managed memory for the object, @@ -1001,11 +966,11 @@ private uint ParsePath(string fullPath, ref PDH_COUNTER_PATH_ELEMENTS pCounterPa // public uint TranslateLocalCounterPath(string englishPath, out string localizedPath) { - uint res = 0; - localizedPath = ""; - PDH_COUNTER_PATH_ELEMENTS pathElts = new PDH_COUNTER_PATH_ELEMENTS(); + uint res = PdhResults.PDH_CSTATUS_VALID_DATA; + localizedPath = string.Empty; + PDH_COUNTER_PATH_ELEMENTS pathElts = new(); res = ParsePath(englishPath, ref pathElts); - if (res != 0) + if (res != PdhResults.PDH_CSTATUS_VALID_DATA) { return res; } @@ -1022,11 +987,10 @@ public uint TranslateLocalCounterPath(string englishPath, out string localizedPa RegistryKey rootKey = Registry.LocalMachine.OpenSubKey("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Perflib\\009"); string[] regCounters = (string[])rootKey.GetValue("Counter"); - // NOTE: 1-based enumeration because the name strings follow index strings in the array Int32 counterIndex = -1; Int32 objIndex = -1; - for (uint enumIndex = 1; enumIndex < regCounters.Length; enumIndex++) + for (int enumIndex = 1; enumIndex < regCounters.Length; enumIndex++) { string regString = regCounters[enumIndex]; if (regString.ToLowerInvariant() == lowerEngCtrName) @@ -1037,7 +1001,7 @@ public uint TranslateLocalCounterPath(string englishPath, out string localizedPa } catch (Exception) { - return (uint)PdhResults.PDH_INVALID_PATH; + return PdhResults.PDH_INVALID_PATH; } } else if (regString.ToLowerInvariant() == lowerEngObjectName) @@ -1048,9 +1012,10 @@ public uint TranslateLocalCounterPath(string englishPath, out string localizedPa } catch (Exception) { - return (uint)PdhResults.PDH_INVALID_PATH; + return PdhResults.PDH_INVALID_PATH; } } + if (counterIndex != -1 && objIndex != -1) { break; @@ -1059,37 +1024,35 @@ public uint TranslateLocalCounterPath(string englishPath, out string localizedPa if (counterIndex == -1 || objIndex == -1) { - return (uint)PdhResults.PDH_INVALID_PATH; + return PdhResults.PDH_INVALID_PATH; } - // Now, call retrieve the localized names of the object and the counter by index: string objNameLocalized; res = LookupPerfNameByIndex(pathElts.MachineName, (uint)objIndex, out objNameLocalized); - if (res != 0) + if (res != PdhResults.PDH_CSTATUS_VALID_DATA) { return res; } - pathElts.ObjectName = objNameLocalized; + pathElts.ObjectName = objNameLocalized; string ctrNameLocalized; res = LookupPerfNameByIndex(pathElts.MachineName, (uint)counterIndex, out ctrNameLocalized); - if (res != 0) + if (res != PdhResults.PDH_CSTATUS_VALID_DATA) { return res; } + pathElts.CounterName = ctrNameLocalized; // Assemble the path back by using the translated object and counter names: res = MakePath(pathElts, out localizedPath, false); - return res; } - public uint LookupPerfNameByIndex(string machineName, uint index, out string locName) { // @@ -1100,8 +1063,8 @@ public uint LookupPerfNameByIndex(string machineName, uint index, out string loc int strSize = 256; IntPtr localizedPathPtr = Marshal.AllocHGlobal(strSize * sizeof(char)); - locName = ""; - uint res = 0; + locName = string.Empty; + uint res; try { res = PdhLookupPerfNameByIndex(machineName, index, localizedPathPtr, ref strSize); @@ -1111,7 +1074,8 @@ public uint LookupPerfNameByIndex(string machineName, uint index, out string loc localizedPathPtr = Marshal.AllocHGlobal(strSize * sizeof(char)); res = PdhLookupPerfNameByIndex(machineName, index, localizedPathPtr, ref strSize); } - if (res == 0) + + if (res == PdhResults.PDH_CSTATUS_VALID_DATA) { locName = Marshal.PtrToStringUni(localizedPathPtr); } @@ -1124,17 +1088,13 @@ public uint LookupPerfNameByIndex(string machineName, uint index, out string loc return res; } - - public uint GetValidPaths(string machineName, string objectName, ref StringCollection counters, ref StringCollection instances, ref StringCollection validPaths) { - uint res = 0; - - PDH_COUNTER_PATH_ELEMENTS pathElts = new PDH_COUNTER_PATH_ELEMENTS(); + PDH_COUNTER_PATH_ELEMENTS pathElts = new(); pathElts.MachineName = machineName; pathElts.ObjectName = objectName; @@ -1165,9 +1125,9 @@ public uint GetValidPaths(string machineName, } } } - return res; - } + return PdhResults.PDH_CSTATUS_VALID_DATA; + } public uint AddCounters(ref StringCollection validPaths, bool bFlushOldCounters) { @@ -1179,21 +1139,21 @@ public uint AddCounters(ref StringCollection validPaths, bool bFlushOldCounters) } bool bAtLeastOneAdded = false; - uint res = 0; + uint res = PdhResults.PDH_CSTATUS_VALID_DATA; foreach (string counterPath in validPaths) { IntPtr counterHandle; res = PdhAddCounter(_hQuery, counterPath, IntPtr.Zero, out counterHandle); - if (res == 0) + if (res == PdhResults.PDH_CSTATUS_VALID_DATA) { - CounterHandleNInstance chi = new CounterHandleNInstance(); + CounterHandleNInstance chi = new(); chi.hCounter = counterHandle; chi.InstanceName = null; - PDH_COUNTER_PATH_ELEMENTS pathElts = new PDH_COUNTER_PATH_ELEMENTS(); + PDH_COUNTER_PATH_ELEMENTS pathElts = new(); res = ParsePath(counterPath, ref pathElts); - if (res == 0 && pathElts.InstanceName != null) + if (res == PdhResults.PDH_CSTATUS_VALID_DATA && pathElts.InstanceName != null) { chi.InstanceName = pathElts.InstanceName.ToLowerInvariant(); } @@ -1207,346 +1167,20 @@ public uint AddCounters(ref StringCollection validPaths, bool bFlushOldCounters) } } - return bAtLeastOneAdded ? 0 : res; - } - - - // - // AddRelogCounters combines instances and adds counters to m_hQuery. - // The counter handles and full paths - // - public uint AddRelogCounters(PerformanceCounterSampleSet sampleSet) - { - Debug.Assert(_hQuery != null && !_hQuery.IsInvalid); - - uint res = 0; - - Dictionary> prefixInstanceMap = new Dictionary>(); - - // - // Go through all the samples one, constructing prefixInstanceMap and adding new counters as needed - // - foreach (PerformanceCounterSample sample in sampleSet.CounterSamples) - { - PDH_COUNTER_PATH_ELEMENTS pathElts = new PDH_COUNTER_PATH_ELEMENTS(); - res = ParsePath(sample.Path, ref pathElts); - if (res != 0) - { - // Skipping for now, but should be a non-terminating error - continue; - } - - string lowerCaseMachine = pathElts.MachineName.ToLowerInvariant(); - string lowerCaseObject = pathElts.ObjectName.ToLowerInvariant(); - string lowerCaseCounter = pathElts.CounterName.ToLowerInvariant(); - - string lcPathMinusInstance = @"\\" + lowerCaseMachine + @"\" + lowerCaseObject + @"\" + lowerCaseCounter; - - List sampleList; - if (prefixInstanceMap.TryGetValue(lcPathMinusInstance, out sampleList)) - { - prefixInstanceMap[lcPathMinusInstance].Add(sample); - } - else - { - List newList = new List(); - newList.Add(sample); - prefixInstanceMap.Add(lcPathMinusInstance, newList); - } - - //Console.WriteLine ("Added path " + sample.Path + " to the 1ist map with prefix " + lcPathMinusInstance); - } - - // - // Add counters to the query, consolidating multi-instance with a wildcard path, - // and construct m_ReloggerPathToHandleAndInstanceMap where each full path would be pointing to its counter handle - // and an instance name (might be empty for no-instance counter types). - // You can have multiple full paths inside m_ReloggerPathToHandleAndInstanceMap pointing to the same handle. - // - - foreach (string prefix in prefixInstanceMap.Keys) - { - IntPtr counterHandle; - string unifiedPath = prefixInstanceMap[prefix][0].Path; - - if (prefixInstanceMap[prefix].Count > 1) - { - res = MakeAllInstancePath(prefixInstanceMap[prefix][0].Path, out unifiedPath); - if (res != 0) - { - // Skipping for now, but should be a non-terminating error - continue; - } - } - - res = PdhAddRelogCounter(_hQuery, - unifiedPath, - (UInt32)prefixInstanceMap[prefix][0].CounterType, - prefixInstanceMap[prefix][0].DefaultScale, - prefixInstanceMap[prefix][0].TimeBase, - out counterHandle); - if (res != 0) - { - // Skipping for now, but should be a non-terminating error - // Console.WriteLine ("PdhAddCounter returned " + res + " for counter path " + unifiedPath); - continue; - } - - //Console.WriteLine ("added pdh query path:" + unifiedPath ); - - //now, add all actual paths to m_ReloggerPathToHandleAndInstanceMap - foreach (PerformanceCounterSample sample in prefixInstanceMap[prefix]) - { - PDH_COUNTER_PATH_ELEMENTS pathElts = new PDH_COUNTER_PATH_ELEMENTS(); - res = ParsePath(sample.Path, ref pathElts); - if (res != 0) - { - // Skipping for now, but should be a non-terminating error - continue; - } - - CounterHandleNInstance chi = new CounterHandleNInstance(); - - chi.hCounter = counterHandle; - - if (pathElts.InstanceName != null) - { - chi.InstanceName = pathElts.InstanceName.ToLowerInvariant(); - } - - if (!_reloggerPathToHandleAndInstanceMap.ContainsKey(sample.Path.ToLowerInvariant())) - { - _reloggerPathToHandleAndInstanceMap.Add(sample.Path.ToLowerInvariant(), chi); - //Console.WriteLine ("added map path:" + sample.Path ); - } - } - } - - //TODO: verify that all counters are in the map - - return (_reloggerPathToHandleAndInstanceMap.Keys.Count > 0) ? 0 : res; - } - - - - // - // AddRelogCountersPreservingPaths preserves all paths and adds as relog counters to m_hQuery. - // The counter handles and full paths are added to m_ReloggerPathToHandleAndInstanceMap - // - public uint AddRelogCountersPreservingPaths(PerformanceCounterSampleSet sampleSet) - { - Debug.Assert(_hQuery != null && !_hQuery.IsInvalid); - - uint res = 0; - - // - // Go through all the samples one, constructing prefixInstanceMap and adding new counters as needed - // - foreach (PerformanceCounterSample sample in sampleSet.CounterSamples) - { - PDH_COUNTER_PATH_ELEMENTS pathElts = new PDH_COUNTER_PATH_ELEMENTS(); - res = ParsePath(sample.Path, ref pathElts); - if (res != 0) - { - // Skipping for now, but should be a non-terminating error - continue; - } - - IntPtr counterHandle; - res = PdhAddRelogCounter(_hQuery, - sample.Path, - (uint)sample.CounterType, - sample.DefaultScale, - sample.TimeBase, - out counterHandle); - if (res != 0) - { - // Skipping for now, but should be a non-terminating error - continue; - } - - CounterHandleNInstance chi = new CounterHandleNInstance(); - - chi.hCounter = counterHandle; - if (pathElts.InstanceName != null) - { - chi.InstanceName = pathElts.InstanceName.ToLowerInvariant(); - } - - if (!_reloggerPathToHandleAndInstanceMap.ContainsKey(sample.Path.ToLowerInvariant())) - { - _reloggerPathToHandleAndInstanceMap.Add(sample.Path.ToLowerInvariant(), chi); - } - } - - return (_reloggerPathToHandleAndInstanceMap.Keys.Count > 0) ? 0 : res; + return bAtLeastOneAdded ? PdhResults.PDH_CSTATUS_VALID_DATA : res; } public string GetCounterSetHelp(string szMachineName, string szObjectName) { - if (_isPreVista) - { - return string.Empty; - } - IntPtr retString = PdhGetExplainText(szMachineName, szObjectName, null); - return Marshal.PtrToStringUni(retString); + // API not available to retrieve + return string.Empty; } - public uint ReadNextSetPreVista(out PerformanceCounterSampleSet nextSet, bool bSkipReading) - { - uint res = 0; - nextSet = null; - - res = PdhCollectQueryData(_hQuery); - if (bSkipReading) - { - return res; - } - if (res != 0 && res != PdhResults.PDH_NO_DATA) - { - return res; - } - - PerformanceCounterSample[] samplesArr = new PerformanceCounterSample[_consumerPathToHandleAndInstanceMap.Count]; - uint sampleIndex = 0; - uint numInvalidDataSamples = 0; - uint lastErr = 0; - - DateTime sampleTimeStamp = DateTime.Now; - - foreach (string path in _consumerPathToHandleAndInstanceMap.Keys) - { - IntPtr counterTypePtr = new IntPtr(0); - UInt32 counterType = (UInt32)PerformanceCounterType.RawBase; - UInt32 defaultScale = 0; - UInt64 timeBase = 0; - - IntPtr hCounter = _consumerPathToHandleAndInstanceMap[path].hCounter; - Debug.Assert(hCounter != null); - - res = GetCounterInfoPlus(hCounter, out counterType, out defaultScale, out timeBase); - if (res != 0) - { - //Console.WriteLine ("GetCounterInfoPlus for " + path + " failed with " + res); - } - - PDH_RAW_COUNTER rawValue; - res = PdhGetRawCounterValue(hCounter, out counterTypePtr, out rawValue); - if (res == PdhResults.PDH_INVALID_DATA || res == PdhResults.PDH_NO_DATA) - { - //Console.WriteLine ("PdhGetRawCounterValue returned " + res); - samplesArr[sampleIndex++] = new PerformanceCounterSample(path, - _consumerPathToHandleAndInstanceMap[path].InstanceName, - 0, - (ulong)0, - (ulong)0, - 0, - PerformanceCounterType.RawBase, - defaultScale, - timeBase, - DateTime.Now, - (UInt64)DateTime.Now.ToFileTime(), - rawValue.CStatus); - - numInvalidDataSamples++; - lastErr = res; - continue; - } - else if (res != 0) - { - return res; - } - - long dtFT = (((long)rawValue.TimeStamp.dwHighDateTime) << 32) + - (uint)rawValue.TimeStamp.dwLowDateTime; - - // - // NOTE: PDH returns the filetime as local time, therefore - // we need to call FromFileTimUtc() to avoid .NET applying the timezone adjustment. - // However, that would result in the DateTime object having Kind.Utc. - // We have to copy it once more to correct that (Kind is a read-only property). - // - sampleTimeStamp = new DateTime(DateTime.FromFileTimeUtc(dtFT).Ticks, DateTimeKind.Local); - - PDH_FMT_COUNTERVALUE_DOUBLE fmtValueDouble; - res = PdhGetFormattedCounterValue(hCounter, - PdhFormat.PDH_FMT_DOUBLE | PdhFormat.PDH_FMT_NOCAP100, - out counterTypePtr, - out fmtValueDouble); - if (res == PdhResults.PDH_INVALID_DATA || res == PdhResults.PDH_NO_DATA) - { - //Console.WriteLine ("PdhGetFormattedCounterValue returned " + res); - samplesArr[sampleIndex++] = new PerformanceCounterSample(path, - _consumerPathToHandleAndInstanceMap[path].InstanceName, - 0, - (ulong)rawValue.FirstValue, - (ulong)rawValue.SecondValue, - rawValue.MultiCount, - (PerformanceCounterType)counterType, - defaultScale, - timeBase, - sampleTimeStamp, - (UInt64)dtFT, - fmtValueDouble.CStatus); - - numInvalidDataSamples++; - lastErr = res; - continue; - } - else if (res != 0) - { - //Console.WriteLine ("PdhGetFormattedCounterValue returned " + res); - return res; - } - - samplesArr[sampleIndex++] = new PerformanceCounterSample(path, - _consumerPathToHandleAndInstanceMap[path].InstanceName, - fmtValueDouble.doubleValue, - (ulong)rawValue.FirstValue, - (ulong)rawValue.SecondValue, - rawValue.MultiCount, - (PerformanceCounterType)counterTypePtr.ToInt32(), - defaultScale, - timeBase, - sampleTimeStamp, - (UInt64)dtFT, - fmtValueDouble.CStatus); - } - - // - // Prior to Vista, PdhCollectQueryDataWithTime() was not available, - // so we could not collect a timestamp for the entire sample set. - // We will use the last sample's timestamp instead. - // - nextSet = new PerformanceCounterSampleSet(sampleTimeStamp, samplesArr, _firstReading); - _firstReading = false; - - if (numInvalidDataSamples == samplesArr.Length) - { - res = lastErr; - } - else - { - // - // Reset the error - any errors are saved per sample in PerformanceCounterSample.Status for kvetching later - // - res = 0; - } - - return res; - } - - public uint ReadNextSet(out PerformanceCounterSampleSet nextSet, bool bSkipReading) { Debug.Assert(_hQuery != null && !_hQuery.IsInvalid); - if (_isPreVista) - { - return ReadNextSetPreVista(out nextSet, bSkipReading); - } - - uint res = 0; + uint res = PdhResults.PDH_CSTATUS_VALID_DATA; nextSet = null; Int64 batchTimeStampFT = 0; @@ -1556,7 +1190,8 @@ public uint ReadNextSet(out PerformanceCounterSampleSet nextSet, bool bSkipReadi { return res; } - if (res != 0 && res != PdhResults.PDH_NO_DATA) + + if (res != PdhResults.PDH_CSTATUS_VALID_DATA && res != PdhResults.PDH_NO_DATA) { return res; } @@ -1576,27 +1211,27 @@ public uint ReadNextSet(out PerformanceCounterSampleSet nextSet, bool bSkipReadi PerformanceCounterSample[] samplesArr = new PerformanceCounterSample[_consumerPathToHandleAndInstanceMap.Count]; uint sampleIndex = 0; uint numInvalidDataSamples = 0; - uint lastErr = 0; + uint lastErr = PdhResults.PDH_CSTATUS_VALID_DATA; foreach (string path in _consumerPathToHandleAndInstanceMap.Keys) { - IntPtr counterTypePtr = new IntPtr(0); + IntPtr counterTypePtr = new(0); UInt32 counterType = (UInt32)PerformanceCounterType.RawBase; UInt32 defaultScale = 0; UInt64 timeBase = 0; IntPtr hCounter = _consumerPathToHandleAndInstanceMap[path].hCounter; - Debug.Assert(hCounter != null); + Debug.Assert(hCounter != IntPtr.Zero); res = GetCounterInfoPlus(hCounter, out counterType, out defaultScale, out timeBase); - if (res != 0) + if (res != PdhResults.PDH_CSTATUS_VALID_DATA) { - //Console.WriteLine ("GetCounterInfoPlus for " + path + " failed with " + res); + // Console.WriteLine ("GetCounterInfoPlus for " + path + " failed with " + res); } PDH_RAW_COUNTER rawValue; res = PdhGetRawCounterValue(hCounter, out counterTypePtr, out rawValue); - if (res != 0) + if (res != PdhResults.PDH_CSTATUS_VALID_DATA) { samplesArr[sampleIndex++] = new PerformanceCounterSample(path, _consumerPathToHandleAndInstanceMap[path].InstanceName, @@ -1609,7 +1244,7 @@ public uint ReadNextSet(out PerformanceCounterSampleSet nextSet, bool bSkipReadi timeBase, batchStamp, (UInt64)batchStamp.ToFileTime(), - (rawValue.CStatus == 0) ? res : rawValue.CStatus); + (rawValue.CStatus == PdhResults.PDH_CSTATUS_VALID_DATA) ? res : rawValue.CStatus); numInvalidDataSamples++; lastErr = res; @@ -1619,14 +1254,14 @@ public uint ReadNextSet(out PerformanceCounterSampleSet nextSet, bool bSkipReadi long dtFT = (((long)rawValue.TimeStamp.dwHighDateTime) << 32) + (uint)rawValue.TimeStamp.dwLowDateTime; - DateTime dt = new DateTime(DateTime.FromFileTimeUtc(dtFT).Ticks, DateTimeKind.Local); + DateTime dt = new(DateTime.FromFileTimeUtc(dtFT).Ticks, DateTimeKind.Local); PDH_FMT_COUNTERVALUE_DOUBLE fmtValueDouble; res = PdhGetFormattedCounterValue(hCounter, PdhFormat.PDH_FMT_DOUBLE | PdhFormat.PDH_FMT_NOCAP100, out counterTypePtr, out fmtValueDouble); - if (res != 0) + if (res != PdhResults.PDH_CSTATUS_VALID_DATA) { samplesArr[sampleIndex++] = new PerformanceCounterSample(path, _consumerPathToHandleAndInstanceMap[path].InstanceName, @@ -1639,7 +1274,7 @@ public uint ReadNextSet(out PerformanceCounterSampleSet nextSet, bool bSkipReadi timeBase, dt, (UInt64)dtFT, - (fmtValueDouble.CStatus == 0) ? res : rawValue.CStatus); + (fmtValueDouble.CStatus == PdhResults.PDH_CSTATUS_VALID_DATA) ? res : rawValue.CStatus); numInvalidDataSamples++; lastErr = res; @@ -1660,7 +1295,6 @@ public uint ReadNextSet(out PerformanceCounterSampleSet nextSet, bool bSkipReadi fmtValueDouble.CStatus); } - nextSet = new PerformanceCounterSampleSet(batchStamp, samplesArr, _firstReading); _firstReading = false; @@ -1673,40 +1307,16 @@ public uint ReadNextSet(out PerformanceCounterSampleSet nextSet, bool bSkipReadi // // Reset the error - any errors are saved per sample in PerformanceCounterSample.Status for kvetching later // - res = 0; - } - - return res; - } - - - public uint GetFilesSummary(out CounterFileInfo summary) - { - IntPtr pNumEntries = new IntPtr(0); - PDH_TIME_INFO pInfo = new PDH_TIME_INFO(); - IntPtr bufSize = new IntPtr(System.Runtime.InteropServices.Marshal.SizeOf(pInfo)); - - uint res = PdhGetDataSourceTimeRangeH(_hDataSource, - ref pNumEntries, - ref pInfo, - ref bufSize); - if (res != 0) - { - summary = new CounterFileInfo(); - return res; + res = PdhResults.PDH_CSTATUS_VALID_DATA; } - summary = new CounterFileInfo(new DateTime(DateTime.FromFileTimeUtc(pInfo.StartTime).Ticks, DateTimeKind.Local), - new DateTime(DateTime.FromFileTimeUtc(pInfo.EndTime).Ticks, DateTimeKind.Local), - pInfo.SampleCount); - return res; } public uint ExpandWildCardPath(string path, out StringCollection expandedPaths) { expandedPaths = new StringCollection(); - IntPtr pcchPathListLength = new IntPtr(0); + IntPtr pcchPathListLength = new(0); uint res = PdhExpandWildCardPathH(_hDataSource, path, @@ -1725,7 +1335,7 @@ public uint ExpandWildCardPath(string path, out StringCollection expandedPaths) try { res = PdhExpandWildCardPathH(_hDataSource, path, strPathList, ref pcchPathListLength, PdhWildCardFlag.PDH_REFRESHCOUNTERS); - if (res == 0) + if (res == PdhResults.PDH_CSTATUS_VALID_DATA) { ReadPdhMultiString(ref strPathList, pcchPathListLength.ToInt32(), ref expandedPaths); } @@ -1737,45 +1347,5 @@ public uint ExpandWildCardPath(string path, out StringCollection expandedPaths) return res; } - - public void ResetRelogValues() - { - Debug.Assert(_hOutputLog != null && !_hOutputLog.IsInvalid); - PdhResetRelogCounterValues(_hOutputLog); - } - - public uint WriteRelogSample(DateTime timeStamp) - { - Debug.Assert(_hOutputLog != null && !_hOutputLog.IsInvalid); - return PdhWriteRelogSample(_hOutputLog, (new DateTime(timeStamp.Ticks, DateTimeKind.Utc)).ToFileTimeUtc()); - } - - public uint SetCounterValue(PerformanceCounterSample sample, out bool bUnknownPath) - { - Debug.Assert(_hOutputLog != null && !_hOutputLog.IsInvalid); - - bUnknownPath = false; - - string lcPath = sample.Path.ToLowerInvariant(); - - if (!_reloggerPathToHandleAndInstanceMap.ContainsKey(lcPath)) - { - bUnknownPath = true; - return 0; - } - - PDH_RAW_COUNTER rawStruct = new PDH_RAW_COUNTER(); - rawStruct.FirstValue = (long)sample.RawValue; - rawStruct.SecondValue = (long)sample.SecondValue; - rawStruct.MultiCount = sample.MultipleCount; - rawStruct.TimeStamp.dwHighDateTime = (int)((new DateTime(sample.Timestamp.Ticks, DateTimeKind.Utc).ToFileTimeUtc() >> 32) & 0xFFFFFFFFL); - rawStruct.TimeStamp.dwLowDateTime = (int)(new DateTime(sample.Timestamp.Ticks, DateTimeKind.Utc).ToFileTimeUtc() & 0xFFFFFFFFL); - rawStruct.CStatus = sample.Status; - - - return PdhSetCounterValue(_reloggerPathToHandleAndInstanceMap[lcPath].hCounter, - ref rawStruct, /*PPDH_RAW_COUNTER */ - _reloggerPathToHandleAndInstanceMap[lcPath].InstanceName); - } } } diff --git a/src/Microsoft.PowerShell.Commands.Diagnostics/PdhSafeHandle.cs b/src/Microsoft.PowerShell.Commands.Diagnostics/PdhSafeHandle.cs index eaa6a401b3f..2fc58cc00e1 100644 --- a/src/Microsoft.PowerShell.Commands.Diagnostics/PdhSafeHandle.cs +++ b/src/Microsoft.PowerShell.Commands.Diagnostics/PdhSafeHandle.cs @@ -1,12 +1,9 @@ -// -// Copyright (C) Microsoft. All rights reserved. -// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. using System; using System.Runtime.InteropServices; -using System.Runtime.ConstrainedExecution; - namespace Microsoft.Powershell.Commands.GetCounter.PdhNative { internal sealed class PdhSafeDataSourceHandle : SafeHandle @@ -21,17 +18,12 @@ public override bool IsInvalid } } - - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] protected override bool ReleaseHandle() { return (PdhHelper.PdhCloseLog(handle, 0) == 0); } } - - - internal sealed class PdhSafeQueryHandle : SafeHandle { private PdhSafeQueryHandle() : base(IntPtr.Zero, true) { } @@ -44,8 +36,6 @@ public override bool IsInvalid } } - - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] protected override bool ReleaseHandle() { return (PdhHelper.PdhCloseQuery(handle) == 0); @@ -64,8 +54,6 @@ public override bool IsInvalid } } - - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.MayFail)] protected override bool ReleaseHandle() { return (PdhHelper.PdhCloseLog(handle, 0) == 0); diff --git a/src/Microsoft.PowerShell.Commands.Diagnostics/resources/GetEventResources.resx b/src/Microsoft.PowerShell.Commands.Diagnostics/resources/GetEventResources.resx index 6f3f81dcce3..f8f41ecc2f5 100644 --- a/src/Microsoft.PowerShell.Commands.Diagnostics/resources/GetEventResources.resx +++ b/src/Microsoft.PowerShell.Commands.Diagnostics/resources/GetEventResources.resx @@ -185,7 +185,7 @@ The following export destination path is ambiguous: {0}. - The {0} performance counter path is not valid. + The {0} performance counter path is not valid. The data in one of the performance counter samples is not valid. View the Status property for each PerformanceCounterSample object to make sure it contains valid data. @@ -253,7 +253,7 @@ The defined template is following: The following value is not in a valid DateTime format: {0}. - Could not retrieve information about the {0} log. Error: {1}. + To access the '{0}' log start PowerShell with elevated user rights. Error: {1} Cannot retrieve event message text. @@ -268,7 +268,7 @@ The defined template is following: This cmdlet can be run only on Microsoft Windows 7 and above. - This Windows PowerShell snap-in contains Windows Eventing and Performance Counter cmdlets. + This PowerShell snap-in contains Windows Eventing and Performance Counter cmdlets. Cannot find any performance counter sets in the {0} files that match the following: {1}. @@ -280,7 +280,9 @@ The defined template is following: Cooked Values - You cannot import more than one comma-separated (.csv) or tab-separated (.tsv) performance counter file in each command. + You cannot import more than one comma-separated (.csv) or tab-separated (.tsv) performance counter file in each command. + + + Log count ({0}) is exceeded Windows Event Log API limit ({1}). Adjust filter to return less log names. - diff --git a/src/Microsoft.PowerShell.Commands.Diagnostics/resources/GetEventResources.txt b/src/Microsoft.PowerShell.Commands.Diagnostics/resources/GetEventResources.txt index 751578835cd..3e0744889e7 100644 --- a/src/Microsoft.PowerShell.Commands.Diagnostics/resources/GetEventResources.txt +++ b/src/Microsoft.PowerShell.Commands.Diagnostics/resources/GetEventResources.txt @@ -1,7 +1,7 @@ -#Copyright (c) 2008 Microsoft Corporation +#Copyright (c) Microsoft Corporation. Vendor=Microsoft -Description=This Windows PowerShell snap-in contains Windows Eventing and Performance Counter cmdlets. +Description=This PowerShell snap-in contains Windows Eventing and Performance Counter cmdlets. NoMatchingEventsFound=No events were found that match the specified selection criteria. NoMatchingLogsFound=There is not an event log on the {0} computer that matches "{1}". @@ -56,4 +56,4 @@ CounterCircularNoMaxSize=The Circular parameter will be ignored unless the MaxSi ExportCtrWin7Required=This cmdlet can be run only on Microsoft Windows 7 and above. FileOpenFailed=Unable to open the {0} file for writing. FileCreateFailed=Unable to create the {0} file. Verify that the path is valid. -ExportDestPathAmbiguous=The following export destination path is ambiguous: {0}. \ No newline at end of file +ExportDestPathAmbiguous=The following export destination path is ambiguous: {0}. diff --git a/src/Microsoft.PowerShell.Commands.Management/Microsoft.PowerShell.Commands.Management.csproj b/src/Microsoft.PowerShell.Commands.Management/Microsoft.PowerShell.Commands.Management.csproj index 0c5eacc0c59..b2622f0517f 100644 --- a/src/Microsoft.PowerShell.Commands.Management/Microsoft.PowerShell.Commands.Management.csproj +++ b/src/Microsoft.PowerShell.Commands.Management/Microsoft.PowerShell.Commands.Management.csproj @@ -1,10 +1,8 @@ - - - - + + - PowerShell Core's Microsoft.PowerShell.Commands.Management project - $(NoWarn);CS1570 + PowerShell's Microsoft.PowerShell.Commands.Management project + $(NoWarn);CS1570;CA1416 Microsoft.PowerShell.Commands.Management @@ -17,19 +15,15 @@ - - - - @@ -39,38 +33,21 @@ - - - - - - - - portable - - - - $(DefineConstants);UNIX - - - - full - - - + + diff --git a/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/SessionBasedWrapper.cs b/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/SessionBasedWrapper.cs index 56cbb10edc2..3e26b2e2e98 100644 --- a/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/SessionBasedWrapper.cs +++ b/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/SessionBasedWrapper.cs @@ -1,6 +1,5 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. using System; using System.Collections; @@ -38,7 +37,7 @@ internal SessionBasedCmdletAdapter() private bool _disposed; /// - /// Releases resources associated with this object + /// Releases resources associated with this object. /// public void Dispose() { @@ -47,7 +46,7 @@ public void Dispose() } /// - /// Releases resources associated with this object + /// Releases resources associated with this object. /// protected virtual void Dispose(bool disposing) { @@ -61,6 +60,7 @@ protected virtual void Dispose(bool disposing) _parentJob = null; } } + _disposed = true; } } @@ -70,35 +70,38 @@ protected virtual void Dispose(bool disposing) #region Common parameters (AsJob, ThrottleLimit, Session) /// - /// Session to operate on + /// Session to operate on. /// [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] protected TSession[] Session { - get { return _session ?? (_session = new TSession[] {this.DefaultSession}); } - set + get { - if (value == null) - { - throw new ArgumentNullException("value"); - } + return _session ??= new TSession[] { this.DefaultSession }; + } + set + { + ArgumentNullException.ThrowIfNull(value); _session = value; _sessionWasSpecified = true; } } + private TSession[] _session; private bool _sessionWasSpecified; /// - /// Whether to wrap and emit the whole operation as a background job + /// Whether to wrap and emit the whole operation as a background job. /// [Parameter] public SwitchParameter AsJob { get { return _asJob; } + set { _asJob = value; } } + private bool _asJob; /// @@ -114,17 +117,17 @@ public SwitchParameter AsJob /// /// Creates a object that performs a query against the wrapped object model. /// - /// Remote session to query - /// Query parameters + /// Remote session to query. + /// Query parameters. /// /// /// This method shouldn't do any processing or interact with the remote session. /// Doing so will interfere with ThrottleLimit functionality. /// /// - /// (and other methods returning job results) will block to support throttling and flow-control. + /// (and other methods returning job results) will block to support throttling and flow-control. /// Implementations of Job instance returned from this method should make sure that implementation-specific flow-control mechanism pauses further processing, - /// until calls from (and other methods returning job results) return. + /// until calls from (and other methods returning job results) return. /// /// internal abstract StartableJob CreateQueryJob(TSession session, QueryBuilder query); @@ -147,14 +150,15 @@ private StartableJob DoCreateQueryJob(TSession sessionForJob, QueryBuilder query discardNonPipelineResults, actionAgainstResults == null ? (Action)null - : delegate (PSObject pso) - { - var objectInstance = - (TObjectInstance) - LanguagePrimitives.ConvertTo(pso, typeof(TObjectInstance), - CultureInfo.InvariantCulture); - actionAgainstResults(sessionForJob, objectInstance); - }); + : ((PSObject pso) => + { + var objectInstance = + (TObjectInstance)LanguagePrimitives.ConvertTo( + pso, + typeof(TObjectInstance), + CultureInfo.InvariantCulture); + actionAgainstResults(sessionForJob, objectInstance); + })); } return queryJob; @@ -163,19 +167,19 @@ private StartableJob DoCreateQueryJob(TSession sessionForJob, QueryBuilder query /// /// Creates a object that invokes an instance method in the wrapped object model. /// - /// Remote session to invoke the method in - /// The object on which to invoke the method - /// Method invocation details - /// true if successful method invocations should emit downstream the being operated on + /// Remote session to invoke the method in. + /// The object on which to invoke the method. + /// Method invocation details. + /// if successful method invocations should emit downstream the being operated on. /// /// /// This method shouldn't do any processing or interact with the remote session. /// Doing so will interfere with ThrottleLimit functionality. /// /// - /// (and other methods returning job results) will block to support throttling and flow-control. + /// (and other methods returning job results) will block to support throttling and flow-control. /// Implementations of Job instance returned from this method should make sure that implementation-specific flow-control mechanism pauses further processing, - /// until calls from (and other methods returning job results) return. + /// until calls from (and other methods returning job results) return. /// /// internal abstract StartableJob CreateInstanceMethodInvocationJob(TSession session, TObjectInstance objectInstance, MethodInvocationInfo methodInvocationInfo, bool passThru); @@ -200,17 +204,17 @@ private StartableJob DoCreateInstanceMethodInvocationJob(TSession sessionForJob, /// /// Creates a object that invokes a static method in the wrapped object model. /// - /// Remote session to invoke the method in - /// Method invocation details + /// Remote session to invoke the method in. + /// Method invocation details. /// /// /// This method shouldn't do any processing or interact with the remote session. /// Doing so will interfere with ThrottleLimit functionality. /// /// - /// (and other methods returning job results) will block to support throttling and flow-control. + /// (and other methods returning job results) will block to support throttling and flow-control. /// Implementations of Job instance returned from this method should make sure that implementation-specific flow-control mechanism pauses further processing, - /// until calls from (and other methods returning job results) return. + /// until calls from (and other methods returning job results) return. /// /// internal abstract StartableJob CreateStaticMethodInvocationJob(TSession session, MethodInvocationInfo methodInvocationInfo); @@ -232,24 +236,21 @@ private StartableJob DoCreateStaticMethodInvocationJob(TSession sessionForJob, M return methodInvocationJob; } - private void HandleJobOutput(Job job, TSession sessionForJob, bool discardNonPipelineResults, Action outputAction) + private static void HandleJobOutput(Job job, TSession sessionForJob, bool discardNonPipelineResults, Action outputAction) { Action processOutput = - delegate (PSObject pso) + (PSObject pso) => { if (pso == null) { return; } - if (outputAction != null) - { - outputAction(pso); - } + outputAction?.Invoke(pso); }; job.Output.DataAdded += - delegate (object sender, DataAddedEventArgs eventArgs) + (object sender, DataAddedEventArgs eventArgs) => { var dataCollection = (PSDataCollection)sender; @@ -284,7 +285,7 @@ internal virtual TSession GetSessionOfOriginFromInstance(TObjectInstance instanc /// /// Returns default sessions to use when the user doesn't specify the -Session cmdlet parameter. /// - /// Default sessions to use when the user doesn't specify the -Session cmdlet parameter + /// Default sessions to use when the user doesn't specify the -Session cmdlet parameter. protected abstract TSession DefaultSession { get; } /// @@ -299,7 +300,7 @@ internal virtual TSession GetSessionOfOriginFromInstance(TObjectInstance instanc private static void DiscardJobOutputs(PSDataCollection psDataCollection) { psDataCollection.DataAdded += - delegate (object sender, DataAddedEventArgs e) + (object sender, DataAddedEventArgs e) => { var localDataCollection = (PSDataCollection)sender; localDataCollection.Clear(); @@ -320,39 +321,40 @@ private enum JobOutputs NonPipelineResults = Output | Error | Warning | Verbose | Debug | Progress, PipelineResults = Results, } + private static void DiscardJobOutputs(Job job, JobOutputs jobOutputsToDiscard) { - if (JobOutputs.Output == (jobOutputsToDiscard & JobOutputs.Output)) + if ((jobOutputsToDiscard & JobOutputs.Output) == JobOutputs.Output) { DiscardJobOutputs(job.Output); } - if (JobOutputs.Error == (jobOutputsToDiscard & JobOutputs.Error)) + if ((jobOutputsToDiscard & JobOutputs.Error) == JobOutputs.Error) { DiscardJobOutputs(job.Error); } - if (JobOutputs.Warning == (jobOutputsToDiscard & JobOutputs.Warning)) + if ((jobOutputsToDiscard & JobOutputs.Warning) == JobOutputs.Warning) { DiscardJobOutputs(job.Warning); } - if (JobOutputs.Verbose == (jobOutputsToDiscard & JobOutputs.Verbose)) + if ((jobOutputsToDiscard & JobOutputs.Verbose) == JobOutputs.Verbose) { DiscardJobOutputs(job.Verbose); } - if (JobOutputs.Debug == (jobOutputsToDiscard & JobOutputs.Debug)) + if ((jobOutputsToDiscard & JobOutputs.Debug) == JobOutputs.Debug) { DiscardJobOutputs(job.Debug); } - if (JobOutputs.Progress == (jobOutputsToDiscard & JobOutputs.Progress)) + if ((jobOutputsToDiscard & JobOutputs.Progress) == JobOutputs.Progress) { DiscardJobOutputs(job.Progress); } - if (JobOutputs.Results == (jobOutputsToDiscard & JobOutputs.Results)) + if ((jobOutputsToDiscard & JobOutputs.Results) == JobOutputs.Results) { DiscardJobOutputs(job.Results); } @@ -367,8 +369,8 @@ private static void DiscardJobOutputs(Job job, JobOutputs jobOutputsToDiscard) /// /// Queries for object instances in the object model. /// - /// Query parameters - /// A lazy evaluated collection of object instances + /// Query parameters. + /// A lazy evaluated collection of object instances. public override void ProcessRecord(QueryBuilder query) { _parentJob.DisableFlowControlForPendingCmdletActionsQueue(); @@ -390,11 +392,11 @@ public override void ProcessRecord(QueryBuilder query) } /// - /// Queries for instance and invokes an instance method + /// Queries for instance and invokes an instance method. /// - /// Query parameters - /// Method invocation details - /// true if successful method invocations should emit downstream the object instance being operated on + /// Query parameters. + /// Method invocation details. + /// if successful method invocations should emit downstream the object instance being operated on. public override void ProcessRecord(QueryBuilder query, MethodInvocationInfo methodInvocationInfo, bool passThru) { _parentJob.DisableFlowControlForPendingJobsQueue(); @@ -407,7 +409,7 @@ public override void ProcessRecord(QueryBuilder query, MethodInvocationInfo meth StartableJob queryJob = this.DoCreateQueryJob( sessionForJob, query, - delegate (TSession sessionForMethodInvocationJob, TObjectInstance objectInstance) + (TSession sessionForMethodInvocationJob, TObjectInstance objectInstance) => { StartableJob methodInvocationJob = this.DoCreateInstanceMethodInvocationJob( sessionForMethodInvocationJob, @@ -489,8 +491,7 @@ private IEnumerable GetSessionsToActAgainst(QueryBuilder queryBuilder) return this.Session; } - var sessionBoundQueryBuilder = queryBuilder as ISessionBoundQueryBuilder; - if (sessionBoundQueryBuilder != null) + if (queryBuilder is ISessionBoundQueryBuilder sessionBoundQueryBuilder) { TSession sessionOfTheQueryBuilder = sessionBoundQueryBuilder.GetTargetSession(); if (sessionOfTheQueryBuilder != null) @@ -524,6 +525,7 @@ private IEnumerable GetSessionsToActAgainst(MethodInvocationInfo metho associatedSessions.Add(associatedSession); } } + if (associatedSessions.Count == 1) { return associatedSessions; @@ -570,13 +572,14 @@ private TSession GetImpliedSession() /// /// Invokes an instance method in the object model. /// - /// The object on which to invoke the method - /// Method invocation details - /// true if successful method invocations should emit downstream the being operated on + /// The object on which to invoke the method. + /// Method invocation details. + /// if successful method invocations should emit downstream the being operated on. public override void ProcessRecord(TObjectInstance objectInstance, MethodInvocationInfo methodInvocationInfo, bool passThru) { - if (objectInstance == null) throw new ArgumentNullException("objectInstance"); - if (methodInvocationInfo == null) throw new ArgumentNullException("methodInvocationInfo"); + ArgumentNullException.ThrowIfNull(objectInstance); + + ArgumentNullException.ThrowIfNull(methodInvocationInfo); foreach (TSession sessionForJob in this.GetSessionsToActAgainst(objectInstance)) { @@ -598,10 +601,10 @@ public override void ProcessRecord(TObjectInstance objectInstance, MethodInvocat /// /// Invokes a static method in the object model. /// - /// Method invocation details + /// Method invocation details. public override void ProcessRecord(MethodInvocationInfo methodInvocationInfo) { - if (methodInvocationInfo == null) throw new ArgumentNullException("methodInvocationInfo"); + ArgumentNullException.ThrowIfNull(methodInvocationInfo); foreach (TSession sessionForJob in this.GetSessionsToActAgainst(methodInvocationInfo)) { @@ -637,6 +640,7 @@ public override void BeginProcessing() { conflictingParameter = "Confirm"; } + if (conflictingParameter != null) { string errorMessage = string.Format( @@ -680,10 +684,7 @@ public override void EndProcessing() public override void StopProcessing() { Job jobToStop = _parentJob; - if (jobToStop != null) - { - jobToStop.StopJob(); - } + jobToStop?.StopJob(); base.StopProcessing(); } diff --git a/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/cim/CimJobException.cs b/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/cim/CimJobException.cs index 53c531b79f7..935cc960c6c 100644 --- a/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/cim/CimJobException.cs +++ b/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/cim/CimJobException.cs @@ -1,20 +1,20 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. using System; using System.Globalization; using System.Management.Automation; using System.Runtime.Serialization; + using Microsoft.Management.Infrastructure; + using Dbg = System.Management.Automation.Diagnostics; namespace Microsoft.PowerShell.Cmdletization.Cim { /// - /// Represents an error during execution of a CIM job + /// Represents an error during execution of a CIM job. /// - [Serializable] public class CimJobException : SystemException, IContainsErrorRecord { #region Standard constructors and methods required for all exceptions @@ -49,32 +49,12 @@ public CimJobException(string message, Exception inner) : base(message, inner) /// /// The that holds the serialized object data about the exception being thrown. /// The that contains contextual information about the source or destination. + [Obsolete("Legacy serialization support is deprecated since .NET 8", DiagnosticId = "SYSLIB0051")] protected CimJobException( SerializationInfo info, - StreamingContext context) : base(info, context) - { - if (info == null) - { - throw new ArgumentNullException("info"); - } - - _errorRecord = (ErrorRecord)info.GetValue("errorRecord", typeof(ErrorRecord)); - } - - /// - /// Sets the SerializationInfo with information about the exception. - /// - /// The that holds the serialized object data about the exception being thrown. - /// The that contains contextual information about the source or destination. - public override void GetObjectData(SerializationInfo info, StreamingContext context) + StreamingContext context) { - if (info == null) - { - throw new ArgumentNullException("info"); - } - - base.GetObjectData(info, context); - info.AddValue("errorRecord", _errorRecord); + throw new NotSupportedException(); } #endregion @@ -89,7 +69,7 @@ internal static CimJobException CreateFromCimException( Dbg.Assert(cimException != null, "Caller should verify cimException != null"); string message = BuildErrorMessage(jobDescription, jobContext, cimException.Message); - CimJobException cimJobException = new CimJobException(message, cimException); + CimJobException cimJobException = new(message, cimException); cimJobException.InitializeErrorRecord(jobContext, cimException); return cimJobException; } @@ -103,16 +83,14 @@ internal static CimJobException CreateFromAnyException( Dbg.Assert(jobContext != null, "Caller should verify jobContext != null"); Dbg.Assert(inner != null, "Caller should verify inner != null"); - CimException cimException = inner as CimException; - if (cimException != null) + if (inner is CimException cimException) { return CreateFromCimException(jobDescription, jobContext, cimException); } string message = BuildErrorMessage(jobDescription, jobContext, inner.Message); - CimJobException cimJobException = new CimJobException(message, inner); - var containsErrorRecord = inner as IContainsErrorRecord; - if (containsErrorRecord != null) + CimJobException cimJobException = new(message, inner); + if (inner is IContainsErrorRecord containsErrorRecord) { cimJobException.InitializeErrorRecord( jobContext, @@ -126,6 +104,7 @@ internal static CimJobException CreateFromAnyException( errorId: "CimJob_" + inner.GetType().Name, errorCategory: ErrorCategory.NotSpecified); } + return cimJobException; } @@ -140,7 +119,7 @@ internal static CimJobException CreateWithFullControl( Dbg.Assert(!string.IsNullOrEmpty(message), "Caller should verify message != null"); Dbg.Assert(!string.IsNullOrEmpty(errorId), "Caller should verify errorId != null"); - CimJobException cimJobException = new CimJobException(jobContext.PrependComputerNameToMessage(message), inner); + CimJobException cimJobException = new(jobContext.PrependComputerNameToMessage(message), inner); cimJobException.InitializeErrorRecord(jobContext, errorId, errorCategory); return cimJobException; } @@ -154,7 +133,7 @@ internal static CimJobException CreateWithoutJobContext( Dbg.Assert(!string.IsNullOrEmpty(message), "Caller should verify message != null"); Dbg.Assert(!string.IsNullOrEmpty(errorId), "Caller should verify errorId != null"); - CimJobException cimJobException = new CimJobException(message, inner); + CimJobException cimJobException = new(message, inner); cimJobException.InitializeErrorRecord(null, errorId, errorCategory); return cimJobException; } @@ -168,7 +147,7 @@ internal static CimJobException CreateFromMethodErrorCode(string jobDescription, string errorMessage = BuildErrorMessage(jobDescription, jobContext, rawErrorMessage); - CimJobException cje = new CimJobException(errorMessage); + CimJobException cje = new(errorMessage); cje.InitializeErrorRecord(jobContext, "CimJob_" + methodName + "_" + errorCodeFromMethod, ErrorCategory.InvalidResult); return cje; @@ -195,15 +174,15 @@ private static string BuildErrorMessage(string jobDescription, CimJobContext job private void InitializeErrorRecordCore(CimJobContext jobContext, Exception exception, string errorId, ErrorCategory errorCategory) { - ErrorRecord coreErrorRecord = new ErrorRecord( + ErrorRecord coreErrorRecord = new( exception: exception, errorId: errorId, errorCategory: errorCategory, - targetObject: jobContext != null ? jobContext.TargetObject : null); + targetObject: jobContext?.TargetObject); if (jobContext != null) { - System.Management.Automation.Remoting.OriginInfo originInfo = new System.Management.Automation.Remoting.OriginInfo( + System.Management.Automation.Remoting.OriginInfo originInfo = new( jobContext.Session.ComputerName, Guid.Empty); @@ -240,7 +219,7 @@ private void InitializeErrorRecord(CimJobContext jobContext, CimException cimExc if (cimException.ErrorData != null) { _errorRecord.CategoryInfo.TargetName = cimException.ErrorSource; - _errorRecord.CategoryInfo.TargetType = jobContext != null ? jobContext.CmdletizationClassName : null; + _errorRecord.CategoryInfo.TargetType = jobContext?.CmdletizationClassName; } } @@ -268,57 +247,57 @@ private static ErrorCategory ConvertCimNativeErrorCodeToErrorCategory(NativeErro case NativeErrorCode.Ok: return ErrorCategory.NotSpecified; case NativeErrorCode.Failed: - return ErrorCategory.NotSpecified; ; + return ErrorCategory.NotSpecified; case NativeErrorCode.AccessDenied: - return ErrorCategory.PermissionDenied; ; + return ErrorCategory.PermissionDenied; case NativeErrorCode.InvalidNamespace: - return ErrorCategory.MetadataError; ; + return ErrorCategory.MetadataError; case NativeErrorCode.InvalidParameter: - return ErrorCategory.InvalidArgument; ; + return ErrorCategory.InvalidArgument; case NativeErrorCode.InvalidClass: - return ErrorCategory.MetadataError; ; + return ErrorCategory.MetadataError; case NativeErrorCode.NotFound: - return ErrorCategory.ObjectNotFound; ; + return ErrorCategory.ObjectNotFound; case NativeErrorCode.NotSupported: - return ErrorCategory.NotImplemented; ; + return ErrorCategory.NotImplemented; case NativeErrorCode.ClassHasChildren: - return ErrorCategory.MetadataError; ; + return ErrorCategory.MetadataError; case NativeErrorCode.ClassHasInstances: - return ErrorCategory.MetadataError; ; + return ErrorCategory.MetadataError; case NativeErrorCode.InvalidSuperClass: - return ErrorCategory.MetadataError; ; + return ErrorCategory.MetadataError; case NativeErrorCode.AlreadyExists: - return ErrorCategory.ResourceExists; ; + return ErrorCategory.ResourceExists; case NativeErrorCode.NoSuchProperty: - return ErrorCategory.MetadataError; ; + return ErrorCategory.MetadataError; case NativeErrorCode.TypeMismatch: - return ErrorCategory.InvalidType; ; + return ErrorCategory.InvalidType; case NativeErrorCode.QueryLanguageNotSupported: - return ErrorCategory.NotImplemented; ; + return ErrorCategory.NotImplemented; case NativeErrorCode.InvalidQuery: - return ErrorCategory.InvalidArgument; ; + return ErrorCategory.InvalidArgument; case NativeErrorCode.MethodNotAvailable: - return ErrorCategory.MetadataError; ; + return ErrorCategory.MetadataError; case NativeErrorCode.MethodNotFound: - return ErrorCategory.MetadataError; ; + return ErrorCategory.MetadataError; case NativeErrorCode.NamespaceNotEmpty: - return ErrorCategory.MetadataError; ; + return ErrorCategory.MetadataError; case NativeErrorCode.InvalidEnumerationContext: - return ErrorCategory.MetadataError; ; + return ErrorCategory.MetadataError; case NativeErrorCode.InvalidOperationTimeout: - return ErrorCategory.InvalidArgument; ; + return ErrorCategory.InvalidArgument; case NativeErrorCode.PullHasBeenAbandoned: - return ErrorCategory.OperationStopped; ; + return ErrorCategory.OperationStopped; case NativeErrorCode.PullCannotBeAbandoned: - return ErrorCategory.CloseError; ; + return ErrorCategory.CloseError; case NativeErrorCode.FilteredEnumerationNotSupported: - return ErrorCategory.NotImplemented; ; + return ErrorCategory.NotImplemented; case NativeErrorCode.ContinuationOnErrorNotSupported: - return ErrorCategory.NotImplemented; ; + return ErrorCategory.NotImplemented; case NativeErrorCode.ServerLimitsExceeded: - return ErrorCategory.ResourceBusy; ; + return ErrorCategory.ResourceBusy; case NativeErrorCode.ServerIsShuttingDown: - return ErrorCategory.ResourceUnavailable; ; + return ErrorCategory.ResourceUnavailable; default: return ErrorCategory.NotSpecified; } @@ -353,14 +332,14 @@ public ErrorRecord ErrorRecord { get { return _errorRecord; } } + private ErrorRecord _errorRecord; internal bool IsTerminatingError { get { - var cimException = this.InnerException as CimException; - if ((cimException == null) || (cimException.ErrorData == null)) + if ((this.InnerException is not CimException cimException) || (cimException.ErrorData == null)) { return false; } @@ -371,7 +350,7 @@ internal bool IsTerminatingError return false; } - UInt16 perceivedSeverityValue = (UInt16)perceivedSeverityProperty.Value; + ushort perceivedSeverityValue = (ushort)perceivedSeverityProperty.Value; if (perceivedSeverityValue != 7) { /* from CIM Schema: Interop\CIM_Error.mof: diff --git a/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/cim/CreateInstanceJob.cs b/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/cim/CreateInstanceJob.cs index 9710f3044a9..eb594dd4eea 100644 --- a/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/cim/CreateInstanceJob.cs +++ b/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/cim/CreateInstanceJob.cs @@ -1,17 +1,18 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. using System; + using Microsoft.Management.Infrastructure; + using Dbg = System.Management.Automation.Diagnostics; namespace Microsoft.PowerShell.Cmdletization.Cim { /// - /// Job wrapping invocation of a CreateInstance intrinsic CIM method + /// Job wrapping invocation of a CreateInstance intrinsic CIM method. /// - internal class CreateInstanceJob : PropertySettingJob + internal sealed class CreateInstanceJob : PropertySettingJob { private CimInstance _resultFromCreateInstance; private CimInstance _resultFromGetInstance; @@ -66,8 +67,8 @@ internal override IObservable GetCimOperation() } #if DEBUG - Dbg.Assert(_getInstanceOperationGotStarted == false, "CreateInstance should be started *before* GetInstance"); - Dbg.Assert(_createInstanceOperationGotStarted == false, "Should not start CreateInstance operation twice"); + Dbg.Assert(!_getInstanceOperationGotStarted, "CreateInstance should be started *before* GetInstance"); + Dbg.Assert(!_createInstanceOperationGotStarted, "Should not start CreateInstance operation twice"); _createInstanceOperationGotStarted = true; #endif return GetCreateInstanceOperation(); @@ -75,8 +76,8 @@ internal override IObservable GetCimOperation() else { #if DEBUG - Dbg.Assert(_createInstanceOperationGotStarted == true, "GetInstance should be started *after* CreateInstance"); - Dbg.Assert(_getInstanceOperationGotStarted == false, "Should not start GetInstance operation twice"); + Dbg.Assert(_createInstanceOperationGotStarted, "GetInstance should be started *after* CreateInstance"); + Dbg.Assert(!_getInstanceOperationGotStarted, "Should not start GetInstance operation twice"); Dbg.Assert(_resultFromGetInstance == null, "GetInstance operation shouldn't happen twice"); _getInstanceOperationGotStarted = true; #endif diff --git a/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/cim/DeleteInstanceJob.cs b/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/cim/DeleteInstanceJob.cs index 075be25cccc..0432c6c9a8f 100644 --- a/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/cim/DeleteInstanceJob.cs +++ b/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/cim/DeleteInstanceJob.cs @@ -1,17 +1,18 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. using System; + using Microsoft.Management.Infrastructure; + using Dbg = System.Management.Automation.Diagnostics; namespace Microsoft.PowerShell.Cmdletization.Cim { /// - /// Job wrapping invocation of a DeleteInstance intrinsic CIM method + /// Job wrapping invocation of a DeleteInstance intrinsic CIM method. /// - internal class DeleteInstanceJob : MethodInvocationJobBase + internal sealed class DeleteInstanceJob : MethodInvocationJobBase { private readonly CimInstance _objectToDelete; diff --git a/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/cim/EnumerateAssociatedInstancesJob.cs b/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/cim/EnumerateAssociatedInstancesJob.cs index 9ea248ff767..569740d0d1b 100644 --- a/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/cim/EnumerateAssociatedInstancesJob.cs +++ b/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/cim/EnumerateAssociatedInstancesJob.cs @@ -1,19 +1,20 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. using System; using System.Globalization; using System.Management.Automation; + using Microsoft.Management.Infrastructure; + using Dbg = System.Management.Automation.Diagnostics; namespace Microsoft.PowerShell.Cmdletization.Cim { /// - /// Job that handles executing a WQL (in the future CQL?) query on a remote CIM server + /// Job that handles executing a WQL (in the future CQL?) query on a remote CIM server. /// - internal class EnumerateAssociatedInstancesJob : QueryJobBase + internal sealed class EnumerateAssociatedInstancesJob : QueryJobBase { private readonly CimInstance _associatedObject; private readonly string _associationName; @@ -90,6 +91,7 @@ internal override void WriteObject(object outputObject) PSObject pso = PSObject.AsPSObject(outputObject); AddShowComputerNameMarker(pso); } + base.WriteObject(outputObject); } diff --git a/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/cim/ExtrinsicMethodInvocationJob.cs b/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/cim/ExtrinsicMethodInvocationJob.cs index 023a1fda977..9eaac2b3d7f 100644 --- a/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/cim/ExtrinsicMethodInvocationJob.cs +++ b/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/cim/ExtrinsicMethodInvocationJob.cs @@ -1,6 +1,5 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. using System; using System.Collections; @@ -8,14 +7,16 @@ using System.Globalization; using System.Linq; using System.Management.Automation; + using Microsoft.Management.Infrastructure; using Microsoft.PowerShell.Cim; + using Dbg = System.Management.Automation.Diagnostics; namespace Microsoft.PowerShell.Cmdletization.Cim { /// - /// Job wrapping invocation of an extrinsic CIM method + /// Job wrapping invocation of an extrinsic CIM method. /// internal abstract class ExtrinsicMethodInvocationJob : MethodInvocationJobBase { @@ -51,22 +52,21 @@ private void ProcessOutParameter(CimMethodResult methodResult, MethodParameter m { Dbg.Assert(methodResult != null, "Caller should verify methodResult != null"); Dbg.Assert(methodParameter != null, "Caller should verify methodParameter != null"); - Dbg.Assert(0 != (methodParameter.Bindings & (MethodParameterBindings.Out | MethodParameterBindings.Error)), "Caller should verify that this is an out parameter"); + Dbg.Assert((methodParameter.Bindings & (MethodParameterBindings.Out | MethodParameterBindings.Error)) != 0, "Caller should verify that this is an out parameter"); Dbg.Assert(cmdletOutput != null, "Caller should verify cmdletOutput != null"); Dbg.Assert(this.MethodSubject != null, "MethodSubject property should be initialized before starting main job processing"); CimMethodParameter outParameter = methodResult.OutParameters[methodParameter.Name]; - object valueReturnedFromMethod = (outParameter == null) ? null : outParameter.Value; + object valueReturnedFromMethod = outParameter?.Value; object dotNetValue = CimValueConverter.ConvertFromCimToDotNet(valueReturnedFromMethod, methodParameter.ParameterType); - if (MethodParameterBindings.Out == (methodParameter.Bindings & MethodParameterBindings.Out)) + if ((methodParameter.Bindings & MethodParameterBindings.Out) == MethodParameterBindings.Out) { methodParameter.Value = dotNetValue; cmdletOutput.Add(methodParameter.Name, methodParameter); - var cimInstances = dotNetValue as CimInstance[]; - if (cimInstances != null) + if (dotNetValue is CimInstance[] cimInstances) { foreach (var instance in cimInstances) { @@ -74,13 +74,12 @@ private void ProcessOutParameter(CimMethodResult methodResult, MethodParameter m } } - var cimInstance = dotNetValue as CimInstance; - if (cimInstance != null) + if (dotNetValue is CimInstance cimInstance) { CimCmdletAdapter.AssociateSessionOfOriginWithInstance(cimInstance, this.JobContext.Session); } } - else if (MethodParameterBindings.Error == (methodParameter.Bindings & MethodParameterBindings.Error)) + else if ((methodParameter.Bindings & MethodParameterBindings.Error) == MethodParameterBindings.Error) { var gotError = (bool)LanguagePrimitives.ConvertTo(dotNetValue, typeof(bool), CultureInfo.InvariantCulture); if (gotError) @@ -104,7 +103,7 @@ private void OnNext(CimMethodResult methodResult) if (cmdletOutput.Count == 1) { - var singleOutputParameter = cmdletOutput.Values.Single(); + var singleOutputParameter = cmdletOutput.Values.First(); if (singleOutputParameter.Value == null) { return; @@ -131,6 +130,7 @@ private void OnNext(CimMethodResult methodResult) var tmp = new PSNoteProperty(element.Key, element.Value.Value); propertyBag.Properties.Add(tmp); } + this.WriteObject(propertyBag); } } @@ -180,6 +180,7 @@ private void WriteObject(object cmdletOutput, MethodParameter methodParameter) pso.TypeNames.Insert(0, methodParameter.ParameterTypeName); } } + this.WriteObject(cmdletOutput); } @@ -188,15 +189,13 @@ public override void OnNext(CimMethodResultBase item) this.ExceptionSafeWrapper( delegate { - var methodResult = item as CimMethodResult; - if (methodResult != null) + if (item is CimMethodResult methodResult) { this.OnNext(methodResult); return; } - var streamedResult = item as CimMethodStreamedResult; - if (streamedResult != null) + if (item is CimMethodStreamedResult streamedResult) { this.OnNext(streamedResult); return; diff --git a/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/cim/InstanceMethodInvocationJob.cs b/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/cim/InstanceMethodInvocationJob.cs index d9d7db6a2ab..06a45933522 100644 --- a/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/cim/InstanceMethodInvocationJob.cs +++ b/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/cim/InstanceMethodInvocationJob.cs @@ -1,18 +1,19 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. using System; + using Microsoft.Management.Infrastructure; using Microsoft.Management.Infrastructure.Options; + using Dbg = System.Management.Automation.Diagnostics; namespace Microsoft.PowerShell.Cmdletization.Cim { /// - /// Job wrapping invocation of an extrinsic CIM method + /// Job wrapping invocation of an extrinsic CIM method. /// - internal class InstanceMethodInvocationJob : ExtrinsicMethodInvocationJob + internal sealed class InstanceMethodInvocationJob : ExtrinsicMethodInvocationJob { private readonly CimInstance _targetInstance; diff --git a/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/cim/MethodInvocationJobBase.cs b/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/cim/MethodInvocationJobBase.cs index 273b25cc619..84b13b82097 100644 --- a/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/cim/MethodInvocationJobBase.cs +++ b/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/cim/MethodInvocationJobBase.cs @@ -1,21 +1,22 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. using System; using System.Collections.Generic; using System.Globalization; using System.Linq; using System.Management.Automation; + using Microsoft.Management.Infrastructure; using Microsoft.Management.Infrastructure.Options; using Microsoft.PowerShell.Cim; + using Dbg = System.Management.Automation.Diagnostics; namespace Microsoft.PowerShell.Cmdletization.Cim { /// - /// Job wrapping invocation of an extrinsic CIM method + /// Job wrapping invocation of an extrinsic CIM method. /// internal abstract class MethodInvocationJobBase : CimChildJobBase { @@ -59,13 +60,14 @@ private IEnumerable GetMethodInputParametersCore(Func GetMethodInputParameters() { - var allMethodParameters = this.GetMethodInputParametersCore(p => !p.Name.StartsWith(CustomOperationOptionPrefix, StringComparison.OrdinalIgnoreCase)); - var methodParametersWithInputValue = allMethodParameters.Where(p => p.IsValuePresent); + var allMethodParameters = this.GetMethodInputParametersCore(static p => !p.Name.StartsWith(CustomOperationOptionPrefix, StringComparison.OrdinalIgnoreCase)); + var methodParametersWithInputValue = allMethodParameters.Where(static p => p.IsValuePresent); return methodParametersWithInputValue; } @@ -79,7 +81,7 @@ internal override CimCustomOptionsDictionary CalculateJobSpecificCustomOptions() IDictionary result = new Dictionary(StringComparer.OrdinalIgnoreCase); IEnumerable customOptions = this - .GetMethodInputParametersCore(p => p.Name.StartsWith(CustomOperationOptionPrefix, StringComparison.OrdinalIgnoreCase)); + .GetMethodInputParametersCore(static p => p.Name.StartsWith(CustomOperationOptionPrefix, StringComparison.OrdinalIgnoreCase)); foreach (MethodParameter customOption in customOptions) { if (customOption.Value == null) @@ -102,7 +104,7 @@ internal IEnumerable GetMethodOutputParameters() } var outParameters = allParameters_plus_returnValue - .Where(p => (0 != (p.Bindings & (MethodParameterBindings.Out | MethodParameterBindings.Error)))); + .Where(static p => ((p.Bindings & (MethodParameterBindings.Out | MethodParameterBindings.Error)) != 0)); return outParameters; } diff --git a/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/cim/ModifyInstanceJob.cs b/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/cim/ModifyInstanceJob.cs index 4efc7d12b66..869002a55f4 100644 --- a/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/cim/ModifyInstanceJob.cs +++ b/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/cim/ModifyInstanceJob.cs @@ -1,18 +1,19 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. using System; using System.Management.Automation; + using Microsoft.Management.Infrastructure; + using Dbg = System.Management.Automation.Diagnostics; namespace Microsoft.PowerShell.Cmdletization.Cim { /// - /// Job wrapping invocation of a ModifyInstance intrinsic CIM method + /// Job wrapping invocation of a ModifyInstance intrinsic CIM method. /// - internal class ModifyInstanceJob : PropertySettingJob + internal sealed class ModifyInstanceJob : PropertySettingJob { private CimInstance _resultFromModifyInstance; private bool _resultFromModifyInstanceHasBeenPassedThru; @@ -66,6 +67,7 @@ internal override object PassThruObject PSObject pso = PSObject.AsPSObject(_resultFromModifyInstance); AddShowComputerNameMarker(pso); } + _resultFromModifyInstanceHasBeenPassedThru = true; return _resultFromModifyInstance; } diff --git a/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/cim/PropertySettingJob.cs b/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/cim/PropertySettingJob.cs index c92ad7e548f..f5bdc0745cd 100644 --- a/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/cim/PropertySettingJob.cs +++ b/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/cim/PropertySettingJob.cs @@ -1,6 +1,5 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. using Microsoft.Management.Infrastructure; using Microsoft.PowerShell.Cim; @@ -8,7 +7,7 @@ namespace Microsoft.PowerShell.Cmdletization.Cim { /// - /// Job wrapping invocation of a CreateInstance or ModifyInstance intrinsic CIM method + /// Job wrapping invocation of a CreateInstance or ModifyInstance intrinsic CIM method. /// internal abstract class PropertySettingJob : MethodInvocationJobBase { diff --git a/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/cim/QueryJob.cs b/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/cim/QueryJob.cs index 8a3d45715c7..7bd2bd17531 100644 --- a/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/cim/QueryJob.cs +++ b/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/cim/QueryJob.cs @@ -1,22 +1,24 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. using System; using System.Globalization; using System.Text; + using Microsoft.Management.Infrastructure; + using Dbg = System.Management.Automation.Diagnostics; namespace Microsoft.PowerShell.Cmdletization.Cim { /// - /// Job that handles executing a WQL (in the future CQL?) query on a remote CIM server + /// Job that handles executing a WQL (in the future CQL?) query on a remote CIM server. /// - internal class QueryInstancesJob : QueryJobBase + internal sealed class QueryInstancesJob : QueryJobBase { private readonly string _wqlQuery; private readonly bool _useEnumerateInstances; + internal QueryInstancesJob(CimJobContext jobContext, CimQuery cimQuery, string wqlCondition) : base(jobContext, cimQuery) { @@ -25,7 +27,7 @@ internal QueryInstancesJob(CimJobContext jobContext, CimQuery cimQuery, string w var wqlQueryBuilder = new StringBuilder(); wqlQueryBuilder.Append("SELECT * FROM "); wqlQueryBuilder.Append(this.JobContext.ClassName); - wqlQueryBuilder.Append(" "); + wqlQueryBuilder.Append(' '); wqlQueryBuilder.Append(wqlCondition); _wqlQuery = wqlQueryBuilder.ToString(); diff --git a/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/cim/QueryJobBase.cs b/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/cim/QueryJobBase.cs index 3f04a81ecdc..b52c23c3dd4 100644 --- a/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/cim/QueryJobBase.cs +++ b/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/cim/QueryJobBase.cs @@ -1,19 +1,20 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. using System.Management.Automation; + using Microsoft.Management.Infrastructure; + using Dbg = System.Management.Automation.Diagnostics; namespace Microsoft.PowerShell.Cmdletization.Cim { /// - /// Base job for queries + /// Base job for queries. /// internal abstract class QueryJobBase : CimChildJobBase { - private CimQuery _cimQuery; + private readonly CimQuery _cimQuery; internal QueryJobBase(CimJobContext jobContext, CimQuery cimQuery) : base(jobContext) @@ -37,6 +38,7 @@ public override void OnNext(CimInstance item) { return; } + this.WriteObject(item); }); } @@ -53,6 +55,7 @@ public override void OnCompleted() { errorId = errorId + "_" + notFoundError.PropertyName; } + CimJobException cimJobException = CimJobException.CreateWithFullControl( this.JobContext, notFoundError.ErrorMessageGenerator(this.Description, this.JobContext.ClassName), @@ -62,6 +65,7 @@ public override void OnCompleted() { cimJobException.ErrorRecord.SetTargetObject(notFoundError.PropertyValue); } + this.WriteError(cimJobException.ErrorRecord); } }); diff --git a/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/cim/StaticMethodInvocationJob.cs b/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/cim/StaticMethodInvocationJob.cs index 5b47d46c6fc..3bc376ab3d5 100644 --- a/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/cim/StaticMethodInvocationJob.cs +++ b/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/cim/StaticMethodInvocationJob.cs @@ -1,17 +1,17 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. using System; + using Microsoft.Management.Infrastructure; using Microsoft.Management.Infrastructure.Options; namespace Microsoft.PowerShell.Cmdletization.Cim { /// - /// Job wrapping invocation of a static CIM method + /// Job wrapping invocation of a static CIM method. /// - internal class StaticMethodInvocationJob : ExtrinsicMethodInvocationJob + internal sealed class StaticMethodInvocationJob : ExtrinsicMethodInvocationJob { internal StaticMethodInvocationJob(CimJobContext jobContext, MethodInvocationInfo methodInvocationInfo) : base(jobContext, false /* passThru */, jobContext.CmdletizationClassName, methodInvocationInfo) diff --git a/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/cim/TerminatingErrorTracker.cs b/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/cim/TerminatingErrorTracker.cs index b00184f0381..6b716fa5501 100644 --- a/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/cim/TerminatingErrorTracker.cs +++ b/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/cim/TerminatingErrorTracker.cs @@ -1,6 +1,5 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. using System; using System.Collections; @@ -10,20 +9,22 @@ using System.Management.Automation.Remoting; using System.Runtime.CompilerServices; using System.Threading; + using Microsoft.Management.Infrastructure; + using Dbg = System.Management.Automation.Diagnostics; namespace Microsoft.PowerShell.Cmdletization.Cim { /// - /// Tracks (per-session) terminating errors in a given cmdlet invocation + /// Tracks (per-session) terminating errors in a given cmdlet invocation. /// - internal class TerminatingErrorTracker + internal sealed class TerminatingErrorTracker { #region Getting tracker for a given cmdlet invocation private static readonly ConditionalWeakTable s_invocationToTracker = - new ConditionalWeakTable(); + new(); private static int GetNumberOfSessions(InvocationInfo invocationInfo) { @@ -48,11 +49,11 @@ private static int GetNumberOfSessions(InvocationInfo invocationInfo) // - this translates into potentially unlimited number of CimSession we will work with return int.MaxValue; } + int maxNumberOfSessionsIndicatedByCimInstanceArguments = 1; foreach (object cmdletArgument in invocationInfo.BoundParameters.Values) { - CimInstance[] array = cmdletArgument as CimInstance[]; - if (array != null) + if (cmdletArgument is CimInstance[] array) { int numberOfSessionsAssociatedWithArgument = array .Select(CimCmdletAdapter.GetSessionOfOriginFromCimInstance) @@ -63,6 +64,7 @@ private static int GetNumberOfSessions(InvocationInfo invocationInfo) numberOfSessionsAssociatedWithArgument); } } + return maxNumberOfSessionsIndicatedByCimInstanceArguments; } @@ -97,7 +99,7 @@ private TerminatingErrorTracker(int numberOfSessions) #region Tracking session's "connectivity" status - private readonly ConcurrentDictionary _sessionToIsConnected = new ConcurrentDictionary(); + private readonly ConcurrentDictionary _sessionToIsConnected = new(); internal void MarkSessionAsConnected(CimSession connectedSession) { @@ -111,6 +113,7 @@ internal bool DidSessionAlreadyPassedConnectivityTest(CimSession session) { return alreadyPassedConnectivityTest; } + return false; } @@ -162,7 +165,7 @@ internal Exception GetExceptionIfBrokenSession( #region Tracking session's "terminated" status - private readonly ConcurrentDictionary _sessionToIsTerminated = new ConcurrentDictionary(); + private readonly ConcurrentDictionary _sessionToIsTerminated = new(); internal void MarkSessionAsTerminated(CimSession terminatedSession, out bool sessionWasAlreadyTerminated) { @@ -171,11 +174,11 @@ internal void MarkSessionAsTerminated(CimSession terminatedSession, out bool ses key: terminatedSession, addValue: true, updateValueFactory: - delegate (CimSession key, bool isTerminatedValueInDictionary) - { - closureSafeSessionWasAlreadyTerminated = isTerminatedValueInDictionary; - return true; - }); + (CimSession key, bool isTerminatedValueInDictionary) => + { + closureSafeSessionWasAlreadyTerminated = isTerminatedValueInDictionary; + return true; + }); sessionWasAlreadyTerminated = closureSafeSessionWasAlreadyTerminated; } @@ -192,22 +195,22 @@ internal bool IsSessionTerminated(CimSession session) internal CmdletMethodInvoker GetErrorReportingDelegate(ErrorRecord errorRecord) { - ManualResetEventSlim manualResetEventSlim = new ManualResetEventSlim(); - object lockObject = new object(); - Func action = delegate (Cmdlet cmdlet) - { - _numberOfReportedSessionTerminatingErrors++; - if (_numberOfReportedSessionTerminatingErrors >= _numberOfSessions) - { - cmdlet.ThrowTerminatingError(errorRecord); - } - else - { - cmdlet.WriteError(errorRecord); - } - - return false; // not really needed here, but required by CmdletMethodInvoker - }; + ManualResetEventSlim manualResetEventSlim = new(); + object lockObject = new(); + Func action = (Cmdlet cmdlet) => + { + _numberOfReportedSessionTerminatingErrors++; + if (_numberOfReportedSessionTerminatingErrors >= _numberOfSessions) + { + cmdlet.ThrowTerminatingError(errorRecord); + } + else + { + cmdlet.WriteError(errorRecord); + } + + return false; // not really needed here, but required by CmdletMethodInvoker + }; return new CmdletMethodInvoker { diff --git a/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/cim/cimChildJobBase.cs b/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/cim/cimChildJobBase.cs index 30fec5de0b4..6bd2bee7fee 100644 --- a/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/cim/cimChildJobBase.cs +++ b/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/cim/cimChildJobBase.cs @@ -1,6 +1,5 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. using System; using System.Collections.Concurrent; @@ -10,15 +9,17 @@ using System.Management.Automation.Remoting; using System.Management.Automation.Remoting.Internal; using System.Threading; + using Microsoft.Management.Infrastructure; using Microsoft.Management.Infrastructure.Options; using Microsoft.PowerShell.Cim; + using Dbg = System.Management.Automation.Diagnostics; namespace Microsoft.PowerShell.Cmdletization.Cim { /// - /// Base class for all child jobs that wrap CIM operations + /// Base class for all child jobs that wrap CIM operations. /// internal abstract class CimChildJobBase : StartableJob, @@ -26,6 +27,7 @@ internal abstract class CimChildJobBase : { private static long s_globalJobNumberCounter; private readonly long _myJobNumber = Interlocked.Increment(ref s_globalJobNumberCounter); + private const string CIMJobType = "CimJob"; internal CimJobContext JobContext @@ -35,6 +37,7 @@ internal CimJobContext JobContext return _jobContext; } } + private readonly CimJobContext _jobContext; internal CimChildJobBase(CimJobContext jobContext) @@ -54,7 +57,8 @@ internal CimChildJobBase(CimJobContext jobContext) _jobSpecificCustomOptions = new Lazy(this.CalculateJobSpecificCustomOptions); } - private CimSensitiveValueConverter _cimSensitiveValueConverter = new CimSensitiveValueConverter(); + private readonly CimSensitiveValueConverter _cimSensitiveValueConverter = new(); + internal CimSensitiveValueConverter CimSensitiveValueConverter { get { return _cimSensitiveValueConverter; } } internal abstract IObservable GetCimOperation(); @@ -80,8 +84,7 @@ private enum WsManErrorCode : uint private static bool IsWsManQuotaReached(Exception exception) { - var cimException = exception as CimException; - if (cimException == null) + if (exception is not CimException cimException) { return false; } @@ -102,12 +105,13 @@ private static bool IsWsManQuotaReached(Exception exception) { return false; } + if (errorCodeProperty.CimType != CimType.UInt32) { return false; } - WsManErrorCode wsManErrorCode = (WsManErrorCode)(UInt32)(errorCodeProperty.Value); + WsManErrorCode wsManErrorCode = (WsManErrorCode)(uint)(errorCodeProperty.Value); switch (wsManErrorCode) // error codes that should result in sleep-and-retry are based on an email from Ryan { case WsManErrorCode.ERROR_WSMAN_QUOTA_MAX_SHELLS: @@ -154,13 +158,16 @@ public virtual void OnCompleted() }); } - private static readonly Random s_globalRandom = new Random(); + private static readonly Random s_globalRandom = new(); private readonly Random _random; private int _sleepAndRetryDelayRangeMs = 1000; private int _sleepAndRetryExtraDelayMs = 0; + private const int MaxRetryDelayMs = 15 * 1000; private const int MinRetryDelayMs = 100; + private Timer _sleepAndRetryTimer; + private void SleepAndRetry_OnWakeup(object state) { this.ExceptionSafeWrapper( @@ -173,15 +180,18 @@ private void SleepAndRetry_OnWakeup(object state) _sleepAndRetryTimer.Dispose(); _sleepAndRetryTimer = null; } + if (_jobWasStopped) { this.SetCompletedJobState(JobState.Stopped, null); return; } } + this.StartJob(); }); } + private void SleepAndRetry() { int tmpRandomDelay = _random.Next(0, _sleepAndRetryDelayRangeMs); @@ -219,7 +229,7 @@ private void SleepAndRetry() } /// - /// Indicates a location where this job is running + /// Indicates a location where this job is running. /// public override string Location { @@ -239,7 +249,7 @@ public override string Location } /// - /// Status message associated with the Job + /// Status message associated with the Job. /// public override string StatusMessage { @@ -305,10 +315,7 @@ internal override void StartJob() this.ExceptionSafeWrapper(delegate { IObservable observable = this.GetCimOperation(); - if (observable != null) - { - observable.Subscribe(this); - } + observable?.Subscribe(this); }); }); } @@ -326,6 +333,7 @@ internal string GetDescription() } internal abstract string Description { get; } + internal abstract string FailSafeDescription { get; } internal void ExceptionSafeWrapper(Action action) @@ -359,10 +367,12 @@ internal void ExceptionSafeWrapper(Action action) { everythingIsOk = true; } + if (_alreadyReachedCompletedState && _jobHadErrors) { everythingIsOk = true; } + if (!everythingIsOk) { Dbg.Assert(false, "PSInvalidOperationException should only happen in certain job states"); @@ -398,7 +408,6 @@ internal CimOperationOptions CreateOperationOptions() operationOptions.SetOption("__MI_OPERATIONOPTIONS_IMPROVEDPERF_STREAMING", 1); - operationOptions.Flags |= this.JobContext.CmdletInvocationContext.CmdletDefinitionContext.SchemaConformanceLevel; if (this.JobContext.CmdletInvocationContext.CmdletDefinitionContext.ResourceUri != null) @@ -411,11 +420,11 @@ internal CimOperationOptions CreateOperationOptions() (_jobContext.WarningActionPreference == ActionPreference.Ignore) ) && (!_jobContext.IsRunningInBackground)) { - operationOptions.DisableChannel((UInt32)MessageChannel.Warning); + operationOptions.DisableChannel((uint)MessageChannel.Warning); } else { - operationOptions.EnableChannel((UInt32)MessageChannel.Warning); + operationOptions.EnableChannel((uint)MessageChannel.Warning); } if (( @@ -423,11 +432,11 @@ internal CimOperationOptions CreateOperationOptions() (_jobContext.VerboseActionPreference == ActionPreference.Ignore) ) && (!_jobContext.IsRunningInBackground)) { - operationOptions.DisableChannel((UInt32)MessageChannel.Verbose); + operationOptions.DisableChannel((uint)MessageChannel.Verbose); } else { - operationOptions.EnableChannel((UInt32)MessageChannel.Verbose); + operationOptions.EnableChannel((uint)MessageChannel.Verbose); } if (( @@ -435,11 +444,11 @@ internal CimOperationOptions CreateOperationOptions() (_jobContext.DebugActionPreference == ActionPreference.Ignore) ) && (!_jobContext.IsRunningInBackground)) { - operationOptions.DisableChannel((UInt32)MessageChannel.Debug); + operationOptions.DisableChannel((uint)MessageChannel.Debug); } else { - operationOptions.EnableChannel((UInt32)MessageChannel.Debug); + operationOptions.EnableChannel((uint)MessageChannel.Debug); } switch (this.JobContext.ShouldProcessOptimization) @@ -494,6 +503,7 @@ internal CimOperationOptions CreateOperationOptions() this.JobContext.CmdletizationModuleVersion, CimSensitiveValueConverter); } + CimOperationOptionsHelper.SetCustomOption( operationOptions, "MI_OPERATIONOPTIONS_POWERSHELL_CMDLETNAME", @@ -509,27 +519,25 @@ internal CimOperationOptions CreateOperationOptions() } CimCustomOptionsDictionary jobSpecificCustomOptions = this.GetJobSpecificCustomOptions(); - if (jobSpecificCustomOptions != null) - { - jobSpecificCustomOptions.Apply(operationOptions, CimSensitiveValueConverter); - } + jobSpecificCustomOptions?.Apply(operationOptions, CimSensitiveValueConverter); return operationOptions; } private readonly Lazy _jobSpecificCustomOptions; + internal abstract CimCustomOptionsDictionary CalculateJobSpecificCustomOptions(); + private CimCustomOptionsDictionary GetJobSpecificCustomOptions() { return _jobSpecificCustomOptions.Value; } - #endregion #region Controlling job state - private readonly CancellationTokenSource _cancellationTokenSource = new CancellationTokenSource(); + private readonly CancellationTokenSource _cancellationTokenSource = new(); /// /// Stops this job. @@ -542,6 +550,7 @@ public override void StopJob() { return; } + _jobWasStopped = true; if (!_jobWasStarted) @@ -559,10 +568,11 @@ public override void StopJob() this.SetJobState(JobState.Stopping); } } + _cancellationTokenSource.Cancel(); } - private readonly object _jobStateLock = new object(); + private readonly object _jobStateLock = new(); private bool _jobHadErrors; private bool _jobWasStarted; private bool _jobWasStopped; @@ -596,6 +606,7 @@ internal void ReportJobFailure(IContainsErrorRecord exception) out sessionWasAlreadyTerminated); } } + if (brokenSessionException != null) { string brokenSessionMessage = string.Format( @@ -612,8 +623,7 @@ internal void ReportJobFailure(IContainsErrorRecord exception) } else { - CimJobException cje = exception as CimJobException; - if ((cje != null) && (cje.IsTerminatingError)) + if ((exception is CimJobException cje) && (cje.IsTerminatingError)) { terminatingErrorTracker.MarkSessionAsTerminated(this.JobContext.Session, out sessionWasAlreadyTerminated); isThisTerminatingError = true; @@ -713,7 +723,7 @@ internal void SetCompletedJobState(JobState state, Exception reason) #region Support for progress reporting - private readonly ConcurrentDictionary _activityIdToLastProgressRecord = new ConcurrentDictionary(); + private readonly ConcurrentDictionary _activityIdToLastProgressRecord = new(); internal override void WriteProgress(ProgressRecord progressRecord) { @@ -746,39 +756,40 @@ internal void FinishProgressReporting() #region Handling extended semantics callbacks - private void WriteProgressCallback(string activity, string currentOperation, string statusDescription, UInt32 percentageCompleted, UInt32 secondsRemaining) + private void WriteProgressCallback(string activity, string currentOperation, string statusDescription, uint percentageCompleted, uint secondsRemaining) { if (string.IsNullOrEmpty(activity)) { activity = this.GetDescription(); } + if (string.IsNullOrEmpty(statusDescription)) { statusDescription = this.StatusMessage; } - Int32 signedSecondsRemaining; - if (secondsRemaining == UInt32.MaxValue) + int signedSecondsRemaining; + if (secondsRemaining == uint.MaxValue) { signedSecondsRemaining = -1; } - else if (secondsRemaining <= Int32.MaxValue) + else if (secondsRemaining <= int.MaxValue) { - signedSecondsRemaining = (Int32)secondsRemaining; + signedSecondsRemaining = (int)secondsRemaining; } else { - signedSecondsRemaining = Int32.MaxValue; + signedSecondsRemaining = int.MaxValue; } - Int32 signedPercentageComplete; - if (percentageCompleted == UInt32.MaxValue) + int signedPercentageComplete; + if (percentageCompleted == uint.MaxValue) { signedPercentageComplete = -1; } else if (percentageCompleted <= 100) { - signedPercentageComplete = (Int32)percentageCompleted; + signedPercentageComplete = (int)percentageCompleted; } else { @@ -807,7 +818,7 @@ private enum MessageChannel Debug = 2, } - private void WriteMessageCallback(UInt32 channel, string message) + private void WriteMessageCallback(uint channel, string message) { this.ExceptionSafeWrapper( delegate @@ -982,6 +993,7 @@ private CimResponseType PromptUserCallback(string message, CimPromptType promptT { _userRespondedYesToAtLeastOneShouldProcess = true; } + return result; } @@ -990,18 +1002,17 @@ private CimResponseType PromptUserCallback(string message, CimPromptType promptT internal static bool IsShowComputerNameMarkerPresent(CimInstance cimInstance) { PSObject pso = PSObject.AsPSObject(cimInstance); - PSPropertyInfo psShowComputerNameProperty = pso.InstanceMembers[RemotingConstants.ShowComputerNameNoteProperty] as PSPropertyInfo; - if (psShowComputerNameProperty == null) + if (pso.InstanceMembers[RemotingConstants.ShowComputerNameNoteProperty] is not PSPropertyInfo psShowComputerNameProperty) { return false; } + return true.Equals(psShowComputerNameProperty.Value); } internal static void AddShowComputerNameMarker(PSObject pso) { - PSPropertyInfo psShowComputerNameProperty = pso.InstanceMembers[RemotingConstants.ShowComputerNameNoteProperty] as PSPropertyInfo; - if (psShowComputerNameProperty != null) + if (pso.InstanceMembers[RemotingConstants.ShowComputerNameNoteProperty] is PSPropertyInfo psShowComputerNameProperty) { psShowComputerNameProperty.Value = true; } @@ -1025,17 +1036,17 @@ internal override void WriteObject(object outputObject) { cimInstance = outputObject as CimInstance; } + if (cimInstance != null) { CimCmdletAdapter.AssociateSessionOfOriginWithInstance(cimInstance, this.JobContext.Session); CimCustomOptionsDictionary.AssociateCimInstanceWithCustomOptions(cimInstance, this.GetJobSpecificCustomOptions()); } + if (this.JobContext.ShowComputerName) { - if (pso == null) - { - pso = PSObject.AsPSObject(outputObject); - } + pso ??= PSObject.AsPSObject(outputObject); + AddShowComputerNameMarker(pso); if (cimInstance == null) { @@ -1055,12 +1066,15 @@ protected override void Dispose(bool disposing) { isCompleted = _alreadyReachedCompletedState; } + if (!isCompleted) { this.StopJob(); this.Finished.WaitOne(); } + _cimSensitiveValueConverter.Dispose(); + _cancellationTokenSource.Dispose(); } } } diff --git a/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/cim/cimCmdletDefinitionContext.cs b/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/cim/cimCmdletDefinitionContext.cs index f08b4e69b6d..8a4e4272561 100644 --- a/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/cim/cimCmdletDefinitionContext.cs +++ b/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/cim/cimCmdletDefinitionContext.cs @@ -1,16 +1,16 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. using System; using System.Collections.Generic; using System.Globalization; using System.Management.Automation; + using Microsoft.Management.Infrastructure.Options; namespace Microsoft.PowerShell.Cmdletization.Cim { - internal class CimCmdletDefinitionContext + internal sealed class CimCmdletDefinitionContext { internal CimCmdletDefinitionContext( string cmdletizationClassName, @@ -26,14 +26,20 @@ internal CimCmdletDefinitionContext( _privateData = privateData; } - public string CmdletizationClassName { get; private set; } - public string CmdletizationClassVersion { get; private set; } - public Version CmdletizationModuleVersion { get; private set; } - public bool SupportsShouldProcess { get; private set; } + public string CmdletizationClassName { get; } + + public string CmdletizationClassVersion { get; } + + public Version CmdletizationModuleVersion { get; } + + public bool SupportsShouldProcess { get; } + private readonly IDictionary _privateData; private const string QueryLanguageKey = "QueryDialect"; + private bool? _useEnumerateInstancesInsteadOfWql; + public bool UseEnumerateInstancesInsteadOfWql { get @@ -48,8 +54,10 @@ public bool UseEnumerateInstancesInsteadOfWql { newValue = true; } + _useEnumerateInstancesInsteadOfWql = newValue; } + return _useEnumerateInstancesInsteadOfWql.Value; } } @@ -106,6 +114,7 @@ public bool ClientSideShouldProcess private Uri _resourceUri; private bool _resourceUriHasBeenCalculated; + public Uri ResourceUri { get @@ -123,6 +132,7 @@ public Uri ResourceUri _resourceUriHasBeenCalculated = true; } + return _resourceUri; } } @@ -133,6 +143,7 @@ public bool SkipTestConnection } private CimOperationFlags? _schemaConformanceLevel; + public CimOperationFlags SchemaConformanceLevel { get @@ -165,6 +176,7 @@ public CimOperationFlags SchemaConformanceLevel _schemaConformanceLevel = newSchemaConformanceLevel; } + return _schemaConformanceLevel.Value; } } diff --git a/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/cim/cimCmdletInvocationContext.cs b/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/cim/cimCmdletInvocationContext.cs index b89f7c4de3e..3bee74526fc 100644 --- a/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/cim/cimCmdletInvocationContext.cs +++ b/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/cim/cimCmdletInvocationContext.cs @@ -1,15 +1,16 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. using System; using System.Management.Automation; + using Microsoft.Management.Infrastructure; + using Dbg = System.Management.Automation.Diagnostics; namespace Microsoft.PowerShell.Cmdletization.Cim { - internal class CimCmdletInvocationContext + internal sealed class CimCmdletInvocationContext { internal CimCmdletInvocationContext( CimCmdletDefinitionContext cmdletDefinitionContext, @@ -75,20 +76,26 @@ private static void WarnAboutUnsupportedActionPreferences( if (actionPreferenceComesFromCommandLineParameter) { Exception exception = new ArgumentException(message); - ErrorRecord errorRecord = new ErrorRecord(exception, "ActionPreferenceNotSupportedByCimCmdletAdapter", ErrorCategory.NotImplemented, null); + ErrorRecord errorRecord = new(exception, "ActionPreferenceNotSupportedByCimCmdletAdapter", ErrorCategory.NotImplemented, null); cmdlet.ThrowTerminatingError(errorRecord); } } - public CimCmdletDefinitionContext CmdletDefinitionContext { get; private set; } + public CimCmdletDefinitionContext CmdletDefinitionContext { get; } + + public InvocationInfo CmdletInvocationInfo { get; } + + public MshCommandRuntime.ShouldProcessPossibleOptimization ShouldProcessOptimization { get; } + + public ActionPreference ErrorActionPreference { get; } + + public ActionPreference WarningActionPreference { get; } + + public ActionPreference VerboseActionPreference { get; } + + public ActionPreference DebugActionPreference { get; } - public InvocationInfo CmdletInvocationInfo { get; private set; } - public MshCommandRuntime.ShouldProcessPossibleOptimization ShouldProcessOptimization { get; private set; } - public ActionPreference ErrorActionPreference { get; private set; } - public ActionPreference WarningActionPreference { get; private set; } - public ActionPreference VerboseActionPreference { get; private set; } - public ActionPreference DebugActionPreference { get; private set; } - public string NamespaceOverride { get; private set; } + public string NamespaceOverride { get; } public bool IsRunningInBackground { @@ -106,7 +113,7 @@ public bool ShowComputerName } } - private readonly Lazy _defaultCimSession = new Lazy(CreateDefaultCimSession); + private readonly Lazy _defaultCimSession = new(CreateDefaultCimSession); private static CimSession CreateDefaultCimSession() { diff --git a/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/cim/cimConverter.cs b/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/cim/cimConverter.cs index 02b74033585..da075286c34 100644 --- a/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/cim/cimConverter.cs +++ b/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/cim/cimConverter.cs @@ -1,6 +1,5 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. using System; using System.Collections.Generic; @@ -11,21 +10,23 @@ using System.Net.Mail; using System.Net.NetworkInformation; using System.Reflection; +using System.Runtime.InteropServices; using System.Security; using System.Security.AccessControl; using System.Security.Cryptography.X509Certificates; using System.Xml; + using Microsoft.Management.Infrastructure; + using Dbg = System.Management.Automation.Diagnostics; -using System.Runtime.InteropServices; // TODO/FIXME: Move this class to src/cimSupport/other directory (to map to the namespace it lives in and functionality it implements [cmdletization independent]) namespace Microsoft.PowerShell.Cim { - internal class CimSensitiveValueConverter : IDisposable + internal sealed class CimSensitiveValueConverter : IDisposable { - private class SensitiveString : IDisposable + private sealed class SensitiveString : IDisposable { private GCHandle _gcHandle; private string _string; @@ -49,19 +50,16 @@ internal SensitiveString(int numberOfCharacters) private unsafe void Copy(char* source, int offset, int charsToCopy) { - if ((offset < 0) || (offset >= _string.Length)) - { - throw new ArgumentOutOfRangeException("offset"); - } - if (offset + charsToCopy > _string.Length) - { - throw new ArgumentOutOfRangeException("charsToCopy"); - } + ArgumentOutOfRangeException.ThrowIfNegative(offset); + ArgumentOutOfRangeException.ThrowIfGreaterThanOrEqual(offset, _string.Length); + ArgumentOutOfRangeException.ThrowIfGreaterThan(offset + charsToCopy, _string.Length, nameof(charsToCopy)); fixed (char* target = _string) - for (int i = 0; i < charsToCopy; i++) { - target[offset + i] = source[i]; + for (int i = 0; i < charsToCopy; i++) + { + target[offset + i] = source[i]; + } } } @@ -98,7 +96,7 @@ internal void Copy(SecureString source, int offset) } /// - /// Releases resources associated with this object + /// Releases resources associated with this object. /// public void Dispose() { @@ -107,7 +105,7 @@ public void Dispose() } /// - /// Releases resources associated with this object + /// Releases resources associated with this object. /// private void Dispose(bool disposing) { @@ -125,10 +123,10 @@ private void Dispose(bool disposing) } } - private readonly List _trackedDisposables = new List(); + private readonly List _trackedDisposables = new(); /// - /// Releases resources associated with this object + /// Releases resources associated with this object. /// public void Dispose() { @@ -137,7 +135,7 @@ public void Dispose() } /// - /// Releases resources associated with this object + /// Releases resources associated with this object. /// private void Dispose(bool disposing) { @@ -147,6 +145,7 @@ private void Dispose(bool disposing) { d.Dispose(); } + _trackedDisposables.Clear(); } } @@ -172,6 +171,7 @@ internal object ConvertFromDotNetToCim(object dotNetObject) var sensitiveString = new SensitiveString(escapedUsername.Length + PSCredentialDelimiter.Length + credential.Password.Length); lock (_trackedDisposables) { _trackedDisposables.Add(sensitiveString); } + sensitiveString.Copy(escapedUsername, 0); sensitiveString.Copy(PSCredentialDelimiter, escapedUsername.Length); sensitiveString.Copy(credential.Password, escapedUsername.Length + PSCredentialDelimiter.Length); @@ -183,6 +183,7 @@ internal object ConvertFromDotNetToCim(object dotNetObject) SecureString secureString = (SecureString)psObject.BaseObject; var sensitiveString = new SensitiveString(secureString.Length); lock (_trackedDisposables) { _trackedDisposables.Add(sensitiveString); } + sensitiveString.Copy(secureString, 0); return sensitiveString.Value; } @@ -200,6 +201,7 @@ internal object ConvertFromDotNetToCim(object dotNetObject) object cimElement = ConvertFromDotNetToCim(dotNetArray.GetValue(i)); cimArray.SetValue(cimElement, i); } + return cimArray; } } @@ -225,7 +227,7 @@ internal static Type GetCimType(Type dotNetType) internal static class CimValueConverter { - /// The only kind of exception this method can throw + /// The only kind of exception this method can throw. internal static object ConvertFromDotNetToCim(object dotNetObject) { if (dotNetObject == null) @@ -243,10 +245,12 @@ internal static object ConvertFromDotNetToCim(object dotNetObject) { return psObject.BaseObject; } + if (typeof(CimInstance).IsAssignableFrom(dotNetType)) { return psObject.BaseObject; } + if (typeof(PSReference).IsAssignableFrom(dotNetType)) { PSReference psReference = (PSReference)psObject.BaseObject; @@ -274,6 +278,7 @@ internal static object ConvertFromDotNetToCim(object dotNetObject) object cimElement = ConvertFromDotNetToCim(dotNetArray.GetValue(i)); cimArray.SetValue(cimElement, i); } + return cimArray; } } @@ -338,10 +343,10 @@ internal static object ConvertFromDotNetToCim(object dotNetObject) CmdletizationResources.CimConversion_CimIntrinsicValue); } - /// The only kind of exception this method can throw + /// The only kind of exception this method can throw. internal static object ConvertFromCimToDotNet(object cimObject, Type expectedDotNetType) { - if (expectedDotNetType == null) { throw new ArgumentNullException("expectedDotNetType"); } + ArgumentNullException.ThrowIfNull(expectedDotNetType); if (cimObject == null) { @@ -357,6 +362,7 @@ internal static object ConvertFromCimToDotNet(object cimObject, Type expectedDot { return LanguagePrimitives.ConvertTo(cimObject, expectedDotNetType, CultureInfo.InvariantCulture); } + if (expectedDotNetType == typeof(CimInstance)) { return LanguagePrimitives.ConvertTo(cimObject, expectedDotNetType, CultureInfo.InvariantCulture); @@ -374,6 +380,7 @@ internal static object ConvertFromCimToDotNet(object cimObject, Type expectedDot object dotNetElement = ConvertFromCimToDotNet(cimArray.GetValue(i), dotNetElementType); dotNetArray.SetValue(dotNetElement, i); } + return dotNetArray; } } @@ -386,21 +393,21 @@ internal static object ConvertFromCimToDotNet(object cimObject, Type expectedDot return dotNetObject; } - Func, object> exceptionSafeReturn = delegate (Func innerAction) - { - try - { - return innerAction(); - } - catch (Exception e) - { - throw CimValueConverter.GetInvalidCastException( - e, - "InvalidCimToDotNetCast", - cimObject, - expectedDotNetType.FullName); - } - }; + Func, object> exceptionSafeReturn = (Func innerAction) => + { + try + { + return innerAction(); + } + catch (Exception e) + { + throw CimValueConverter.GetInvalidCastException( + e, + "InvalidCimToDotNetCast", + cimObject, + expectedDotNetType.FullName); + } + }; if (typeof(ObjectSecurity).IsAssignableFrom(expectedDotNetType)) { @@ -418,7 +425,9 @@ internal static object ConvertFromCimToDotNet(object cimObject, Type expectedDot var cimIntrinsicValue = (byte[])LanguagePrimitives.ConvertTo(cimObject, typeof(byte[]), CultureInfo.InvariantCulture); return exceptionSafeReturn(delegate { + #pragma warning disable SYSLIB0057 return new X509Certificate2(cimIntrinsicValue); + #pragma warning restore SYSLIB0057 }); } @@ -446,8 +455,8 @@ internal static object ConvertFromCimToDotNet(object cimObject, Type expectedDot return exceptionSafeReturn(delegate { int indexOfLastColon = cimIntrinsicValue.LastIndexOf(':'); - int port = int.Parse(cimIntrinsicValue.Substring(indexOfLastColon + 1), NumberStyles.Integer, CultureInfo.InvariantCulture); - IPAddress address = IPAddress.Parse(cimIntrinsicValue.Substring(0, indexOfLastColon)); + int port = int.Parse(cimIntrinsicValue.AsSpan(indexOfLastColon + 1), NumberStyles.Integer, CultureInfo.InvariantCulture); + IPAddress address = IPAddress.Parse(cimIntrinsicValue.AsSpan(0, indexOfLastColon)); return new IPEndPoint(address, port); }); } @@ -483,6 +492,7 @@ internal static CimType GetCimTypeEnum(Type dotNetType) { return CimType.Reference; } + if (typeof(PSReference[]).IsAssignableFrom(dotNetType)) { return CimType.ReferenceArray; @@ -509,10 +519,12 @@ internal static Type GetCimType(Type dotNetType) { return dotNetType; } + if (dotNetType == typeof(CimInstance)) { return dotNetType; } + if (dotNetType == typeof(PSReference)) { return dotNetType; @@ -569,7 +581,7 @@ internal static Type GetCimType(Type dotNetType) } /// - /// Returns a type of CIM representation if conversion from/to CIM can be done purely with LanguagePrimitives.ConvertTo + /// Returns a type of CIM representation if conversion from/to CIM can be done purely with LanguagePrimitives.ConvertTo. /// /// /// @@ -614,6 +626,7 @@ internal static Type GetElementType(Type arrayType) { return null; } + Type elementType = arrayType.GetElementType(); if (elementType.IsArray) { @@ -666,4 +679,4 @@ internal static void AssertIntrinsicCimType(Type type) "Caller should verify that type is an intrinsic CIM type"); } } -} \ No newline at end of file +} diff --git a/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/cim/cimJobContext.cs b/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/cim/cimJobContext.cs index a3caada5792..27d48ccab9a 100644 --- a/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/cim/cimJobContext.cs +++ b/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/cim/cimJobContext.cs @@ -1,15 +1,15 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. using System; using System.Globalization; using System.Management.Automation; + using Microsoft.Management.Infrastructure; namespace Microsoft.PowerShell.Cmdletization.Cim { - internal class CimJobContext + internal sealed class CimJobContext { internal CimJobContext( CimCmdletInvocationContext cmdletInvocationContext, @@ -22,10 +22,11 @@ internal CimJobContext( this.TargetObject = targetObject ?? this.ClassName; } - public CimCmdletInvocationContext CmdletInvocationContext { get; private set; } + public CimCmdletInvocationContext CmdletInvocationContext { get; } + + public CimSession Session { get; } - public CimSession Session { get; private set; } - public object TargetObject { get; private set; } + public object TargetObject { get; } public string ClassName { @@ -43,6 +44,7 @@ public string ClassNameOrNullIfResourceUriIsUsed { return null; } + return this.ClassName; } } @@ -55,6 +57,7 @@ public string Namespace { return this.CmdletInvocationContext.NamespaceOverride; } + return GetCimNamespace(this.CmdletInvocationContext.CmdletDefinitionContext.CmdletizationClassName); } } diff --git a/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/cim/cimOperationOptionsHelper.cs b/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/cim/cimOperationOptionsHelper.cs index ededc6cd10e..34c708b5f4c 100644 --- a/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/cim/cimOperationOptionsHelper.cs +++ b/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/cim/cimOperationOptionsHelper.cs @@ -1,22 +1,23 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. using System; using System.Collections.Generic; using System.Linq; using System.Runtime.CompilerServices; + using Microsoft.Management.Infrastructure; using Microsoft.Management.Infrastructure.Options; using Microsoft.PowerShell.Cim; + using Dbg = System.Management.Automation.Diagnostics; namespace Microsoft.PowerShell.Cmdletization.Cim { - internal class CimCustomOptionsDictionary + internal sealed class CimCustomOptionsDictionary { private readonly IDictionary _dict; - private readonly object _dictModificationLock = new object(); + private readonly object _dictModificationLock = new(); private CimCustomOptionsDictionary(IEnumerable> wrappedDictionary) { @@ -41,7 +42,7 @@ internal static CimCustomOptionsDictionary Create(IEnumerable s_cimInstanceToCustomOptions = new ConditionalWeakTable(); + private static readonly ConditionalWeakTable s_cimInstanceToCustomOptions = new(); internal static void AssociateCimInstanceWithCustomOptions(CimInstance cimInstance, CimCustomOptionsDictionary newCustomOptions) { @@ -105,6 +106,7 @@ internal static CimCustomOptionsDictionary MergeOptions(CimCustomOptionsDictiona result = MergeOptions(result, instanceRelatedToThisOperation); } } + return result; } @@ -115,7 +117,7 @@ internal void Apply(CimOperationOptions cimOperationOptions, CimSensitiveValueCo } /// - /// CimQuery supports building of queries against CIM object model + /// CimQuery supports building of queries against CIM object model. /// internal static class CimOperationOptionsHelper { diff --git a/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/cim/cimQuery.cs b/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/cim/cimQuery.cs index 236585fd2a9..f08c2ab3c11 100644 --- a/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/cim/cimQuery.cs +++ b/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/cim/cimQuery.cs @@ -1,6 +1,5 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. using System; using System.Collections; @@ -17,9 +16,9 @@ namespace Microsoft.PowerShell.Cmdletization.Cim { /// - /// CimQuery supports building of queries against CIM object model + /// CimQuery supports building of queries against CIM object model. /// - internal class CimQuery : QueryBuilder, ISessionBoundQueryBuilder + internal sealed class CimQuery : QueryBuilder, ISessionBoundQueryBuilder { private readonly StringBuilder _wqlCondition; @@ -28,9 +27,9 @@ internal class CimQuery : QueryBuilder, ISessionBoundQueryBuilder private string _resultRole; private string _sourceRole; - internal readonly Dictionary queryOptions = new Dictionary(StringComparer.OrdinalIgnoreCase); + internal readonly Dictionary queryOptions = new(StringComparer.OrdinalIgnoreCase); - internal ClientSideQuery ClientSideQuery { get; private set; } + internal ClientSideQuery ClientSideQuery { get; } internal CimQuery() { @@ -53,7 +52,7 @@ private static string ObjectToWqlLiteral(object o) { if (LanguagePrimitives.IsNull(o)) { - return "null"; // based on an example at http://msdn.microsoft.com/en-us/library/aa394054(VS.85).aspx + return "null"; // based on an example at https://msdn.microsoft.com/library/aa394054(VS.85).aspx } o = CimValueConverter.ConvertFromDotNetToCim(o); @@ -96,9 +95,10 @@ private static string ObjectToWqlLiteral(object o) { if ((bool)LanguagePrimitives.ConvertTo(o, typeof(bool), CultureInfo.InvariantCulture)) { - return "TRUE"; // based on http://msdn.microsoft.com/en-us/library/aa394054(VS.85).aspx + return "TRUE"; // based on https://msdn.microsoft.com/library/aa394054(VS.85).aspx } - return "FALSE"; // based on http://msdn.microsoft.com/en-us/library/aa394054(VS.85).aspx + + return "FALSE"; // based on https://msdn.microsoft.com/library/aa394054(VS.85).aspx } throw CimValueConverter.GetInvalidCastException( @@ -177,7 +177,7 @@ private static string GetMatchCondition(string propertyName, IEnumerable propert .Select(propertyValue => wildcardsEnabled ? GetMatchConditionForLikeOperator(propertyName, propertyValue) : GetMatchConditionForEqualityOperator(propertyName, propertyValue)) - .Where(individualCondition => !string.IsNullOrWhiteSpace(individualCondition)) + .Where(static individualCondition => !string.IsNullOrWhiteSpace(individualCondition)) .ToList(); if (individualConditions.Count == 0) { @@ -191,13 +191,13 @@ private static string GetMatchCondition(string propertyName, IEnumerable propert #region Public inputs from cmdletization /// - /// Modifies the query, so that it only returns objects with a given property value + /// Modifies the query, so that it only returns objects with a given property value. /// - /// Property name to query on - /// Property values to accept in the query + /// Property name to query on. + /// Property values to accept in the query. /// - /// true if should be treated as a containing a wildcard pattern; - /// false otherwise + /// if should be treated as a containing a wildcard pattern; + /// otherwise. /// /// /// Describes how to handle filters that didn't match any objects @@ -214,13 +214,13 @@ public override void FilterByProperty(string propertyName, IEnumerable allowedPr } /// - /// Modifies the query, so that it does not return objects with a given property value + /// Modifies the query, so that it does not return objects with a given property value. /// - /// Property name to query on - /// Property values to reject in the query + /// Property name to query on. + /// Property values to reject in the query. /// - /// true if should be treated as a containing a wildcard pattern; - /// false otherwise + /// if should be treated as a containing a wildcard pattern; + /// otherwise. /// /// /// Describes how to handle filters that didn't match any objects @@ -241,10 +241,10 @@ public override void ExcludeByProperty(string propertyName, IEnumerable excluded } /// - /// Modifies the query, so that it returns only objects that have a property value greater than or equal to a threshold + /// Modifies the query, so that it returns only objects that have a property value greater than or equal to a threshold. /// - /// Property name to query on - /// Minimum property value + /// Property name to query on. + /// Minimum property value. /// /// Describes how to handle filters that didn't match any objects /// @@ -265,10 +265,10 @@ public override void FilterByMinPropertyValue(string propertyName, object minPro } /// - /// Modifies the query, so that it returns only objects that have a property value less than or equal to a threshold + /// Modifies the query, so that it returns only objects that have a property value less than or equal to a threshold. /// - /// Property name to query on - /// Maximum property value + /// Property name to query on. + /// Maximum property value. /// /// Describes how to handle filters that didn't match any objects /// @@ -291,10 +291,10 @@ public override void FilterByMaxPropertyValue(string propertyName, object maxPro /// /// Modifies the query, so that it returns only objects associated with /// - /// object that query results have to be associated with - /// name of the association - /// name of the role that has in the association - /// name of the role that query results have in the association + /// Object that query results have to be associated with. + /// Name of the association. + /// Name of the role that has in the association. + /// Name of the role that query results have in the association. /// /// Describes how to handle filters that didn't match any objects /// @@ -308,20 +308,14 @@ public override void FilterByAssociatedInstance(object associatedInstance, strin } /// - /// Sets a query option + /// Sets a query option. /// /// /// public override void AddQueryOption(string optionName, object optionValue) { - if (string.IsNullOrEmpty(optionName)) - { - throw new ArgumentNullException("optionName"); - } - if (optionValue == null) - { - throw new ArgumentNullException("optionValue"); - } + ArgumentException.ThrowIfNullOrEmpty(optionName); + ArgumentNullException.ThrowIfNull(optionValue); this.queryOptions[optionName] = optionValue; } @@ -361,13 +355,14 @@ CimSession ISessionBoundQueryBuilder.GetTargetSession() { return CimCmdletAdapter.GetSessionOfOriginFromCimInstance(_associatedObject); } + return null; } /// - /// Returns a string that represents the current CIM query + /// Returns a string that represents the current CIM query. /// - /// A string that represents the current CIM query + /// A string that represents the current CIM query. public override string ToString() { return _wqlCondition.ToString(); diff --git a/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/cim/cimWrapper.cs b/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/cim/cimWrapper.cs index 54773a8b886..472046b192f 100644 --- a/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/cim/cimWrapper.cs +++ b/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/cim/cimWrapper.cs @@ -1,6 +1,5 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. using System; using System.Collections.ObjectModel; @@ -9,13 +8,15 @@ using System.Management.Automation; using System.Runtime.CompilerServices; using System.Threading; + using Microsoft.Management.Infrastructure; + using Dbg = System.Management.Automation.Diagnostics; namespace Microsoft.PowerShell.Cmdletization.Cim { /// - /// CIM-specific ObjectModelWrapper + /// CIM-specific ObjectModelWrapper. /// public sealed class CimCmdletAdapter : SessionBasedCmdletAdapter, @@ -32,7 +33,7 @@ public sealed class CimCmdletAdapter : #region Changing Session parameter to CimSession /// - /// CimSession to operate on + /// CimSession to operate on. /// [Parameter] [ValidateNotNullOrEmpty] @@ -44,6 +45,7 @@ public CimSession[] CimSession { return base.Session; } + set { base.Session = value; @@ -65,12 +67,14 @@ public override int ThrottleLimit return this.CmdletDefinitionContext.DefaultThrottleLimit; } + set { base.ThrottleLimit = value; _throttleLimitIsSetExplicitly = true; } } + private bool _throttleLimitIsSetExplicitly; #endregion @@ -78,9 +82,9 @@ public override int ThrottleLimit #region ObjectModelWrapper overrides /// - /// Creates a query builder for CIM OM + /// Creates a query builder for CIM OM. /// - /// Query builder for CIM OM + /// Query builder for CIM OM. public override QueryBuilder GetQueryBuilder() { return new CimQuery(); @@ -90,31 +94,30 @@ internal CimCmdletInvocationContext CmdletInvocationContext { get { - return _cmdletInvocationContext ?? - (_cmdletInvocationContext = new CimCmdletInvocationContext( - this.CmdletDefinitionContext, - this.Cmdlet, - this.GetDynamicNamespace())); + return _cmdletInvocationContext ??= new CimCmdletInvocationContext( + this.CmdletDefinitionContext, + this.Cmdlet, + this.GetDynamicNamespace()); } } + private CimCmdletInvocationContext _cmdletInvocationContext; internal CimCmdletDefinitionContext CmdletDefinitionContext { get { - if (_cmdletDefinitionContext == null) - { - _cmdletDefinitionContext = new CimCmdletDefinitionContext( - this.ClassName, - this.ClassVersion, - this.ModuleVersion, - this.Cmdlet.CommandInfo.CommandMetadata.SupportsShouldProcess, - this.PrivateData); - } + _cmdletDefinitionContext ??= new CimCmdletDefinitionContext( + this.ClassName, + this.ClassVersion, + this.ModuleVersion, + this.Cmdlet.CommandInfo.CommandMetadata.SupportsShouldProcess, + this.PrivateData); + return _cmdletDefinitionContext; } } + private CimCmdletDefinitionContext _cmdletDefinitionContext; internal InvocationInfo CmdletInvocationInfo @@ -131,7 +134,7 @@ internal InvocationInfo CmdletInvocationInfo /// /// Returns a new job name to use for the parent job that handles throttling of the child jobs that actually perform querying and method invocation. /// - /// Job name + /// Job name. protected override string GenerateParentJobName() { return "CimJob" + Interlocked.Increment(ref CimCmdletAdapter.s_jobNumber).ToString(CultureInfo.InvariantCulture); @@ -140,7 +143,7 @@ protected override string GenerateParentJobName() /// /// Returns default sessions to use when the user doesn't specify the -Session cmdlet parameter. /// - /// Default sessions to use when the user doesn't specify the -Session cmdlet parameter + /// Default sessions to use when the user doesn't specify the -Session cmdlet parameter. protected override CimSession DefaultSession { get @@ -160,15 +163,14 @@ private CimJobContext CreateJobContext(CimSession session, object targetObject) /// /// Creates a object that performs a query against the wrapped object model. /// - /// Remote session to query - /// Query parameters - /// object that performs a query against the wrapped object model + /// Remote session to query. + /// Query parameters. + /// object that performs a query against the wrapped object model. internal override StartableJob CreateQueryJob(CimSession session, QueryBuilder baseQuery) { - CimQuery query = baseQuery as CimQuery; - if (query == null) + if (baseQuery is not CimQuery query) { - throw new ArgumentNullException("baseQuery"); + throw new ArgumentNullException(nameof(baseQuery)); } TerminatingErrorTracker tracker = TerminatingErrorTracker.GetTracker(this.CmdletInvocationInfo, isStaticCmdlet: false); @@ -176,6 +178,7 @@ internal override StartableJob CreateQueryJob(CimSession session, QueryBuilder b { return null; } + if (!IsSupportedSession(session, tracker)) { return null; @@ -190,10 +193,10 @@ internal override StartableJob CreateQueryJob(CimSession session, QueryBuilder b /// /// Creates a object that invokes an instance method in the wrapped object model. /// - /// Remote session to invoke the method in - /// The object on which to invoke the method - /// Method invocation details - /// true if successful method invocations should emit downstream the being operated on + /// Remote session to invoke the method in. + /// The object on which to invoke the method. + /// Method invocation details. + /// if successful method invocations should emit downstream the being operated on. /// internal override StartableJob CreateInstanceMethodInvocationJob(CimSession session, CimInstance objectInstance, MethodInvocationInfo methodInvocationInfo, bool passThru) { @@ -202,6 +205,7 @@ internal override StartableJob CreateInstanceMethodInvocationJob(CimSession sess { return null; } + if (!IsSupportedSession(session, tracker)) { return null; @@ -274,7 +278,7 @@ private bool IsSupportedSession(CimSession cimSession, TerminatingErrorTracker t cimSession.ComputerName, nameOfUnsupportedSwitch); Exception exception = new NotSupportedException(errorMessage); - ErrorRecord errorRecord = new ErrorRecord( + ErrorRecord errorRecord = new( exception, "NoExtendedSemanticsSupportInRemoteDcomProtocol", ErrorCategory.NotImplemented, @@ -293,8 +297,8 @@ private bool IsSupportedSession(CimSession cimSession, TerminatingErrorTracker t /// (of the class named by ) /// in the wrapped object model. /// - /// Remote session to invoke the method in - /// Method invocation details + /// Remote session to invoke the method in. + /// Method invocation details. internal override StartableJob CreateStaticMethodInvocationJob(CimSession session, MethodInvocationInfo methodInvocationInfo) { TerminatingErrorTracker tracker = TerminatingErrorTracker.GetTracker(this.CmdletInvocationInfo, isStaticCmdlet: true); @@ -302,6 +306,7 @@ internal override StartableJob CreateStaticMethodInvocationJob(CimSession sessio { return null; } + if (!IsSupportedSession(session, tracker)) { return null; @@ -330,7 +335,7 @@ internal override StartableJob CreateStaticMethodInvocationJob(CimSession sessio #region Session affinity management - private static readonly ConditionalWeakTable s_cimInstanceToSessionOfOrigin = new ConditionalWeakTable(); + private static readonly ConditionalWeakTable s_cimInstanceToSessionOfOrigin = new(); internal static void AssociateSessionOfOriginWithInstance(CimInstance cimInstance, CimSession sessionOfOrigin) { @@ -345,6 +350,7 @@ internal static CimSession GetSessionOfOriginFromCimInstance(CimInstance instanc { s_cimInstanceToSessionOfOrigin.TryGetValue(instance, out result); } + return result; } @@ -358,6 +364,7 @@ internal override CimSession GetSessionOfOriginFromInstance(CimInstance instance #region Handling of dynamic parameters private RuntimeDefinedParameterDictionary _dynamicParameters; + private const string CimNamespaceParameter = "CimNamespace"; private string GetDynamicNamespace() @@ -384,10 +391,10 @@ object IDynamicParameters.GetDynamicParameters() if (this.CmdletDefinitionContext.ExposeCimNamespaceParameter) { - Collection namespaceAttributes = new Collection(); + Collection namespaceAttributes = new(); namespaceAttributes.Add(new ValidateNotNullOrEmptyAttribute()); namespaceAttributes.Add(new ParameterAttribute()); - RuntimeDefinedParameter namespaceRuntimeParameter = new RuntimeDefinedParameter( + RuntimeDefinedParameter namespaceRuntimeParameter = new( CimNamespaceParameter, typeof(string), namespaceAttributes); diff --git a/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/cim/clientSideQuery.cs b/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/cim/clientSideQuery.cs index 67ef3cb12a7..c1a8552db8c 100644 --- a/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/cim/clientSideQuery.cs +++ b/src/Microsoft.PowerShell.Commands.Management/cimSupport/cmdletization/cim/clientSideQuery.cs @@ -1,6 +1,5 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. using System; using System.Collections; @@ -8,8 +7,10 @@ using System.Globalization; using System.Linq; using System.Management.Automation; + using Microsoft.Management.Infrastructure; using Microsoft.PowerShell.Cim; + using Dbg = System.Management.Automation.Diagnostics; namespace Microsoft.PowerShell.Cmdletization.Cim @@ -19,9 +20,9 @@ namespace Microsoft.PowerShell.Cmdletization.Cim /// 1) filtering that cannot be translated into a server-side query (i.e. when CimQuery.WildcardToWqlLikeOperand reports that it cannot translate into WQL) /// 2) detecting if all expected results have been received and giving friendly user errors otherwise (i.e. could not find process with name='foo'; details in Windows 8 Bugs: #60926) /// - internal class ClientSideQuery : QueryBuilder + internal sealed class ClientSideQuery : QueryBuilder { - internal class NotFoundError + internal sealed class NotFoundError { public NotFoundError() { @@ -35,8 +36,7 @@ public NotFoundError(string propertyName, object propertyValue, bool wildcardsEn if (wildcardsEnabled) { - var propertyValueAsString = propertyValue as string; - if ((propertyValueAsString != null) && (WildcardPattern.ContainsWildcardCharacters(propertyValueAsString))) + if ((propertyValue is string propertyValueAsString) && (WildcardPattern.ContainsWildcardCharacters(propertyValueAsString))) { this.ErrorMessageGenerator = (queryDescription, className) => GetErrorMessageForNotFound_ForWildcard(this.PropertyName, this.PropertyValue, className); @@ -54,9 +54,11 @@ public NotFoundError(string propertyName, object propertyValue, bool wildcardsEn } } - public string PropertyName { get; private set; } - public object PropertyValue { get; private set; } - public Func ErrorMessageGenerator { get; private set; } + public string PropertyName { get; } + + public object PropertyValue { get; } + + public Func ErrorMessageGenerator { get; } private static string GetErrorMessageForNotFound(string queryDescription, string className) { @@ -150,8 +152,10 @@ public virtual IEnumerable GetNotFoundErrors_IfThisIsTheOnlyFilte private abstract class CimInstancePropertyBasedFilter : CimInstanceFilterBase { - private readonly List _propertyValueFilters = new List(); + private readonly List _propertyValueFilters = new(); + protected IEnumerable PropertyValueFilters { get { return _propertyValueFilters; } } + protected void AddPropertyValueFilter(PropertyValueFilter propertyValueFilter) { _propertyValueFilters.Add(propertyValueFilter); @@ -171,11 +175,12 @@ protected override bool IsMatchCore(CimInstance cimInstance) } } } + return isMatch; } } - private class CimInstanceRegularFilter : CimInstancePropertyBasedFilter + private sealed class CimInstanceRegularFilter : CimInstancePropertyBasedFilter { public CimInstanceRegularFilter(string propertyName, IEnumerable allowedPropertyValues, bool wildcardsEnabled, BehaviorOnNoMatch behaviorOnNoMatch) { @@ -196,7 +201,7 @@ public CimInstanceRegularFilter(string propertyName, IEnumerable allowedProperty if (valueBehaviors.Count == 1) { - this.BehaviorOnNoMatch = valueBehaviors.Single(); + this.BehaviorOnNoMatch = valueBehaviors.First(); } else { @@ -217,7 +222,7 @@ public override bool ShouldReportErrorOnNoMatches_IfMultipleFilters() case BehaviorOnNoMatch.Default: default: return this.PropertyValueFilters - .Where(f => !f.HadMatch).Any(f => f.BehaviorOnNoMatch == BehaviorOnNoMatch.ReportErrors); + .Any(static f => !f.HadMatch && f.BehaviorOnNoMatch == BehaviorOnNoMatch.ReportErrors); } } @@ -241,7 +246,7 @@ public override IEnumerable GetNotFoundErrors_IfThisIsTheOnlyFilt } } - private class CimInstanceExcludeFilter : CimInstancePropertyBasedFilter + private sealed class CimInstanceExcludeFilter : CimInstancePropertyBasedFilter { public CimInstanceExcludeFilter(string propertyName, IEnumerable excludedPropertyValues, bool wildcardsEnabled, BehaviorOnNoMatch behaviorOnNoMatch) { @@ -266,7 +271,7 @@ public CimInstanceExcludeFilter(string propertyName, IEnumerable excludedPropert } } - private class CimInstanceMinFilter : CimInstancePropertyBasedFilter + private sealed class CimInstanceMinFilter : CimInstancePropertyBasedFilter { public CimInstanceMinFilter(string propertyName, object minPropertyValue, BehaviorOnNoMatch behaviorOnNoMatch) { @@ -287,7 +292,7 @@ public CimInstanceMinFilter(string propertyName, object minPropertyValue, Behavi } } - private class CimInstanceMaxFilter : CimInstancePropertyBasedFilter + private sealed class CimInstanceMaxFilter : CimInstancePropertyBasedFilter { public CimInstanceMaxFilter(string propertyName, object minPropertyValue, BehaviorOnNoMatch behaviorOnNoMatch) { @@ -308,7 +313,7 @@ public CimInstanceMaxFilter(string propertyName, object minPropertyValue, Behavi } } - private class CimInstanceAssociationFilter : CimInstanceFilterBase + private sealed class CimInstanceAssociationFilter : CimInstanceFilterBase { public CimInstanceAssociationFilter(BehaviorOnNoMatch behaviorOnNoMatch) { @@ -346,10 +351,13 @@ public BehaviorOnNoMatch BehaviorOnNoMatch { _behaviorOnNoMatch = this.GetDefaultBehaviorWhenNoMatchesFound(this.CimTypedExpectedPropertyValue); } + return _behaviorOnNoMatch; } } + protected abstract BehaviorOnNoMatch GetDefaultBehaviorWhenNoMatchesFound(object cimTypedExpectedPropertyValue); + private BehaviorOnNoMatch _behaviorOnNoMatch; public string PropertyName { get; } @@ -372,6 +380,7 @@ public bool IsMatch(CimInstance o) { return false; } + object actualPropertyValue = propertyInfo.Value; if (CimTypedExpectedPropertyValue == null) @@ -395,7 +404,7 @@ public bool IsMatch(CimInstance o) private object ConvertActualValueToExpectedType(object actualPropertyValue, object expectedPropertyValue) { - if ((actualPropertyValue is string) && (!(expectedPropertyValue is string))) + if (actualPropertyValue is string && expectedPropertyValue is not string) { actualPropertyValue = LanguagePrimitives.ConvertTo(actualPropertyValue, expectedPropertyValue.GetType(), CultureInfo.InvariantCulture); } @@ -423,6 +432,7 @@ private static bool IsSameType(object actualPropertyValue, object expectedProper { return true; } + if (expectedPropertyValue == null) { return true; @@ -455,8 +465,7 @@ protected override BehaviorOnNoMatch GetDefaultBehaviorWhenNoMatchesFound(object } else { - string expectedPropertyValueAsString = cimTypedExpectedPropertyValue as string; - if (expectedPropertyValueAsString != null && WildcardPattern.ContainsWildcardCharacters(expectedPropertyValueAsString)) + if (cimTypedExpectedPropertyValue is string expectedPropertyValueAsString && WildcardPattern.ContainsWildcardCharacters(expectedPropertyValueAsString)) { return BehaviorOnNoMatch.SilentlyContinue; } @@ -492,8 +501,8 @@ private static bool NonWildcardEqual(string propertyName, object actualPropertyV expectedPropertyValue = expectedPropertyValue.ToString(); actualPropertyValue = actualPropertyValue.ToString(); } - var expectedPropertyValueAsString = expectedPropertyValue as string; - if (expectedPropertyValueAsString != null) + + if (expectedPropertyValue is string expectedPropertyValueAsString) { var actualPropertyValueAsString = (string)actualPropertyValue; return actualPropertyValueAsString.Equals(expectedPropertyValueAsString, StringComparison.OrdinalIgnoreCase); @@ -511,15 +520,17 @@ private static bool WildcardEqual(string propertyName, object actualPropertyValu { return false; } + if (!LanguagePrimitives.TryConvertTo(expectedPropertyValue, out expectedPropertyValueAsString)) { return false; } + return WildcardPattern.Get(expectedPropertyValueAsString, WildcardOptions.IgnoreCase).IsMatch(actualPropertyValueAsString); } } - internal class PropertyValueExcludeFilter : PropertyValueRegularFilter + internal sealed class PropertyValueExcludeFilter : PropertyValueRegularFilter { public PropertyValueExcludeFilter(string propertyName, object expectedPropertyValue, bool wildcardsEnabled, BehaviorOnNoMatch behaviorOnNoMatch) : base(propertyName, expectedPropertyValue, wildcardsEnabled, behaviorOnNoMatch) @@ -537,7 +548,7 @@ protected override bool IsMatchingValue(object actualPropertyValue) } } - internal class PropertyValueMinFilter : PropertyValueFilter + internal sealed class PropertyValueMinFilter : PropertyValueFilter { public PropertyValueMinFilter(string propertyName, object expectedPropertyValue, BehaviorOnNoMatch behaviorOnNoMatch) : base(propertyName, expectedPropertyValue, behaviorOnNoMatch) @@ -558,8 +569,7 @@ private static bool ActualValueGreaterThanOrEqualToExpectedValue(string property { try { - var expectedComparable = expectedPropertyValue as IComparable; - if (expectedComparable == null) + if (expectedPropertyValue is not IComparable expectedComparable) { return false; } @@ -573,7 +583,7 @@ private static bool ActualValueGreaterThanOrEqualToExpectedValue(string property } } - internal class PropertyValueMaxFilter : PropertyValueFilter + internal sealed class PropertyValueMaxFilter : PropertyValueFilter { public PropertyValueMaxFilter(string propertyName, object expectedPropertyValue, BehaviorOnNoMatch behaviorOnNoMatch) : base(propertyName, expectedPropertyValue, behaviorOnNoMatch) @@ -594,8 +604,7 @@ private static bool ActualValueLessThanOrEqualToExpectedValue(string propertyNam { try { - var actualComparable = actualPropertyValue as IComparable; - if (actualComparable == null) + if (actualPropertyValue is not IComparable actualComparable) { return false; } @@ -612,8 +621,8 @@ private static bool ActualValueLessThanOrEqualToExpectedValue(string propertyNam private int _numberOfResultsFromMi; private int _numberOfMatchingResults; - private readonly List _filters = new List(); - private readonly object _myLock = new object(); + private readonly List _filters = new(); + private readonly object _myLock = new(); #region "Public" interface for client-side filtering @@ -644,7 +653,7 @@ internal IEnumerable GenerateNotFoundErrors() return Enumerable.Empty(); } - if (_filters.All(f => !f.ShouldReportErrorOnNoMatches_IfMultipleFilters())) + if (_filters.All(static f => !f.ShouldReportErrorOnNoMatches_IfMultipleFilters())) { return Enumerable.Empty(); } diff --git a/src/Microsoft.PowerShell.Commands.Management/commands/management/AddContentCommand.cs b/src/Microsoft.PowerShell.Commands.Management/commands/management/AddContentCommand.cs index d6a1c7f067f..0f9762084d5 100644 --- a/src/Microsoft.PowerShell.Commands.Management/commands/management/AddContentCommand.cs +++ b/src/Microsoft.PowerShell.Commands.Management/commands/management/AddContentCommand.cs @@ -1,12 +1,10 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. using System; using System.Collections.Generic; using System.Management.Automation; using System.Management.Automation.Internal; -using Dbg = System.Management.Automation; namespace Microsoft.PowerShell.Commands { @@ -14,7 +12,7 @@ namespace Microsoft.PowerShell.Commands /// A command that appends the specified content to the item at the specified path. /// [Cmdlet(VerbsCommon.Add, "Content", DefaultParameterSetName = "Path", SupportsShouldProcess = true, SupportsTransactions = true, - HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113278")] + HelpUri = "https://go.microsoft.com/fwlink/?linkid=2096489")] public class AddContentCommand : WriteContentCommandBase { #region protected members @@ -23,15 +21,12 @@ public class AddContentCommand : WriteContentCommandBase /// Seeks to the end of the writer stream in each of the writers in the /// content holders. /// - /// /// /// The content holders that contain the writers to be moved. /// - /// /// /// If calling Seek on the content writer throws an exception. /// - /// internal override void SeekContentPosition(List contentHolders) { foreach (ContentHolder holder in contentHolders) @@ -45,14 +40,13 @@ internal override void SeekContentPosition(List contentHolders) catch (Exception e) // Catch-all OK, 3rd party callout { ProviderInvocationException providerException = - new ProviderInvocationException( + new( "ProviderSeekError", SessionStateStrings.ProviderSeekError, holder.PathInfo.Provider, holder.PathInfo.Path, e); - // Log a provider health event MshLog.LogProviderHealthEvent( @@ -65,20 +59,17 @@ internal override void SeekContentPosition(List contentHolders) } } } - } // SeekContentPosition + } /// /// Makes the call to ShouldProcess with appropriate action and target strings. /// - /// /// /// The path to the item on which the content will be added. /// - /// /// /// True if the action should continue or false otherwise. /// - /// internal override bool CallShouldProcess(string path) { string action = NavigationResources.AddContentAction; @@ -89,6 +80,5 @@ internal override bool CallShouldProcess(string path) } #endregion protected members - } // AddContentCommand -} // namespace Microsoft.PowerShell.Commands - + } +} diff --git a/src/Microsoft.PowerShell.Commands.Management/commands/management/CIMHelper.cs b/src/Microsoft.PowerShell.Commands.Management/commands/management/CIMHelper.cs index ad56f989ae7..688b6362e16 100644 --- a/src/Microsoft.PowerShell.Commands.Management/commands/management/CIMHelper.cs +++ b/src/Microsoft.PowerShell.Commands.Management/commands/management/CIMHelper.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; using System.Collections.Generic; using System.Reflection; @@ -76,14 +79,13 @@ internal static string WqlQueryAll(string from) /// internal static T GetFirst(CimSession session, string nameSpace, string wmiClassName) where T : class, new() { - if (string.IsNullOrEmpty(wmiClassName)) - throw new ArgumentException("String argument may not be null or empty", "wmiClassName"); + ArgumentException.ThrowIfNullOrEmpty(wmiClassName); try { var type = typeof(T); - var binding = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance; - T rv = new T(); + const BindingFlags binding = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance; + T rv = new(); using (var instance = session.QueryFirstInstance(nameSpace, CIMHelper.WqlQueryAll(wmiClassName))) { @@ -129,8 +131,7 @@ internal static string WqlQueryAll(string from) /// internal static T[] GetAll(CimSession session, string nameSpace, string wmiClassName) where T : class, new() { - if (string.IsNullOrEmpty(wmiClassName)) - throw new ArgumentException("String argument may not be null or empty", "wmiClassName"); + ArgumentException.ThrowIfNullOrEmpty(wmiClassName); var rv = new List(); @@ -141,11 +142,11 @@ internal static string WqlQueryAll(string from) if (instances != null) { var type = typeof(T); - var binding = BindingFlags.Public | BindingFlags.Instance; + const BindingFlags binding = BindingFlags.Public | BindingFlags.Instance; foreach (var instance in instances) { - T objT = new T(); + T objT = new(); using (instance) { @@ -247,11 +248,11 @@ internal static class CIMExtensions /// /// An "overload" of the /// .QueryInstances - /// method that takes only the namespace and query string as a parameters + /// method that takes only the namespace and query string as a parameters. /// - /// The CimSession to be queried - /// A string containing the namespace to run the query against - /// A string containing the query to be run + /// The CimSession to be queried. + /// A string containing the namespace to run the query against. + /// A string containing the query to be run. /// /// An IEnumerable interface that can be used to enumerate the instances /// @@ -263,9 +264,9 @@ internal static IEnumerable QueryInstances(this CimSession session, /// /// Execute a CIM query and return only the first instance in the result. /// - /// The CimSession to be queried - /// A string containing the namespace to run the query against - /// A string containing the query to be run + /// The CimSession to be queried. + /// A string containing the namespace to run the query against. + /// A string containing the query to be run. /// /// A object /// representing the first instance in a query result if successful, null @@ -292,8 +293,8 @@ internal static CimInstance QueryFirstInstance(this CimSession session, string n /// /// Execute a CIM query and return only the first instance in the result. /// - /// The CimSession to be queried - /// A string containing the query to be run + /// The CimSession to be queried. + /// A string containing the query to be run. /// /// A object /// representing the first instance in a query result if successful, null diff --git a/src/Microsoft.PowerShell.Commands.Management/commands/management/ClearContentCommand.cs b/src/Microsoft.PowerShell.Commands.Management/commands/management/ClearContentCommand.cs index 13ec9dc416f..bd6ba2ee726 100644 --- a/src/Microsoft.PowerShell.Commands.Management/commands/management/ClearContentCommand.cs +++ b/src/Microsoft.PowerShell.Commands.Management/commands/management/ClearContentCommand.cs @@ -1,9 +1,7 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. using System.Management.Automation; -using Dbg = System.Management.Automation; namespace Microsoft.PowerShell.Commands { @@ -11,7 +9,7 @@ namespace Microsoft.PowerShell.Commands /// A command that appends the specified content to the item at the specified path. /// [Cmdlet(VerbsCommon.Clear, "Content", DefaultParameterSetName = "Path", SupportsShouldProcess = true, SupportsTransactions = true, - HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113282")] + HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2096807")] public class ClearContentCommand : ContentCommandBase { #region Command code @@ -62,11 +60,11 @@ protected override void ProcessRecord() pathNotFound)); } } - } // ProcessRecord + } #endregion Command code /// - /// Determines if the provider for the specified path supports ShouldProcess + /// Determines if the provider for the specified path supports ShouldProcess. /// /// protected override bool ProviderSupportsShouldProcess @@ -77,22 +75,18 @@ protected override bool ProviderSupportsShouldProcess } } - /// /// A virtual method for retrieving the dynamic parameters for a cmdlet. Derived cmdlets /// that require dynamic parameters should override this method and return the /// dynamic parameter object. /// - /// /// /// The context under which the command is running. /// - /// /// /// An object representing the dynamic parameters for the cmdlet or null if there /// are none. /// - /// internal override object GetDynamicParameters(CmdletProviderContext context) { if (Path != null && Path.Length > 0) @@ -102,7 +96,6 @@ internal override object GetDynamicParameters(CmdletProviderContext context) } return InvokeProvider.Content.ClearContentDynamicParameters(".", context); - } // GetDynamicParameters - } // ClearContentCommand -} // namespace Microsoft.PowerShell.Commands - + } + } +} diff --git a/src/Microsoft.PowerShell.Commands.Management/commands/management/ClearPropertyCommand.cs b/src/Microsoft.PowerShell.Commands.Management/commands/management/ClearPropertyCommand.cs index 6e74d62b502..f63eb8eed91 100644 --- a/src/Microsoft.PowerShell.Commands.Management/commands/management/ClearPropertyCommand.cs +++ b/src/Microsoft.PowerShell.Commands.Management/commands/management/ClearPropertyCommand.cs @@ -1,24 +1,22 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. using System.Collections.ObjectModel; using System.Management.Automation; -using Dbg = System.Management.Automation; namespace Microsoft.PowerShell.Commands { /// - /// A command to clear the value of a property of an item at a specified path + /// A command to clear the value of a property of an item at a specified path. /// [Cmdlet(VerbsCommon.Clear, "ItemProperty", DefaultParameterSetName = "Path", SupportsShouldProcess = true, SupportsTransactions = true, - HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113284")] + HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2096903")] public class ClearItemPropertyCommand : PassThroughItemPropertyCommandBase { #region Parameters /// - /// Gets or sets the path parameter to the command + /// Gets or sets the path parameter to the command. /// [Parameter(Position = 0, ParameterSetName = "Path", Mandatory = true, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true)] @@ -27,70 +25,66 @@ public string[] Path get { return paths; - } // get + } set { paths = value; - } // set - } // Path + } + } /// - /// Gets or sets the literal path parameter to the command + /// Gets or sets the literal path parameter to the command. /// [Parameter(ParameterSetName = "LiteralPath", Mandatory = true, ValueFromPipeline = false, ValueFromPipelineByPropertyName = true)] - [Alias("PSPath")] + [Alias("PSPath", "LP")] public string[] LiteralPath { get { return paths; - } // get + } set { base.SuppressWildcardExpansion = true; paths = value; - } // set - } // LiteralPath + } + } /// - /// The properties to clear from the item + /// The properties to clear from the item. /// - /// [Parameter(Position = 1, Mandatory = true, ValueFromPipelineByPropertyName = true)] public string Name { get { return _property; - } // get + } set { _property = value; } - } // Name + } /// /// A virtual method for retrieving the dynamic parameters for a cmdlet. Derived cmdlets /// that require dynamic parameters should override this method and return the /// dynamic parameter object. /// - /// /// /// The context under which the command is running. /// - /// /// /// An object representing the dynamic parameters for the cmdlet or null if there /// are none. /// - /// internal override object GetDynamicParameters(CmdletProviderContext context) { - Collection propertyCollection = new Collection(); + Collection propertyCollection = new(); propertyCollection.Add(_property); if (Path != null && Path.Length > 0) @@ -102,11 +96,12 @@ internal override object GetDynamicParameters(CmdletProviderContext context) propertyCollection, context); } + return InvokeProvider.Property.ClearPropertyDynamicParameters( ".", propertyCollection, context); - } // GetDynamicParameters + } #endregion Parameters @@ -122,14 +117,14 @@ internal override object GetDynamicParameters(CmdletProviderContext context) #region Command code /// - /// Clears the properties of an item at the specified path + /// Clears the properties of an item at the specified path. /// protected override void ProcessRecord() { CmdletProviderContext currentContext = CmdletProviderContext; currentContext.PassThru = PassThru; - Collection propertyCollection = new Collection(); + Collection propertyCollection = new(); propertyCollection.Add(_property); foreach (string path in Path) @@ -170,9 +165,8 @@ protected override void ProcessRecord() pathNotFound)); } } - } // ProcessRecord + } #endregion Command code - - } // ClearItemPropertyCommand -} // namespace Microsoft.PowerShell.Commands + } +} diff --git a/src/Microsoft.PowerShell.Commands.Management/commands/management/ClearRecycleBinCommand.cs b/src/Microsoft.PowerShell.Commands.Management/commands/management/ClearRecycleBinCommand.cs index 183c3712fbc..bc4e44220dd 100644 --- a/src/Microsoft.PowerShell.Commands.Management/commands/management/ClearRecycleBinCommand.cs +++ b/src/Microsoft.PowerShell.Commands.Management/commands/management/ClearRecycleBinCommand.cs @@ -1,11 +1,15 @@ -using System; +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.IO; using System.Management.Automation; using System.Runtime.InteropServices; -using System.IO; -using System.Globalization; -using System.ComponentModel; using System.Text.RegularExpressions; -using System.Diagnostics.CodeAnalysis; + +#if !UNIX namespace Microsoft.PowerShell.Commands { @@ -14,7 +18,7 @@ namespace Microsoft.PowerShell.Commands /// This cmdlet clear all files in the RecycleBin for the given DriveLetter. /// If not DriveLetter is specified, then the RecycleBin for all drives are cleared. /// - [Cmdlet(VerbsCommon.Clear, "RecycleBin", SupportsShouldProcess = true, HelpUri = "https://go.microsoft.com/fwlink/?LinkId=524082", ConfirmImpact = ConfirmImpact.High)] + [Cmdlet(VerbsCommon.Clear, "RecycleBin", SupportsShouldProcess = true, HelpUri = "https://go.microsoft.com/fwlink/?LinkId=2109377", ConfirmImpact = ConfirmImpact.High)] public class ClearRecycleBinCommand : PSCmdlet { private string[] _drivesList; @@ -30,19 +34,21 @@ public class ClearRecycleBinCommand : PSCmdlet public string[] DriveLetter { get { return _drivesList; } + set { _drivesList = value; } } /// /// Property that sets force parameter. This will allow to clear the recyclebin. /// - [Parameter()] + [Parameter] public SwitchParameter Force { get { return _force; } + set { _force = value; @@ -72,7 +78,7 @@ protected override void ProcessRecord() { WriteError(new ErrorRecord( new ArgumentException( - String.Format(CultureInfo.InvariantCulture, ClearRecycleBinResources.InvalidDriveNameFormat, "C", "C:", "C:\\")), + string.Format(CultureInfo.InvariantCulture, ClearRecycleBinResources.InvalidDriveNameFormat, "C", "C:", "C:\\")), "InvalidDriveNameFormat", ErrorCategory.InvalidArgument, drive)); @@ -106,7 +112,7 @@ private bool ValidDrivePath(string drivePath) { foreach (DriveInfo drive in _availableDrives) { - if (String.Compare(drive.Name, drivePath, StringComparison.OrdinalIgnoreCase) == 0) + if (string.Equals(drive.Name, drivePath, StringComparison.OrdinalIgnoreCase)) { actualDrive = drive; break; @@ -119,7 +125,7 @@ private bool ValidDrivePath(string drivePath) { WriteError(new ErrorRecord( new System.IO.DriveNotFoundException( - String.Format(CultureInfo.InvariantCulture, ClearRecycleBinResources.DriveNotFound, drivePath, "Get-Volume")), + string.Format(CultureInfo.InvariantCulture, ClearRecycleBinResources.DriveNotFound, drivePath, "Get-Volume")), "DriveNotFound", ErrorCategory.InvalidArgument, drivePath)); @@ -131,13 +137,15 @@ private bool ValidDrivePath(string drivePath) // The drive path exists, and the drive is 'fixed'. return true; } + WriteError(new ErrorRecord( new ArgumentException( - String.Format(CultureInfo.InvariantCulture, ClearRecycleBinResources.InvalidDriveType, drivePath, "Get-Volume")), + string.Format(CultureInfo.InvariantCulture, ClearRecycleBinResources.InvalidDriveType, drivePath, "Get-Volume")), "InvalidDriveType", ErrorCategory.InvalidArgument, drivePath)); } + return false; } @@ -146,7 +154,7 @@ private bool ValidDrivePath(string drivePath) /// /// /// - private bool IsValidPattern(string input) + private static bool IsValidPattern(string input) { return Regex.IsMatch(input, @"^[a-z]{1}$|^[a-z]{1}:$|^[a-z]{1}:\\$", RegexOptions.IgnoreCase); } @@ -157,14 +165,14 @@ private bool IsValidPattern(string input) /// /// /// - private string GetDrivePath(string driveName) + private static string GetDrivePath(string driveName) { string drivePath; if (driveName.EndsWith(":\\", StringComparison.OrdinalIgnoreCase)) { drivePath = driveName; } - else if (driveName.EndsWith(":", StringComparison.OrdinalIgnoreCase)) + else if (driveName.EndsWith(':')) { drivePath = driveName + "\\"; } @@ -172,6 +180,7 @@ private string GetDrivePath(string driveName) { drivePath = driveName + ":\\"; } + return drivePath; } @@ -199,47 +208,36 @@ private void EmptyRecycleBin(string drivePath) { // If driveName is null, then clear the recyclebin for all drives; otherwise, just for the specified driveName. - string activity = String.Format(CultureInfo.InvariantCulture, ClearRecycleBinResources.ClearRecycleBinProgressActivity); + string activity = string.Format(CultureInfo.InvariantCulture, ClearRecycleBinResources.ClearRecycleBinProgressActivity); string statusDescription; if (drivePath == null) { - statusDescription = String.Format(CultureInfo.InvariantCulture, ClearRecycleBinResources.ClearRecycleBinStatusDescriptionForAllDrives); + statusDescription = string.Format(CultureInfo.InvariantCulture, ClearRecycleBinResources.ClearRecycleBinStatusDescriptionForAllDrives); } else { - statusDescription = String.Format(CultureInfo.InvariantCulture, ClearRecycleBinResources.ClearRecycleBinStatusDescriptionByDrive, drivePath); + statusDescription = string.Format(CultureInfo.InvariantCulture, ClearRecycleBinResources.ClearRecycleBinStatusDescriptionByDrive, drivePath); } - ProgressRecord progress = new ProgressRecord(0, activity, statusDescription); + ProgressRecord progress = new(0, activity, statusDescription); progress.PercentComplete = 30; progress.RecordType = ProgressRecordType.Processing; WriteProgress(progress); + // no need to check result as a failure is returned only if recycle bin is already empty uint result = NativeMethod.SHEmptyRecycleBin(IntPtr.Zero, drivePath, NativeMethod.RecycleFlags.SHERB_NOCONFIRMATION | NativeMethod.RecycleFlags.SHERB_NOPROGRESSUI | NativeMethod.RecycleFlags.SHERB_NOSOUND); - int lastError = Marshal.GetLastWin32Error(); - - // update the progress bar to completed progress.PercentComplete = 100; progress.RecordType = ProgressRecordType.Completed; WriteProgress(progress); - - // 0 is for a successful operation - // 203 comes up when trying to empty an already emptied recyclebin - // 18 comes up when there are no more files in the given recyclebin - if (!(lastError == 0 || lastError == 203 || lastError == 18)) - { - Win32Exception exception = new Win32Exception(lastError); - WriteError(new ErrorRecord(exception, "FailedToClearRecycleBin", ErrorCategory.InvalidOperation, "RecycleBin")); - } } } } - internal static class NativeMethod + internal static partial class NativeMethod { // Internal code to SHEmptyRecycleBin internal enum RecycleFlags : uint @@ -248,7 +246,9 @@ internal enum RecycleFlags : uint SHERB_NOPROGRESSUI = 0x00000002, SHERB_NOSOUND = 0x00000004 } - [DllImport("Shell32.dll", CharSet = CharSet.Unicode)] - internal static extern uint SHEmptyRecycleBin(IntPtr hwnd, string pszRootPath, RecycleFlags dwFlags); + + [LibraryImport("Shell32.dll", StringMarshalling = StringMarshalling.Utf16, EntryPoint = "SHEmptyRecycleBinW")] + internal static partial uint SHEmptyRecycleBin(IntPtr hwnd, string pszRootPath, RecycleFlags dwFlags); } -} \ No newline at end of file +} +#endif diff --git a/src/Microsoft.PowerShell.Commands.Management/commands/management/Clipboard.cs b/src/Microsoft.PowerShell.Commands.Management/commands/management/Clipboard.cs new file mode 100644 index 00000000000..77e1b497b95 --- /dev/null +++ b/src/Microsoft.PowerShell.Commands.Management/commands/management/Clipboard.cs @@ -0,0 +1,390 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Diagnostics; +using System.Runtime.InteropServices; +using System.Threading; + +namespace Microsoft.PowerShell.Commands.Internal +{ + internal static partial class Clipboard + { + private static bool? _clipboardSupported; + + // Used if an external clipboard is not available, e.g. if xclip is missing. + // This is useful for testing in CI as well. + private static string _internalClipboard; + + private static string StartProcess( + string tool, + string args, + string stdin = "", + bool readStdout = true) + { + ProcessStartInfo startInfo = new(); + startInfo.UseShellExecute = false; + startInfo.RedirectStandardInput = true; + startInfo.RedirectStandardOutput = true; + startInfo.RedirectStandardError = true; + startInfo.FileName = tool; + startInfo.Arguments = args; + string stdout = string.Empty; + + using (Process process = new()) + { + process.StartInfo = startInfo; + try + { + process.Start(); + } + catch (System.ComponentModel.Win32Exception) + { + _clipboardSupported = false; + return string.Empty; + } + + process.StandardInput.Write(stdin); + process.StandardInput.Close(); + + if (readStdout) + { + stdout = process.StandardOutput.ReadToEnd(); + } + + process.WaitForExit(250); + _clipboardSupported = process.ExitCode == 0; + } + + return stdout; + } + + public static string GetText() + { + if (_clipboardSupported == false) + { + return _internalClipboard ?? string.Empty; + } + + string tool = string.Empty; + string args = string.Empty; + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + string clipboardText = string.Empty; + ExecuteOnStaThread(() => GetTextImpl(out clipboardText)); + return clipboardText; + } + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + { + tool = "xclip"; + args = "-selection clipboard -out"; + } + else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + { + tool = "pbpaste"; + } + else + { + _clipboardSupported = false; + return string.Empty; + } + + return StartProcess(tool, args); + } + + public static void SetText(string text) + { + if (_clipboardSupported == false) + { + _internalClipboard = text; + return; + } + + string tool = string.Empty; + string args = string.Empty; + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + ExecuteOnStaThread(() => SetClipboardData(Tuple.Create(text, CF_UNICODETEXT))); + return; + } + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + { + tool = "xclip"; + if (string.IsNullOrEmpty(text)) + { + args = "-selection clipboard /dev/null"; + } + else + { + args = "-selection clipboard -in"; + } + } + else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + { + tool = "pbcopy"; + } + else + { + _clipboardSupported = false; + return; + } + + StartProcess(tool, args, text, readStdout: false); + if (_clipboardSupported == false) + { + _internalClipboard = text; + } + } + + public static void SetRtf(string plainText, string rtfText) + { + if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + return; + } + + if (s_CF_RTF == 0) + { + s_CF_RTF = RegisterClipboardFormat("Rich Text Format"); + } + + ExecuteOnStaThread(() => SetClipboardData( + Tuple.Create(plainText, CF_UNICODETEXT), + Tuple.Create(rtfText, s_CF_RTF))); + } + + private const uint GMEM_MOVEABLE = 0x0002; + private const uint GMEM_ZEROINIT = 0x0040; + private const uint GHND = GMEM_MOVEABLE | GMEM_ZEROINIT; + + [LibraryImport("kernel32.dll")] + private static partial IntPtr GlobalAlloc(uint flags, UIntPtr dwBytes); + + [LibraryImport("kernel32.dll")] + private static partial IntPtr GlobalFree(IntPtr hMem); + + [LibraryImport("kernel32.dll")] + private static partial IntPtr GlobalLock(IntPtr hMem); + + [LibraryImport("kernel32.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + private static partial bool GlobalUnlock(IntPtr hMem); + + [LibraryImport("kernel32.dll", EntryPoint = "RtlMoveMemory")] + private static partial void CopyMemory(IntPtr dest, IntPtr src, uint count); + + [LibraryImport("user32.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + private static partial bool IsClipboardFormatAvailable(uint format); + + [LibraryImport("user32.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + private static partial bool OpenClipboard(IntPtr hWndNewOwner); + + [LibraryImport("user32.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + private static partial bool CloseClipboard(); + + [LibraryImport("user32.dll")] + [return: MarshalAs(UnmanagedType.Bool)] + private static partial bool EmptyClipboard(); + + [LibraryImport("user32.dll")] + private static partial IntPtr GetClipboardData(uint format); + + [LibraryImport("user32.dll")] + private static partial IntPtr SetClipboardData(uint format, IntPtr data); + + [LibraryImport("user32.dll", StringMarshalling = StringMarshalling.Utf16)] + private static partial uint RegisterClipboardFormat(string lpszFormat); + + private const uint CF_TEXT = 1; + private const uint CF_UNICODETEXT = 13; + + private static uint s_CF_RTF; + + private static bool GetTextImpl(out string text) + { + try + { + if (IsClipboardFormatAvailable(CF_UNICODETEXT)) + { + if (OpenClipboard(IntPtr.Zero)) + { + var data = GetClipboardData(CF_UNICODETEXT); + if (data != IntPtr.Zero) + { + data = GlobalLock(data); + text = Marshal.PtrToStringUni(data); + GlobalUnlock(data); + return true; + } + } + } + else if (IsClipboardFormatAvailable(CF_TEXT)) + { + if (OpenClipboard(IntPtr.Zero)) + { + var data = GetClipboardData(CF_TEXT); + if (data != IntPtr.Zero) + { + data = GlobalLock(data); + text = Marshal.PtrToStringAnsi(data); + GlobalUnlock(data); + return true; + } + } + } + } + catch + { + // Ignore exceptions + } + finally + { + CloseClipboard(); + } + + text = string.Empty; + return false; + } + + private static bool SetClipboardData(params Tuple[] data) + { + try + { + if (!OpenClipboard(IntPtr.Zero)) + { + return false; + } + + EmptyClipboard(); + + foreach (var d in data) + { + if (!SetSingleClipboardData(d.Item1, d.Item2)) + { + return false; + } + } + } + finally + { + CloseClipboard(); + } + + return true; + } + + private static bool SetSingleClipboardData(string text, uint format) + { + IntPtr hGlobal = IntPtr.Zero; + IntPtr data = IntPtr.Zero; + + try + { + uint bytes; + if (format == s_CF_RTF || format == CF_TEXT) + { + bytes = (uint)(text.Length + 1); + data = Marshal.StringToHGlobalAnsi(text); + } + else if (format == CF_UNICODETEXT) + { + bytes = (uint)((text.Length + 1) * 2); + data = Marshal.StringToHGlobalUni(text); + } + else + { + // Not yet supported format. + return false; + } + + if (data == IntPtr.Zero) + { + return false; + } + + hGlobal = GlobalAlloc(GHND, (UIntPtr)bytes); + if (hGlobal == IntPtr.Zero) + { + return false; + } + + IntPtr dataCopy = GlobalLock(hGlobal); + if (dataCopy == IntPtr.Zero) + { + return false; + } + + CopyMemory(dataCopy, data, bytes); + GlobalUnlock(hGlobal); + + if (SetClipboardData(format, hGlobal) != IntPtr.Zero) + { + // The clipboard owns this memory now, so don't free it. + hGlobal = IntPtr.Zero; + } + } + catch + { + // Ignore failures + } + finally + { + if (data != IntPtr.Zero) + { + Marshal.FreeHGlobal(data); + } + + if (hGlobal != IntPtr.Zero) + { + GlobalFree(hGlobal); + } + } + + return true; + } + + private static void ExecuteOnStaThread(Func action) + { + const int RetryCount = 5; + int tries = 0; + + if (Thread.CurrentThread.GetApartmentState() == ApartmentState.STA) + { + while (tries++ < RetryCount && !action()) + { + // wait until RetryCount or action + } + + return; + } + + Exception exception = null; + var thread = new Thread(() => + { + try + { + while (tries++ < RetryCount && !action()) + { + // wait until RetryCount or action + } + } + catch (Exception e) + { + exception = e; + } + }); + + thread.SetApartmentState(ApartmentState.STA); + thread.Start(); + thread.Join(); + + if (exception != null) + { + throw exception; + } + } + } +} diff --git a/src/Microsoft.PowerShell.Commands.Management/commands/management/CombinePathCommand.cs b/src/Microsoft.PowerShell.Commands.Management/commands/management/CombinePathCommand.cs index e6293091539..79b1c862bfd 100644 --- a/src/Microsoft.PowerShell.Commands.Management/commands/management/CombinePathCommand.cs +++ b/src/Microsoft.PowerShell.Commands.Management/commands/management/CombinePathCommand.cs @@ -1,11 +1,10 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. using System; -using System.Text; using System.Collections.ObjectModel; using System.Management.Automation; + using Dbg = System.Management.Automation; namespace Microsoft.PowerShell.Commands @@ -14,26 +13,27 @@ namespace Microsoft.PowerShell.Commands /// A command that adds the parent and child parts of a path together /// with the appropriate path separator. /// - [Cmdlet(VerbsCommon.Join, "Path", SupportsTransactions = true, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113347")] + [Cmdlet(VerbsCommon.Join, "Path", SupportsTransactions = true, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2096811")] [OutputType(typeof(string))] public class JoinPathCommand : CoreCommandWithCredentialsBase { #region Parameters /// - /// Gets or sets the path parameter to the command + /// Gets or sets the path parameter to the command. /// [Parameter(Position = 0, Mandatory = true, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true)] [Alias("PSPath")] public string[] Path { get; set; } /// - /// Gets or sets the childPath parameter to the command + /// Gets or sets the childPath parameter to the command. /// [Parameter(Position = 1, Mandatory = true, ValueFromPipelineByPropertyName = true)] [AllowNull] [AllowEmptyString] - public string ChildPath { get; set; } + [AllowEmptyCollection] + public string[] ChildPath { get; set; } /// /// Gets or sets additional childPaths to the command. @@ -42,15 +42,30 @@ public class JoinPathCommand : CoreCommandWithCredentialsBase [AllowNull] [AllowEmptyString] [AllowEmptyCollection] - public string[] AdditionalChildPath { get; set; } = Utils.EmptyArray(); + public string[] AdditionalChildPath { get; set; } = Array.Empty(); /// - /// Determines if the path should be resolved after being joined + /// Determines if the path should be resolved after being joined. /// /// [Parameter] public SwitchParameter Resolve { get; set; } + /// + /// Gets or sets the extension to use for the resulting path. + /// If not specified, the original extension (if any) is preserved. + /// + /// Behavior: + /// - If the path has an existing extension, it will be replaced with the specified extension. + /// - If the path does not have an extension, the specified extension will be added. + /// - If an empty string is provided, any existing extension will be removed. + /// - A leading dot in the extension is optional; if omitted, one will be added automatically. + /// + /// + [Parameter(ValueFromPipelineByPropertyName = true)] + [ValidateNotNull] + public string Extension { get; set; } + #endregion Parameters #region Command code @@ -65,7 +80,15 @@ protected override void ProcessRecord() Path != null, "Since Path is a mandatory parameter, paths should never be null"); - string combinedChildPath = ChildPath; + string combinedChildPath = string.Empty; + + if (this.ChildPath != null) + { + foreach (string childPath in this.ChildPath) + { + combinedChildPath = SessionState.Path.Combine(combinedChildPath, childPath, CmdletProviderContext); + } + } // join the ChildPath elements if (AdditionalChildPath != null) @@ -120,6 +143,12 @@ protected override void ProcessRecord() continue; } + // If Extension parameter is present it is not null due to [ValidateNotNull]. + if (Extension is not null) + { + joinedPath = System.IO.Path.ChangeExtension(joinedPath, Extension.Length == 0 ? null : Extension); + } + if (Resolve) { // Resolve the paths. The default API (GetResolvedPSPathFromPSPath) @@ -204,7 +233,7 @@ protected override void ProcessRecord() pathNotFound)); continue; } - } // for each path + } } else { @@ -214,10 +243,8 @@ protected override void ProcessRecord() } } } - } // ProcessRecord + } #endregion Command code - - } // JoinPathCommand -} // namespace Microsoft.PowerShell.Commands - + } +} diff --git a/src/Microsoft.PowerShell.Commands.Management/commands/management/CommitTransactionCommand.cs b/src/Microsoft.PowerShell.Commands.Management/commands/management/CommitTransactionCommand.cs index 2058bdd833a..94e923bfa6e 100644 --- a/src/Microsoft.PowerShell.Commands.Management/commands/management/CommitTransactionCommand.cs +++ b/src/Microsoft.PowerShell.Commands.Management/commands/management/CommitTransactionCommand.cs @@ -1,8 +1,8 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. using System.Management.Automation; + using Dbg = System.Management.Automation; namespace Microsoft.PowerShell.Commands @@ -14,7 +14,7 @@ namespace Microsoft.PowerShell.Commands public class CompleteTransactionCommand : PSCmdlet { /// - /// Commits the current transaction + /// Commits the current transaction. /// protected override void EndProcessing() { @@ -26,6 +26,5 @@ protected override void EndProcessing() this.Context.TransactionManager.Commit(); } } - } // CommitTransactionCommand -} // namespace Microsoft.PowerShell.Commands - + } +} diff --git a/src/Microsoft.PowerShell.Commands.Management/commands/management/Computer.cs b/src/Microsoft.PowerShell.Commands.Management/commands/management/Computer.cs index 1e6b7e1392d..ea8c111531c 100644 --- a/src/Microsoft.PowerShell.Commands.Management/commands/management/Computer.cs +++ b/src/Microsoft.PowerShell.Commands.Management/commands/management/Computer.cs @@ -1,8 +1,8 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + #if !UNIX -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ using System; using System.Collections; using System.Collections.Generic; @@ -11,6268 +11,1619 @@ using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; +using System.Linq; using System.Management.Automation; using System.Management.Automation.Internal; using System.Net; -using System.Reflection; -using System.Runtime.InteropServices; using System.Runtime.Serialization; using System.Security.Cryptography; -using System.Security.Permissions; using System.Text; using System.Threading; -using Microsoft.Win32; -using Microsoft.PowerShell.Commands.Internal; using Microsoft.Management.Infrastructure; using Microsoft.Management.Infrastructure.Options; -using System.Linq; +using Microsoft.Win32; using Dbg = System.Management.Automation; -#if CORECLR -using Microsoft.PowerShell.CoreClr.Stubs; -#else -//TODO:CORECLR System.DirectoryServices is not available on CORE CLR -using System.DirectoryServices; -using System.Management; // We are not porting the library to CoreCLR -using Microsoft.WSMan.Management; -#endif - -// FxCop suppressions for resource strings: -[module: SuppressMessage("Microsoft.Naming", "CA1703:ResourceStringsShouldBeSpelledCorrectly", Scope = "resource", Target = "ComputerResources.resources", MessageId = "unjoined")] -[module: SuppressMessage("Microsoft.Naming", "CA1701:ResourceStringCompoundWordsShouldBeCasedCorrectly", Scope = "resource", Target = "ComputerResources.resources", MessageId = "UpTime")] - namespace Microsoft.PowerShell.Commands { -#region Test-Connection + #region Restart-Computer /// - /// This cmdlet is used to test whether a particular host is reachable across an - /// IP network. It works by sending ICMP "echo request" packets to the target - /// host and listening for ICMP "echo response" replies. This cmdlet prints a - /// statistical summary when finished. + /// This exception is thrown when the timeout expires before a computer finishes restarting. /// - [Cmdlet(VerbsDiagnostic.Test, "Connection", DefaultParameterSetName = RegularParameterSet, - HelpUri = "https://go.microsoft.com/fwlink/?LinkID=135266", RemotingCapability = RemotingCapability.OwnedByCommand)] - [OutputType(typeof(Boolean))] - [OutputType(@"System.Management.ManagementObject#root\cimv2\Win32_PingStatus")] - public class TestConnectionCommand : PSCmdlet + public sealed class RestartComputerTimeoutException : RuntimeException { -#region "Parameters" + /// + /// Name of the computer that is restarting. + /// + public string ComputerName { get; } - private const string RegularParameterSet = "Default"; - private const string QuietParameterSet = "Quiet"; - private const string SourceParameterSet = "Source"; + /// + /// The timeout value specified by the user. It indicates the seconds to wait before timeout. + /// + public int Timeout { get; } /// - /// + /// Construct a RestartComputerTimeoutException. /// - [Parameter(ParameterSetName = SourceParameterSet)] - [Parameter(ParameterSetName = RegularParameterSet)] - public SwitchParameter AsJob { get; set; } = false; + /// + /// + /// + /// + internal RestartComputerTimeoutException(string computerName, int timeout, string message, string errorId) + : base(message) + { + SetErrorId(errorId); + SetErrorCategory(ErrorCategory.OperationTimeout); + ComputerName = computerName; + Timeout = timeout; + } /// - /// The following is the definition of the input parameter "DcomAuthentication". - /// Specifies the authentication level to be used with WMI connection. Valid - /// values are: - /// - /// Unchanged = -1, - /// Default = 0, - /// None = 1, - /// Connect = 2, - /// Call = 3, - /// Packet = 4, - /// PacketIntegrity = 5, - /// PacketPrivacy = 6. + /// Construct a RestartComputerTimeoutException. /// + public RestartComputerTimeoutException() : base() { } - [Parameter] - [Alias("Authentication")] - public AuthenticationLevel DcomAuthentication { get; set; } = AuthenticationLevel.Packet; + /// + /// Constructs a RestartComputerTimeoutException. + /// + /// + /// The message used in the exception. + /// + public RestartComputerTimeoutException(string message) : base(message) { } /// - /// The authentication options for CIM_WSMan connection + /// Constructs a RestartComputerTimeoutException. /// - [Parameter] - [ValidateSet( - "Default", - "Basic", - "Negotiate", // can be used with and without credential (without -> PSRP mapped to NegotiateWithImplicitCredential) - "CredSSP", - "Digest", - "Kerberos")] // can be used with and without credential (not sure about implications) - [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] - public string WsmanAuthentication { get; set; } = "Default"; + /// + /// The message used in the exception. + /// + /// + /// An exception that led to this exception. + /// + public RestartComputerTimeoutException(string message, Exception innerException) : base(message, innerException) { } + } + /// + /// Defines the services that Restart-Computer can wait on. + /// + [SuppressMessage("Microsoft.Design", "CA1027:MarkEnumsWithFlags")] + public enum WaitForServiceTypes + { /// - /// Specify the protocol to use + /// Wait for the WMI service to be ready. /// - [Parameter] - [ValidateSet(ComputerWMIHelper.DcomProtocol, ComputerWMIHelper.WsmanProtocol)] - public string Protocol { get; set; } = -#if CORECLR - //CoreClr does not support DCOM protocol - // This change makes sure that the the command works seamlessly if user did not explicitly entered the protocol - ComputerWMIHelper.WsmanProtocol; -#else - ComputerWMIHelper.DcomProtocol; -#endif + Wmi = 0x0, /// - /// The following is the definition of the input parameter "BufferSize". - /// Buffer size sent with the this command. The default value is 32. + /// Wait for the WinRM service to be ready. /// - [Parameter] - [Alias("Size", "Bytes", "BS")] - [ValidateRange((int)0, (int)65500)] - public Int32 BufferSize { get; set; } = 32; + WinRM = 0x1, /// - /// The following is the definition of the input parameter "TimeOut". - /// Time-out value in milliseconds. If a response is not received in this time, no response is assumed. The default is 1000 milliseconds. + /// Wait for the PowerShell to be ready. /// - [Parameter] - [ValidateRange((int)1, Int32.MaxValue)] - public Int32 TimeOut { get; set; } = 1000; + PowerShell = 0x2, + } + + /// + /// Restarts the computer. + /// + [Cmdlet(VerbsLifecycle.Restart, "Computer", SupportsShouldProcess = true, DefaultParameterSetName = DefaultParameterSet, + HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2097060", RemotingCapability = RemotingCapability.OwnedByCommand)] + public class RestartComputerCommand : PSCmdlet, IDisposable + { + #region "Parameters and PrivateData" + + private const string DefaultParameterSet = "DefaultSet"; + private const int forcedReboot = 6; // see https://msdn.microsoft.com/library/aa394058(v=vs.85).aspx /// - /// The following is the definition of the input parameter "ComputerName". - /// Value of the address requested. The form of the value can be either the - /// computer name ("wxyz1234"), IPv4 address ("192.168.177.124"), or IPv6 - /// address ("2010:836B:4179::836B:4179"). + /// The authentication options for CIM_WSMan connection. + /// + [Parameter(ParameterSetName = DefaultParameterSet)] + [ValidateSet( + "Default", + "Basic", + "Negotiate", // can be used with and without credential (without -> PSRP mapped to NegotiateWithImplicitCredential) + "CredSSP", + "Digest", + "Kerberos")] // can be used with explicit or implicit credential + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] + public string WsmanAuthentication { get; set; } + + /// + /// Specifies the computer (s)Name on which this command is executed. + /// When this parameter is omitted, this cmdlet restarts the local computer. + /// Type the NETBIOS name, IP address, or fully-qualified domain name of one + /// or more computers in a comma-separated list. To specify the local computer, type the computername or "localhost". /// - [Parameter(Mandatory = true, - Position = 0, + [Parameter(Position = 0, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true)] [ValidateNotNullOrEmpty] [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] - [Alias("CN", "IPAddress", "__SERVER", "Server", "Destination")] - public String[] ComputerName { get; set; } + [Alias("CN", "__SERVER", "Server", "IPAddress")] + public string[] ComputerName { get; set; } = new string[] { "." }; - /// - /// The following is the definition of the input parameter "Count". - /// Number of echo requests to send. - /// - [Parameter] - [ValidateRange(1, UInt32.MaxValue)] - public Int32 Count { get; set; } = 4; + private List _validatedComputerNames = new(); + private readonly List _waitOnComputers = new(); + private readonly HashSet _uniqueComputerNames = new(StringComparer.OrdinalIgnoreCase); /// /// The following is the definition of the input parameter "Credential". /// Specifies a user account that has permission to perform this action. Type a /// user-name, such as "User01" or "Domain01\User01", or enter a PSCredential - /// object, such as one from the Get-Credential cmdlet + /// object, such as one from the Get-Credential cmdlet. /// - [Parameter(ParameterSetName = SourceParameterSet, Mandatory = false)] + [Parameter(Position = 1)] [ValidateNotNullOrEmpty] [Credential] public PSCredential Credential { get; set; } /// - /// The following is the definition of the input parameter "FromComputerName". - /// Specifies the Computer names where the ping request is originated from. + /// Using Force in conjunction with Reboot on a + /// remote computer immediately reboots the remote computer. /// - [Parameter(Position = 1, ParameterSetName = SourceParameterSet, Mandatory = true)] - [ValidateNotNullOrEmpty] - [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] - [Alias("FCN", "SRC")] - public String[] Source { get; set; } = new string[] { "." }; + [Parameter] + [Alias("f")] + public SwitchParameter Force { get; set; } /// - /// The following is the definition of the input parameter "Impersonation". - /// Specifies the impersonation level to use when calling the WMI method. Valid - /// values are: - /// - /// Default = 0, - /// Anonymous = 1, - /// Identify = 2, - /// Impersonate = 3, - /// Delegate = 4. + /// Specify the Wait parameter. Prompt will be blocked is the Timeout is not 0. /// - [Parameter] - public ImpersonationLevel Impersonation { get; set; } = ImpersonationLevel.Impersonate; + [Parameter(ParameterSetName = DefaultParameterSet)] + public SwitchParameter Wait { get; set; } /// - /// The following is the definition of the input parameter "ThrottleLimit". - /// The number of concurrent computers on which the command will be allowed to - /// execute + /// Specify the Timeout parameter. + /// Negative value indicates wait infinitely. + /// Positive value indicates the seconds to wait before timeout. /// - [Parameter(ParameterSetName = SourceParameterSet)] - [Parameter(ParameterSetName = RegularParameterSet)] - [ValidateRange(int.MinValue, (int)1000)] - public Int32 ThrottleLimit + [Parameter(ParameterSetName = DefaultParameterSet)] + [Alias("TimeoutSec")] + [ValidateRange(-1, int.MaxValue)] + public int Timeout { - get { return throttlelimit; } + get + { + return _timeout; + } + set { - throttlelimit = value; - if (throttlelimit <= 0) - throttlelimit = 32; + _timeout = value; + _timeoutSpecified = true; } } - private Int32 throttlelimit = 32; - /// - /// The following is the definition of the input parameter "TimeToLive". - /// Life span of the packet in seconds. The value is treated as an upper limit. - /// All routers must decrement this value by 1 (one). When this value becomes 0 - /// (zero), the packet is dropped by the router. The default value is 80 - /// seconds. The hops between routers rarely take this amount of time. - /// - [Parameter] - [ValidateRange(1, (int)255)] - [Alias("TTL")] - public Int32 TimeToLive { get; set; } = 80; + private int _timeout = -1; + private bool _timeoutSpecified = false; /// - /// delay parameter + /// Specify the For parameter. + /// Wait for the specific service before unblocking the prompt. /// - [Parameter] - [ValidateRange(1, 60)] - public Int32 Delay { get; set; } = 1; + [Parameter(ParameterSetName = DefaultParameterSet)] + public WaitForServiceTypes For + { + get + { + return _waitFor; + } + + set + { + _waitFor = value; + _waitForSpecified = true; + } + } + + private WaitForServiceTypes _waitFor = WaitForServiceTypes.PowerShell; + private bool _waitForSpecified = false; /// - /// quiet parameter + /// Specify the Delay parameter. + /// The specific time interval (in second) to wait between network pings or service queries. /// - [Parameter(ParameterSetName = QuietParameterSet)] - public SwitchParameter Quiet + [Parameter(ParameterSetName = DefaultParameterSet)] + [ValidateRange(1, short.MaxValue)] + public short Delay { - get { return quiet; } - set { quiet = value; } + get + { + return (short)_delay; + } + + set + { + _delay = value; + _delaySpecified = true; + } } - private bool quiet = false; -#endregion "parameters" -#region "Overrides" + private int _delay = 5; + private bool _delaySpecified = false; + + /// + /// Script to test if the PowerShell is ready. + /// + private const string TestPowershellScript = @" +$array = @($input) +$result = @{} +foreach ($computerName in $array[1]) +{ + $ret = $null + $arguments = @{ + ComputerName = $computerName + ScriptBlock = { $true } + + SessionOption = New-PSSessionOption -NoMachineProfile + ErrorAction = 'SilentlyContinue' + } -#if !CORECLR - ///// - ///// To Store the output for each ping reply - ///// - private ManagementObjectSearcher searcher; + if ( $null -ne $array[0] ) + { + $arguments['Credential'] = $array[0] + } + + $result[$computerName] = (Invoke-Command @arguments) -as [bool] +} +$result +"; -#endif - private TransportProtocol _transportProtocol = TransportProtocol.DCOM; - private readonly CancellationTokenSource cancel = new CancellationTokenSource(); - private Dictionary quietResults = new Dictionary(); /// - /// To begin processing Test-connection + /// The indicator to use when show progress. /// - protected override void BeginProcessing() - { - base.BeginProcessing(); - // Verify parameter set + private readonly string[] _indicator = { "|", "/", "-", "\\" }; - bool haveProtocolParam = this.MyInvocation.BoundParameters.ContainsKey("Protocol"); - bool haveWsmanAuthenticationParam = this.MyInvocation.BoundParameters.ContainsKey("WsmanAuthentication"); - bool haveDcomAuthenticationParam = this.MyInvocation.BoundParameters.ContainsKey("DcomAuthentication"); - bool haveDcomImpersonation = this.MyInvocation.BoundParameters.ContainsKey("Impersonation"); - _transportProtocol = (this.Protocol.Equals(ComputerWMIHelper.WsmanProtocol, StringComparison.OrdinalIgnoreCase) || (haveWsmanAuthenticationParam && !haveProtocolParam)) ? - TransportProtocol.WSMan : TransportProtocol.DCOM; + /// + /// The activity id. + /// + private int _activityId; - if (haveWsmanAuthenticationParam && (haveDcomAuthenticationParam || haveDcomImpersonation)) - { - string errMsg = StringUtil.Format(ComputerResources.StopCommandParamWSManAuthConflict, ComputerResources.StopCommandParamMessage); - ThrowTerminatingError( - new ErrorRecord( - new PSArgumentException(errMsg), - "InvalidParameter", - ErrorCategory.InvalidArgument, - this)); - } + /// + /// After call 'Shutdown' on the target computer, wait a few + /// seconds for the restart to begin. + /// + private const int SecondsToWaitForRestartToBegin = 25; - if ((_transportProtocol == TransportProtocol.DCOM) && haveWsmanAuthenticationParam) - { - string errMsg = StringUtil.Format(ComputerResources.StopCommandWSManAuthProtocolConflict, ComputerResources.StopCommandParamMessage); - ThrowTerminatingError( - new ErrorRecord( - new PSArgumentException(errMsg), - "InvalidParameter", - ErrorCategory.InvalidArgument, - this)); - } + /// + /// Actual time out in seconds. + /// + private int _timeoutInMilliseconds; - if ((_transportProtocol == TransportProtocol.WSMan) && (haveDcomAuthenticationParam || haveDcomImpersonation)) - { - string errMsg = StringUtil.Format(ComputerResources.StopCommandAuthProtocolConflict, ComputerResources.StopCommandParamMessage); - ThrowTerminatingError( - new ErrorRecord( - new PSArgumentException(errMsg), - "InvalidParameter", - ErrorCategory.InvalidArgument, - this)); - } + /// + /// Indicate to exit. + /// + private bool _exit, _timeUp; + private readonly CancellationTokenSource _cancel = new(); -#if CORECLR - if (this.MyInvocation.BoundParameters.ContainsKey("DcomAuthentication")) - { - string errMsg = StringUtil.Format(ComputerResources.InvalidParameterForCoreClr, "DcomAuthentication"); - PSArgumentException ex = new PSArgumentException(errMsg, ComputerResources.InvalidParameterForCoreClr); - ThrowTerminatingError(new ErrorRecord(ex, "InvalidParameterForCoreClr", ErrorCategory.InvalidArgument, null)); - } + /// + /// A waithandler to wait on. Current thread will wait on it during the delay interval. + /// + private readonly ManualResetEventSlim _waitHandler = new(false); + private readonly Dictionary _computerInfos = new(StringComparer.OrdinalIgnoreCase); - if (this.MyInvocation.BoundParameters.ContainsKey("Impersonation")) - { - string errMsg = StringUtil.Format(ComputerResources.InvalidParameterForCoreClr, "Impersonation"); - PSArgumentException ex = new PSArgumentException(errMsg, ComputerResources.InvalidParameterForCoreClr); - ThrowTerminatingError(new ErrorRecord(ex, "InvalidParameterForCoreClr", ErrorCategory.InvalidArgument, null)); - } + // CLR 4.0 Port note - use https://msdn.microsoft.com/library/system.net.networkinformation.ipglobalproperties.hostname(v=vs.110).aspx + private readonly string _shortLocalMachineName = Dns.GetHostName(); - if(this.Protocol.Equals(ComputerWMIHelper.DcomProtocol , StringComparison.OrdinalIgnoreCase)) - { - InvalidOperationException ex = new InvalidOperationException(ComputerResources.InvalidParameterDCOMNotSupported); - ThrowTerminatingError(new ErrorRecord(ex, "InvalidParameterDCOMNotSupported", ErrorCategory.InvalidOperation, null)); - } -#endif + // And for this, use PsUtils.GetHostname() + private readonly string _fullLocalMachineName = Dns.GetHostEntryAsync(string.Empty).Result.HostName; + private int _percent; + private string _status; + private string _activity; + private Timer _timer; + private System.Management.Automation.PowerShell _powershell; + + private const string StageVerification = "VerifyStage"; + private const string WmiConnectionTest = "WMI"; + private const string WinrmConnectionTest = "WinRM"; + private const string PowerShellConnectionTest = "PowerShell"; + + #endregion "parameters and PrivateData" + + #region "IDisposable Members" - //testing - } /// - /// Process Record + /// Dispose Method. /// - protected override void ProcessRecord() + public void Dispose() { - switch (_transportProtocol) - { -#if !CORECLR - case TransportProtocol.DCOM: - processDCOMProtocolForTestConnection(); - break; -#endif - - case TransportProtocol.WSMan: - ProcessWSManProtocolForTestConnection(); - break; - } + this.Dispose(true); + // Use SuppressFinalize in case a subclass + // of this type implements a finalizer. + GC.SuppressFinalize(this); } + /// - /// to implement ^C + /// Dispose Method. /// - protected override void StopProcessing() + /// + public void Dispose(bool disposing) { -#if !CORECLR - ManagementObjectSearcher stopSearcher = searcher; - if (stopSearcher != null) - { - try - { - stopSearcher.Dispose(); - } - catch (ObjectDisposedException) { } - } -#endif - try + if (disposing) { - cancel.Cancel(); + _timer?.Dispose(); + _waitHandler.Dispose(); + _cancel.Dispose(); + _powershell?.Dispose(); } - catch (ObjectDisposedException) { } - catch (AggregateException) { } } -#endregion -#region "Private Methods " - private string QueryString(string[] machinenames, bool escaperequired, bool selectrequired) - { - StringBuilder FilterString = new StringBuilder(); - if (selectrequired) - { - FilterString.Append("Select * from "); - FilterString.Append(ComputerWMIHelper.WMI_Class_PingStatus); - FilterString.Append(" where "); - } - FilterString.Append("(("); - for (int i = 0; i <= machinenames.Length - 1; i++) + #endregion "IDisposable Members" + + #region "Private Methods" + + /// + /// Validate parameters for 'DefaultSet' + /// 1. When the Wait is specified, the computername cannot contain the local machine + /// 2. If the local machine is present, make sure it is at the end of the list (so the remote ones get restarted before the local machine reboot). + /// + private void ValidateComputerNames() + { + bool containLocalhost = false; + _validatedComputerNames.Clear(); + + foreach (string name in ComputerName) { - FilterString.Append("Address='"); - string EscapeComp = machinenames[i].ToString(); - if (EscapeComp.Equals(".", StringComparison.CurrentCultureIgnoreCase)) - EscapeComp = "localhost"; - if (escaperequired) + ErrorRecord error = null; + string targetComputerName = ComputerWMIHelper.ValidateComputerName(name, _shortLocalMachineName, _fullLocalMachineName, ref error); + if (targetComputerName == null) { - EscapeComp = EscapeComp.Replace("\\", "\\\\'").ToString(); - EscapeComp = EscapeComp.Replace("'", "\\'").ToString(); + if (error != null) + { + WriteError(error); + } + + continue; + } + + if (targetComputerName.Equals(ComputerWMIHelper.localhostStr, StringComparison.OrdinalIgnoreCase)) + { + containLocalhost = true; } - FilterString.Append(EscapeComp.ToString()); - FilterString.Append("'"); - if (i < machinenames.Length - 1) + else if (!_uniqueComputerNames.Contains(targetComputerName)) { - FilterString.Append(" Or "); + _validatedComputerNames.Add(targetComputerName); + _uniqueComputerNames.Add(targetComputerName); } } - FilterString.Append(")"); - FilterString.Append(" And "); - FilterString.Append("TimeToLive="); - FilterString.Append(TimeToLive); - FilterString.Append(" And "); - FilterString.Append("BufferSize="); - FilterString.Append(BufferSize); - FilterString.Append(" And "); - FilterString.Append("TimeOut="); - FilterString.Append(TimeOut); - FilterString.Append(")"); - return FilterString.ToString(); - } - private void ProcessPingStatus(Object pingStatusObj) - { - Dbg.Diagnostics.Assert(pingStatusObj != null, "Caller should verify that pingStatus != null"); - //Dbg.Diagnostics.Assert(pingStatusObj.ClassPath.ClassName.Equals("Win32_PingStatus"), "Caller should verify that pingStatus is a Win32_PingStatus object"); - string destinationAddress = null; - UInt32 primaryAddressResolutionStatus; - UInt32 statusCode; -#if !CORECLR - if (_transportProtocol == TransportProtocol.DCOM) + + // Force wait with a test hook even if we're on the local computer + if (!InternalTestHooks.TestWaitStopComputer && Wait && containLocalhost) { - ManagementBaseObject pingStatus = (ManagementBaseObject)pingStatusObj; - - destinationAddress = (string)LanguagePrimitives.ConvertTo( - pingStatus.GetPropertyValue("Address"), - typeof(string), - CultureInfo.InvariantCulture); - - primaryAddressResolutionStatus = (UInt32)LanguagePrimitives.ConvertTo( - pingStatus.GetPropertyValue("PrimaryAddressResolutionStatus"), - typeof(UInt32), - CultureInfo.InvariantCulture); - statusCode = (UInt32)LanguagePrimitives.ConvertTo( - pingStatus.GetPropertyValue("StatusCode"), - typeof(UInt32), - CultureInfo.InvariantCulture); + // The local machine will be ignored, and an error will be emitted. + InvalidOperationException ex = new(ComputerResources.CannotWaitLocalComputer); + WriteError(new ErrorRecord(ex, "CannotWaitLocalComputer", ErrorCategory.InvalidOperation, null)); + containLocalhost = false; } - else + + // Add the localhost to the end of the list, so we will restart remote machines + // before we restart the local one. + if (containLocalhost) { -#endif - CimInstance pingStatus = (CimInstance)pingStatusObj; - destinationAddress = (string)LanguagePrimitives.ConvertTo( - pingStatus.CimInstanceProperties["Address"].Value.ToString(), - typeof(string), - CultureInfo.InvariantCulture); - primaryAddressResolutionStatus = (UInt32)LanguagePrimitives.ConvertTo( - pingStatus.CimInstanceProperties["PrimaryAddressResolutionStatus"].Value, - typeof(UInt32), - CultureInfo.InvariantCulture); - statusCode = (UInt32)LanguagePrimitives.ConvertTo( - pingStatus.CimInstanceProperties["StatusCode"].Value, - typeof(UInt32), - CultureInfo.InvariantCulture); - -#if !CORECLR + _validatedComputerNames.Add(ComputerWMIHelper.localhostStr); } -#endif - if (primaryAddressResolutionStatus != 0) + } + + /// + /// Write out progress. + /// + /// + /// + /// + /// + private void WriteProgress(string activity, string status, int percent, ProgressRecordType progressRecordType) + { + ProgressRecord progress = new(_activityId, activity, status); + progress.PercentComplete = percent; + progress.RecordType = progressRecordType; + WriteProgress(progress); + } + + /// + /// Calculate the progress percentage. + /// + /// + /// + private int CalculateProgressPercentage(string currentStage) + { + switch (currentStage) { - if (!quiet) - { - Win32Exception win32Exception = new Win32Exception(unchecked((int)primaryAddressResolutionStatus)); - string message = StringUtil.Format(ComputerResources.NoPingResult, destinationAddress, win32Exception.Message); - Exception pingException = new System.Net.NetworkInformation.PingException(message, win32Exception); - ErrorRecord errorRecord = new ErrorRecord(pingException, "TestConnectionException", ErrorCategory.ResourceUnavailable, destinationAddress); - WriteError(errorRecord); - } + case StageVerification: + return _waitFor.Equals(WaitForServiceTypes.Wmi) || _waitFor.Equals(WaitForServiceTypes.WinRM) + ? 33 + : 20; + case WmiConnectionTest: + return _waitFor.Equals(WaitForServiceTypes.Wmi) ? 66 : 40; + case WinrmConnectionTest: + return _waitFor.Equals(WaitForServiceTypes.WinRM) ? 66 : 60; + case PowerShellConnectionTest: + return 80; + default: + break; } - else + + Dbg.Diagnostics.Assert(false, "CalculateProgressPercentage should never hit the default case"); + return 0; + } + + /// + /// Event handler for the timer. + /// + /// + private void OnTimedEvent(object s) + { + _exit = _timeUp = true; + _cancel.Cancel(); + _waitHandler.Set(); + + if (_powershell != null) { - if (statusCode != 0) - { - if (!quiet) - { - Win32Exception win32Exception = new Win32Exception(unchecked((int)statusCode)); - string message = StringUtil.Format(ComputerResources.NoPingResult, destinationAddress, win32Exception.Message); - Exception pingException = new System.Net.NetworkInformation.PingException(message, win32Exception); - ErrorRecord errorRecord = new ErrorRecord(pingException, "TestConnectionException", ErrorCategory.ResourceUnavailable, destinationAddress); - WriteError(errorRecord); - } - } - else - { - this.quietResults[destinationAddress] = true; - if (!quiet) - { - WriteObject(pingStatusObj); - } - } + _powershell.Stop(); + _powershell.Dispose(); } } -#if !CORECLR - private void processDCOMProtocolForTestConnection() + private sealed class ComputerInfo + { + internal string LastBootUpTime; + internal bool RebootComplete; + } + + private List TestRestartStageUsingWsman(IEnumerable computerNames, List nextTestList, CancellationToken token) { - ConnectionOptions options = ComputerWMIHelper.GetConnectionOptions(DcomAuthentication, this.Impersonation, this.Credential); - if (AsJob) + var restartStageTestList = new List(); + var operationOptions = new CimOperationOptions { - string filter = QueryString(ComputerName, true, false); - GetWmiObjectCommand WMICmd = new GetWmiObjectCommand(); - WMICmd.Filter = filter.ToString(); - WMICmd.Class = ComputerWMIHelper.WMI_Class_PingStatus; - WMICmd.ComputerName = Source; - WMICmd.Authentication = DcomAuthentication; - WMICmd.Impersonation = Impersonation; - WMICmd.ThrottleLimit = throttlelimit; - PSWmiJob wmiJob = new PSWmiJob(WMICmd, Source, throttlelimit, this.MyInvocation.MyCommand.Name, Count); - this.JobRepository.Add(wmiJob); - WriteObject(wmiJob); - } - else + Timeout = TimeSpan.FromMilliseconds(2000), + CancellationToken = token + }; + foreach (var computer in computerNames) { - int sourceCount = 0; - foreach (string fromcomp in Source) + try { - try + if (token.IsCancellationRequested) { - sourceCount++; - EnumerationOptions enumOptions = new EnumerationOptions(); - enumOptions.UseAmendedQualifiers = true; - enumOptions.DirectRead = true; + break; + } - int destCount = 0; - foreach (var tocomp in ComputerName) + using (CimSession cimSession = RemoteDiscoveryHelper.CreateCimSession(computer, Credential, WsmanAuthentication, isLocalHost: false, this, token)) + { + bool itemRetrieved = false; + IEnumerable mCollection = cimSession.QueryInstances( + ComputerWMIHelper.CimOperatingSystemNamespace, + ComputerWMIHelper.CimQueryDialect, + "Select * from " + ComputerWMIHelper.WMI_Class_OperatingSystem, + operationOptions); + foreach (CimInstance os in mCollection) { - destCount++; - string querystring = QueryString(new string[] { tocomp }, true, true); - ObjectQuery query = new ObjectQuery(querystring); - ManagementScope scope = new ManagementScope(ComputerWMIHelper.GetScopeString(fromcomp, ComputerWMIHelper.WMI_Path_CIM), options); - scope.Options.EnablePrivileges = true; - scope.Connect(); - - using (searcher = new ManagementObjectSearcher(scope, query, enumOptions)) - { - for (int j = 0; j <= Count - 1; j++) - { - using (ManagementObjectCollection mobj = searcher.Get()) - { - int mobjCount = 0; - foreach (ManagementBaseObject obj in mobj) - { - using (obj) - { - mobjCount++; - - ProcessPingStatus(obj); + itemRetrieved = true; + string newLastBootUpTime = os.CimInstanceProperties["LastBootUpTime"].Value.ToString(); + string oldLastBootUpTime = _computerInfos[computer].LastBootUpTime; - // to delay the request, if case to avoid the delay for the last pingrequest - if (mobjCount < mobj.Count || j < Count - 1 || sourceCount < Source.Length || destCount < ComputerName.Length) - Thread.Sleep(Delay * 1000); - } - } - } - } + if (!string.Equals(newLastBootUpTime, oldLastBootUpTime, StringComparison.OrdinalIgnoreCase)) + { + _computerInfos[computer].RebootComplete = true; + nextTestList.Add(computer); + } + else + { + restartStageTestList.Add(computer); } } - searcher = null; - } - catch (ManagementException e) - { - ErrorRecord errorRecord = new ErrorRecord(e, "TestConnectionException", ErrorCategory.InvalidOperation, null); - WriteError(errorRecord); - continue; - } - catch (System.Runtime.InteropServices.COMException e) - { - ErrorRecord errorRecord = new ErrorRecord(e, "TestConnectionException", ErrorCategory.InvalidOperation, null); - WriteError(errorRecord); - continue; + + if (!itemRetrieved) + { + restartStageTestList.Add(computer); + } } } - } - - if (quiet) - { - foreach (string destinationAddress in this.ComputerName) + catch (CimException) + { + restartStageTestList.Add(computer); + } + catch (Exception) { - bool destinationResult = false; - this.quietResults.TryGetValue(destinationAddress, out destinationResult); - WriteObject(destinationResult); + restartStageTestList.Add(computer); } } + + return restartStageTestList; } -#endif - private void ProcessWSManProtocolForTestConnection() - { - if (AsJob) - { - // TODO: Need job for MI.Net WSMan protocol - // Early return of job object. - throw new PSNotSupportedException(); - } + private List SetUpComputerInfoUsingWsman(IEnumerable computerNames, CancellationToken token) + { + var validComputerNameList = new List(); var operationOptions = new CimOperationOptions { Timeout = TimeSpan.FromMilliseconds(2000), - CancellationToken = cancel.Token + CancellationToken = token }; - int destCount = 0; - int sourceCount = 0; - foreach (string sourceComp in Source) + foreach (var computer in computerNames) { try { - sourceCount++; - string sourceMachine; - if ((sourceComp.Equals("localhost", StringComparison.CurrentCultureIgnoreCase)) || (sourceComp.Equals(".", StringComparison.OrdinalIgnoreCase))) - { - sourceMachine = Dns.GetHostName(); - } - else - { - sourceMachine = sourceComp; - } - foreach (var tocomp in ComputerName) + using (CimSession cimSession = RemoteDiscoveryHelper.CreateCimSession(computer, Credential, WsmanAuthentication, isLocalHost: false, this, token)) { - destCount++; - string querystring = QueryString(new string[] { tocomp }, true, true); - - using (CimSession cimSession = RemoteDiscoveryHelper.CreateCimSession(sourceComp, this.Credential, WsmanAuthentication, cancel.Token, this)) + bool itemRetrieved = false; + IEnumerable mCollection = cimSession.QueryInstances( + ComputerWMIHelper.CimOperatingSystemNamespace, + ComputerWMIHelper.CimQueryDialect, + "Select * from " + ComputerWMIHelper.WMI_Class_OperatingSystem, + operationOptions); + foreach (CimInstance os in mCollection) { - WriteVerbose(String.Format("WMI query {0} sent to {1}", querystring, sourceComp)); - for (int echoRequestCount = 0; echoRequestCount < Count; echoRequestCount++) + itemRetrieved = true; + if (!_computerInfos.ContainsKey(computer)) { - IEnumerable mCollection = cimSession.QueryInstances( - ComputerWMIHelper.CimOperatingSystemNamespace, - ComputerWMIHelper.CimQueryDialect, - querystring, - operationOptions); - int total = mCollection.ToList().Count; - int cimInsCount = 1; - foreach (CimInstance obj in mCollection) + var info = new ComputerInfo { - ProcessPingStatus(obj); - cimInsCount++; - // to delay the request, if case to avoid the delay for the last pingrequest - if (cimInsCount < total || echoRequestCount < Count - 1 || sourceCount < Source.Length || destCount < ComputerName.Length) - Thread.Sleep(Delay * 1000); - } + LastBootUpTime = os.CimInstanceProperties["LastBootUpTime"].Value.ToString(), + RebootComplete = false + }; + _computerInfos.Add(computer, info); + validComputerNameList.Add(computer); } } + + if (!itemRetrieved) + { + string errMsg = StringUtil.Format(ComputerResources.RestartComputerSkipped, computer, ComputerResources.CannotGetOperatingSystemObject); + var error = new ErrorRecord(new InvalidOperationException(errMsg), "RestartComputerSkipped", + ErrorCategory.OperationStopped, computer); + this.WriteError(error); + } } } catch (CimException ex) { - ErrorRecord errorRecord = new ErrorRecord(ex, "TestConnectionException", ErrorCategory.InvalidOperation, null); - WriteError(errorRecord); - continue; + string errMsg = StringUtil.Format(ComputerResources.RestartComputerSkipped, computer, ex.Message); + var error = new ErrorRecord(new InvalidOperationException(errMsg), "RestartComputerSkipped", + ErrorCategory.OperationStopped, computer); + this.WriteError(error); } - catch (System.Runtime.InteropServices.COMException e) + catch (Exception ex) { - ErrorRecord errorRecord = new ErrorRecord(e, "TestConnectionException", ErrorCategory.InvalidOperation, null); - WriteError(errorRecord); - continue; + string errMsg = StringUtil.Format(ComputerResources.RestartComputerSkipped, computer, ex.Message); + var error = new ErrorRecord(new InvalidOperationException(errMsg), "RestartComputerSkipped", + ErrorCategory.OperationStopped, computer); + this.WriteError(error); } } - if (quiet) + return validComputerNameList; + } + + private void WriteOutTimeoutError(IEnumerable computerNames) + { + const string errorId = "RestartComputerTimeout"; + foreach (string computer in computerNames) { - foreach (string destinationAddress in this.ComputerName) + string errorMsg = StringUtil.Format(ComputerResources.RestartcomputerFailed, computer, ComputerResources.TimeoutError); + var exception = new RestartComputerTimeoutException(computer, Timeout, errorMsg, errorId); + var error = new ErrorRecord(exception, errorId, ErrorCategory.OperationTimeout, computer); + if (!InternalTestHooks.TestWaitStopComputer) { - bool destinationResult = false; - this.quietResults.TryGetValue(destinationAddress, out destinationResult); - WriteObject(destinationResult); + WriteError(error); } } } -#endregion "Private Methods " - } -#endregion Test-Connection -#if !CORECLR - -#region Enable-ComputerRestore - - /// - /// Cmdlet for Enable-ComputerRestore - /// - [Cmdlet(VerbsLifecycle.Enable, "ComputerRestore", SupportsShouldProcess = true, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=135209")] - public sealed class EnableComputerRestoreCommand : PSCmdlet, IDisposable - { -#region Parameters - /// - /// Specifies the Drive on which the system restore will be enabled. - /// The drive string should be of the form "C:\". - /// - [Parameter(Position = 0, Mandatory = true)] - [ValidateNotNullOrEmpty] - [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] - public string[] Drive { get; set; } + #endregion "Private Methods" - #endregion Parameters - private const string ErrorBase = "ComputerResources"; - private ManagementClass WMIClass; -#region "IDisposable Members" + #region "Internal Methods" - /// - /// Dispose Method - /// - public void Dispose() + internal static List TestWmiConnectionUsingWsman(List computerNames, List nextTestList, PSCredential credential, string wsmanAuthentication, PSCmdlet cmdlet, CancellationToken token) { - this.Dispose(true); - // Use SuppressFinalize in case a subclass - // of this type implements a finalizer. - GC.SuppressFinalize(this); - } - - /// - /// Dispose Method. - /// - /// - public void Dispose(bool disposing) - { - if (disposing) - { - if (WMIClass != null) - { - WMIClass.Dispose(); - } - } - } - -#endregion "IDisposable Members" - -#region Overrides - - /// - /// To Enable the Restore Point of the drives - /// - protected override void BeginProcessing() - { - // system restore APIs are not supported on ARM platform - if (ComputerWMIHelper.SkipSystemRestoreOperationForARMPlatform(this)) + // Check if the WMI service "Winmgmt" is started + const string wmiServiceQuery = "Select * from " + ComputerWMIHelper.WMI_Class_Service + " Where name = 'Winmgmt'"; + var wmiTestList = new List(); + var operationOptions = new CimOperationOptions { - return; - } - - ManagementScope scope = new ManagementScope(ComputerWMIHelper.WMI_Path_Default); - scope.Connect(); - WMIClass = new ManagementClass(ComputerWMIHelper.WMI_Class_SystemRestore); - WMIClass.Scope = scope; - int retValue; - //get the system drive - string sysdrive = System.Environment.ExpandEnvironmentVariables("%SystemDrive%"); - sysdrive = String.Concat(new string[] { sysdrive, "\\" }); - - - if (ComputerWMIHelper.ContainsSystemDrive(Drive, sysdrive)) + Timeout = TimeSpan.FromMilliseconds(2000), + CancellationToken = token + }; + foreach (var computer in computerNames) { - object[] input = { sysdrive }; try { - retValue = Convert.ToInt32(WMIClass.InvokeMethod("Enable", input), System.Globalization.CultureInfo.CurrentCulture); - //if success (return value is 0 or if already enabled (error code is 1056 in XP and 0 in vista) - if ((retValue.Equals(0)) || (retValue.Equals(ComputerWMIHelper.ErrorCode_Service))) + if (token.IsCancellationRequested) + { + break; + } + + using (CimSession cimSession = RemoteDiscoveryHelper.CreateCimSession(computer, credential, wsmanAuthentication, isLocalHost: false, cmdlet, token)) { - string driveNew; - foreach (string drive in Drive) //for each input drive + bool itemRetrieved = false; + IEnumerable mCollection = cimSession.QueryInstances( + ComputerWMIHelper.CimOperatingSystemNamespace, + ComputerWMIHelper.CimQueryDialect, + wmiServiceQuery, + operationOptions); + foreach (CimInstance service in mCollection) { - if (!ShouldProcess(drive)) - { - continue; - } - if (!drive.EndsWith("\\", StringComparison.CurrentCultureIgnoreCase)) + itemRetrieved = true; + if (LanguagePrimitives.IsTrue(service.CimInstanceProperties["Started"].Value)) { - driveNew = String.Concat(drive, "\\"); + nextTestList.Add(computer); } else - driveNew = drive; - if (!ComputerWMIHelper.IsValidDrive(driveNew))//if not valid drive,throw error { - Exception Ex = new ArgumentException(StringUtil.Format(ComputerResources.InvalidDrive, drive)); - WriteError(new ErrorRecord(Ex, "EnableComputerRestoreInvalidDrive", ErrorCategory.InvalidData, null)); - continue; + wmiTestList.Add(computer); } - //parameter for Enable method - //if the input drive is not system drive - if (!driveNew.Equals(sysdrive, StringComparison.OrdinalIgnoreCase)) - { - object[] inputDrive = { driveNew }; - retValue = Convert.ToInt32(WMIClass.InvokeMethod("Enable", inputDrive), System.Globalization.CultureInfo.CurrentCulture); + } - //if not enabled, retry again - if (retValue.Equals(ComputerWMIHelper.ErrorCode_Interface)) - { - retValue = Convert.ToInt32(WMIClass.InvokeMethod("Enable", inputDrive), System.Globalization.CultureInfo.CurrentCulture); - } - } - //if not success and if it is not already enabled (error code is 1056 in XP) - // Error 1717 - The interface is unknown. Even though this comes sometimes . The Drive is getting enabled. - if (!(retValue.Equals(0)) && !(retValue.Equals(ComputerWMIHelper.ErrorCode_Service)) && !(retValue.Equals(ComputerWMIHelper.ErrorCode_Interface))) - { - Exception Ex = new ArgumentException(StringUtil.Format(ComputerResources.NotEnabled, drive)); - WriteError(new ErrorRecord(Ex, "EnableComputerRestoreNotEnabled", ErrorCategory.InvalidOperation, null)); - continue; - } + if (!itemRetrieved) + { + wmiTestList.Add(computer); } } - else - { - ArgumentException Ex = new ArgumentException(StringUtil.Format(ComputerResources.NotEnabled, sysdrive)); - WriteError(new ErrorRecord(Ex, "EnableComputerRestoreNotEnabled", ErrorCategory.InvalidOperation, null)); - } } - catch (ManagementException e) + catch (CimException) { - if ((e.ErrorCode.Equals(ManagementStatus.NotFound)) || (e.ErrorCode.Equals(ManagementStatus.InvalidClass))) - { - ErrorRecord er = new ErrorRecord(new ArgumentException(StringUtil.Format(ComputerResources.NotSupported)), null, ErrorCategory.InvalidOperation, null); - WriteError(er); - } - else - { - ErrorRecord errorRecord = new ErrorRecord(e, "GetWMIManagementException", ErrorCategory.InvalidOperation, null); - WriteError(errorRecord); - } + wmiTestList.Add(computer); } - catch (COMException e) + catch (Exception) { - if (string.IsNullOrEmpty(e.Message)) - { - Exception Ex = new ArgumentException(StringUtil.Format(ComputerResources.SystemRestoreServiceDisabled)); - WriteError(new ErrorRecord(Ex, "ServiceDisabled", ErrorCategory.InvalidOperation, null)); - } - else - { - ErrorRecord errorRecord = new ErrorRecord(e, "COMException", ErrorCategory.InvalidOperation, null); - WriteError(errorRecord); - } + wmiTestList.Add(computer); } } - else - { - ArgumentException Ex = new ArgumentException(StringUtil.Format(ComputerResources.NoSystemDrive)); - WriteError(new ErrorRecord(Ex, "EnableComputerNoSystemDrive", ErrorCategory.InvalidArgument, null)); - } - }//end of BeginProcessing - /// - /// to implement ^C - /// - protected override void StopProcessing() - { - if (WMIClass != null) - { - WMIClass.Dispose(); - } + return wmiTestList; } -#endregion Overrides - }//end of class -#endregion - -#region Disable-ComputerRestore - - /// - /// This cmdlet is to Disable Computer Restore points. - /// - [Cmdlet(VerbsLifecycle.Disable, "ComputerRestore", SupportsShouldProcess = true, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=135207")] - public sealed class DisableComputerRestoreCommand : PSCmdlet, IDisposable - { -#region Parameters - /// - /// Specifies the Drive on which the system restore will be enabled. - /// The drive string should be of the form "C:\". - /// - [Parameter(Position = 0, Mandatory = true)] - [ValidateNotNullOrEmpty] - [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] - public string[] Drive { get; set; } - - #endregion Parameters - - private ManagementClass WMIClass; - private const string ErrorBase = "ComputerResources"; -#region "IDisposable Members" - /// - /// Dispose Method + /// Test the PowerShell state for the restarting computer. /// - public void Dispose() + /// + /// + /// + /// + /// + internal static List TestPowerShell(List computerNames, List nextTestList, System.Management.Automation.PowerShell powershell, PSCredential credential) { - this.Dispose(true); - // Use SuppressFinalize in case a subclass - // of this type implements a finalizer. - GC.SuppressFinalize(this); - } + List psList = new(); - /// - /// Dispose Method. - /// - /// - public void Dispose(bool disposing) - { - if (disposing) + try { - if (WMIClass != null) + Collection psObjectCollection = powershell.Invoke(new object[] { credential, computerNames.ToArray() }); + if (psObjectCollection == null) { - WMIClass.Dispose(); + Dbg.Diagnostics.Assert(false, "This should never happen. Invoke should never return null."); } - } - } - -#endregion "IDisposable Members" - -#region Overrides - - /// - /// To Disable the Restore Point of the drives - /// - protected override void BeginProcessing() - { - // system restore APIs are not supported on ARM platform - if (ComputerWMIHelper.SkipSystemRestoreOperationForARMPlatform(this)) - { - return; - } - ManagementScope scope = new ManagementScope(ComputerWMIHelper.WMI_Path_Default); - scope.Connect(); - WMIClass = new ManagementClass(ComputerWMIHelper.WMI_Class_SystemRestore); - WMIClass.Scope = scope; - string driveNew; - foreach (string drive in Drive) - { - if (!ShouldProcess(drive)) + // If ^C or timeout happens when we are in powershell.Invoke(), the psObjectCollection might be empty + if (psObjectCollection.Count == 0) { - continue; + return computerNames; } - if (!drive.EndsWith("\\", StringComparison.CurrentCultureIgnoreCase)) - { - driveNew = String.Concat(drive, "\\"); - } - else - driveNew = drive; + object result = PSObject.Base(psObjectCollection[0]); + Hashtable data = result as Hashtable; + Dbg.Diagnostics.Assert(data != null, "data should never be null"); + Dbg.Diagnostics.Assert(data.Count == computerNames.Count, "data should contain results for all computers in computerNames"); - if (!ComputerWMIHelper.IsValidDrive(driveNew)) - { - ErrorRecord er = new ErrorRecord(new ArgumentException(StringUtil.Format(ComputerResources.NotValidDrive, drive)), null, ErrorCategory.InvalidData, null); - WriteError(er); - continue; - } - else + foreach (string computer in computerNames) { - try - { - object[] input = { driveNew }; - int retValue = Convert.ToInt32(WMIClass.InvokeMethod("Disable", input), System.Globalization.CultureInfo.CurrentCulture); - // Error 1717 - The interface is unknown. Even though this comes sometimes . The Drive is getting disabled. - if (!(retValue.Equals(0)) && !(retValue.Equals(ComputerWMIHelper.ErrorCode_Interface))) - { - ErrorRecord er = new ErrorRecord(new ArgumentException(StringUtil.Format(ComputerResources.NotDisabled, drive)), null, ErrorCategory.InvalidOperation, null); - WriteError(er); - continue; - } - } - catch (ManagementException e) + if (LanguagePrimitives.IsTrue(data[computer])) { - if ((e.ErrorCode.Equals(ManagementStatus.NotFound)) || (e.ErrorCode.Equals(ManagementStatus.InvalidClass))) - { - ErrorRecord er = new ErrorRecord(new ArgumentException(StringUtil.Format(ComputerResources.NotSupported)), null, ErrorCategory.InvalidOperation, null); - WriteError(er); - } - else - { - ErrorRecord errorRecord = new ErrorRecord(e, "GetWMIManagementException", ErrorCategory.InvalidOperation, null); - WriteError(errorRecord); - } + nextTestList.Add(computer); } - catch (COMException e) + else { - if (string.IsNullOrEmpty(e.Message)) - { - Exception Ex = new ArgumentException(StringUtil.Format(ComputerResources.SystemRestoreServiceDisabled)); - WriteError(new ErrorRecord(Ex, "ServiceDisabled", ErrorCategory.InvalidOperation, null)); - } - else - { - ErrorRecord errorRecord = new ErrorRecord(e, "COMException", ErrorCategory.InvalidOperation, null); - WriteError(errorRecord); - } + psList.Add(computer); } } } - } - - /// - /// to implement ^C - /// - protected override void StopProcessing() - { - if (WMIClass != null) + catch (PipelineStoppedException) { - WMIClass.Dispose(); + // powershell.Stop() is invoked because timeout expires, or Ctrl+C is pressed + } + catch (ObjectDisposedException) + { + // powershell.dispose() is invoked because timeout expires, or Ctrl+C is pressed } - } - -#endregion Overrides - }//end of class -#endregion Disable-ComputerRestore - -#region Checkpoint-Computer - - /// - /// Creates the Restore Point for the Local computer - /// - [Cmdlet(VerbsData.Checkpoint, "Computer", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=135197")] - public class CheckpointComputerCommand : PSCmdlet, IDisposable - { -#region Parameters + return psList; + } - /// - /// The description to be displayed so the user can easily identify a restore point. - /// - [Parameter(Position = 0, Mandatory = true)] - [ValidateNotNullOrEmpty] - public string Description { get; set; } + #endregion "Internal Methods" + #region "Overrides" /// - /// The type of restore point. + /// BeginProcessing method. /// - [Parameter(Position = 1)] - [Alias("RPT")] - [ValidateSetAttribute(new string[] { "APPLICATION_INSTALL", "APPLICATION_UNINSTALL", "DEVICE_DRIVER_INSTALL", "MODIFY_SETTINGS", "CANCELLED_OPERATION" })] - [ValidateNotNullOrEmpty] - public string RestorePointType + protected override void BeginProcessing() { - get { return _restorepointtype; } - set + // Timeout, For, Delay, Progress cannot be present if Wait is not present + if ((_timeoutSpecified || _waitForSpecified || _delaySpecified) && !Wait) + { + InvalidOperationException ex = new(ComputerResources.RestartComputerInvalidParameter); + ThrowTerminatingError(new ErrorRecord(ex, "RestartComputerInvalidParameter", ErrorCategory.InvalidOperation, null)); + } + + if (Wait) { - // null check is not needed (because of ValidateNotNullOrEmpty), - // but we have to include it to silence OACR - if (value == null) + _activityId = Random.Shared.Next(); + if (_timeout == -1 || _timeout >= int.MaxValue / 1000) + { + _timeoutInMilliseconds = int.MaxValue; + } + else { - throw PSTraceSource.NewArgumentNullException("value"); + _timeoutInMilliseconds = _timeout * 1000; } - _restorepointtype = value; - if (_restorepointtype.Equals("APPLICATION_INSTALL", StringComparison.OrdinalIgnoreCase)) - intRestorePoint = 0; - else if (_restorepointtype.Equals("APPLICATION_UNINSTALL", StringComparison.OrdinalIgnoreCase)) - intRestorePoint = 1; - else if (_restorepointtype.Equals("DEVICE_DRIVER_INSTALL", StringComparison.OrdinalIgnoreCase)) - intRestorePoint = 10; - else if (_restorepointtype.Equals("MODIFY_SETTINGS", StringComparison.OrdinalIgnoreCase)) - intRestorePoint = 12; - else if (_restorepointtype.Equals("CANCELLED_OPERATION", StringComparison.OrdinalIgnoreCase)) - intRestorePoint = 13; + // We don't support combined service types for now + switch (_waitFor) + { + case WaitForServiceTypes.Wmi: + case WaitForServiceTypes.WinRM: + break; + case WaitForServiceTypes.PowerShell: + _powershell = System.Management.Automation.PowerShell.Create(); + _powershell.AddScript(TestPowershellScript); + break; + default: + InvalidOperationException ex = new(ComputerResources.NoSupportForCombinedServiceType); + ErrorRecord error = new(ex, "NoSupportForCombinedServiceType", ErrorCategory.InvalidOperation, (int)_waitFor); + ThrowTerminatingError(error); + break; + } } } - private string _restorepointtype = "APPLICATION_INSTALL"; -#endregion Parameters - -#region private - private DateTime lastTimeProgressWasWritten = DateTime.UtcNow; - private int intRestorePoint = 0; - private int ret = int.MaxValue; /// - /// Shared Exception. Used when exception thrown from the Restore point thread. + /// ProcessRecord method. /// - private static Exception exceptionfromnewthread = null; - - private void WriteProgress(string statusDescription, int? percentComplete) + [SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling")] + protected override void ProcessRecord() { - ProgressRecordType recordType; - if (percentComplete.HasValue && percentComplete.Value == 100) - { - recordType = ProgressRecordType.Completed; - } - else + // Validate parameters + ValidateComputerNames(); + + object[] flags = new object[] { 2, 0 }; + if (Force) { - recordType = ProgressRecordType.Processing; + flags[0] = forcedReboot; } - if (recordType == ProgressRecordType.Processing) + if (ParameterSetName.Equals(DefaultParameterSet, StringComparison.OrdinalIgnoreCase)) { - TimeSpan timeSinceProgressWasWrittenLast = DateTime.UtcNow - lastTimeProgressWasWritten; - if (timeSinceProgressWasWrittenLast < TimeSpan.FromMilliseconds(200)) + if (Wait && _timeout != 0) { - return; + _validatedComputerNames = + SetUpComputerInfoUsingWsman(_validatedComputerNames, _cancel.Token); } - } - lastTimeProgressWasWritten = DateTime.UtcNow; - string activityDescription = StringUtil.Format(ComputerResources.ProgressActivity); - ProgressRecord progressRecord = new ProgressRecord( - 1905347723, // unique id - activityDescription, - statusDescription); + foreach (string computer in _validatedComputerNames) + { + bool isLocal = false; + string compname; - if (percentComplete.HasValue) - { - progressRecord.PercentComplete = percentComplete.Value; - } + if (computer.Equals("localhost", StringComparison.OrdinalIgnoreCase)) + { + compname = _shortLocalMachineName; + isLocal = true; + } + else + { + compname = computer; + } - progressRecord.RecordType = recordType; - this.WriteProgress(progressRecord); - } - - private void WriteProgress(DateTime starttime) - { - int percentageCompleted = ProgressRecord.GetPercentageComplete(starttime, TimeSpan.FromSeconds(90)); - if (percentageCompleted < 100) - { - WriteProgress(StringUtil.Format(ComputerResources.ProgressStatusCreatingRestorePoint, percentageCompleted), percentageCompleted); - } - } + // Generate target and action strings + string action = + StringUtil.Format( + ComputerResources.RestartComputerAction, + isLocal ? ComputerResources.LocalShutdownPrivilege : ComputerResources.RemoteShutdownPrivilege); + string target = + isLocal ? StringUtil.Format(ComputerResources.DoubleComputerName, "localhost", compname) : compname; - private DateTime startUtcTime, startLocalTime; - private void WriteProgress() - { - while (true) - { - WriteProgress(this.startUtcTime); - System.Threading.Thread.Sleep(1000); - if (exceptionfromnewthread == null) - { - if (ret == 0) + if (!ShouldProcess(target, action)) { - if (this.IsRestorePointCreated(Description, this.startLocalTime)) - { - break; - } + continue; } - else if (ret != int.MaxValue) + + bool isSuccess = + ComputerWMIHelper.InvokeWin32ShutdownUsingWsman(this, isLocal, compname, flags, Credential, WsmanAuthentication, ComputerResources.RestartcomputerFailed, "RestartcomputerFailed", _cancel.Token); + + if (isSuccess && Wait && _timeout != 0) { - // Invocation is complete with error - break; + _waitOnComputers.Add(computer); } } - else - { - // Exception is thrown, breaking the loop - break; - } - } - WriteProgress(StringUtil.Format(ComputerResources.ProgressStatusCompleted), 100); - } - - private void CreateRestorePoint() - { - ManagementClass WMIClass = null; - try - { - ManagementScope scope = new ManagementScope(ComputerWMIHelper.WMI_Path_Default); - scope.Connect(); - WMIClass = new ManagementClass(ComputerWMIHelper.WMI_Class_SystemRestore); - WMIClass.Scope = scope; - - //create restore point - ManagementBaseObject inParams = WMIClass.GetMethodParameters("CreateRestorePoint"); - object[] param = { Description, intRestorePoint, 100 }; // the event type will be always 100,Begin_System_Change - ret = Convert.ToInt32(WMIClass.InvokeMethod("CreateRestorePoint", param), System.Globalization.CultureInfo.CurrentCulture); - } - catch (Exception ex) - { - // We catch all exceptions because we don't want the exception to be thrown from a separate worker thread - exceptionfromnewthread = ex; - } - finally - { - if (WMIClass != null) + if (_waitOnComputers.Count > 0) { - WMIClass.Dispose(); - } - } - } - -#endregion - -#region "IDisposable Members" - - /// - /// Dispose Method - /// - public void Dispose() - { - // Use SuppressFinalize in case a subclass - // of this type implements a finalizer. - GC.SuppressFinalize(this); - } - -#endregion "IDisposable Members" - - /// - /// BeginProcessing method. - /// - protected override void BeginProcessing() - { - // system restore APIs are not supported on ARM platform - if (ComputerWMIHelper.SkipSystemRestoreOperationForARMPlatform(this)) - { - return; - } + var restartStageTestList = new List(_waitOnComputers); + var wmiTestList = new List(); + var winrmTestList = new List(); + var psTestList = new List(); + var allDoneList = new List(); - //Setting Exception from the new thread always to null - exceptionfromnewthread = null; + bool isForWmi = _waitFor.Equals(WaitForServiceTypes.Wmi); + bool isForWinRm = _waitFor.Equals(WaitForServiceTypes.WinRM); + bool isForPowershell = _waitFor.Equals(WaitForServiceTypes.PowerShell); - //on vista, CANCELLED_OPERATION restorepointtype does not work - if ((Environment.OSVersion.Version.Major >= 6) && (intRestorePoint == 13)) - { - ErrorRecord er = new ErrorRecord(new InvalidOperationException(StringUtil.Format(ComputerResources.NotSupported)), null, ErrorCategory.InvalidOperation, null); - ThrowTerminatingError(er); - } + int indicatorIndex = 0; + int machineCompleteRestart = 0; + int actualDelay = SecondsToWaitForRestartToBegin; + bool first = true; + bool waitComplete = false; - if (!CanCreateNewRestorePoint(DateTime.Now)) { return; } + _percent = 0; + _status = ComputerResources.WaitForRestartToBegin; + _activity = _waitOnComputers.Count == 1 ? + StringUtil.Format(ComputerResources.RestartSingleComputerActivity, _waitOnComputers[0]) : + ComputerResources.RestartMultipleComputersActivity; - this.startUtcTime = DateTime.UtcNow; - this.startLocalTime = DateTime.Now; - ThreadStart start = new ThreadStart(this.CreateRestorePoint); - Thread thread = new Thread(start); - thread.Start(); - WriteProgress(); - if (exceptionfromnewthread == null) - { - if (ret.Equals(1058)) - { - Exception Ex = new ArgumentException(StringUtil.Format(ComputerResources.ServiceDisabled)); - WriteError(new ErrorRecord(Ex, "CheckpointComputerServiceDisabled", ErrorCategory.InvalidOperation, null)); - } - else if (!(ret.Equals(0)) && !(ret.Equals(1058))) - { - Exception Ex = new ArgumentException(StringUtil.Format(ComputerResources.RestorePointNotCreated)); - WriteError(new ErrorRecord(Ex, "CheckpointComputerPointNotCreated", ErrorCategory.InvalidOperation, null)); - } - } - else - { - if (exceptionfromnewthread is System.Runtime.InteropServices.COMException) - { - if (string.IsNullOrEmpty(exceptionfromnewthread.Message)) - { - Exception e = new ArgumentException(StringUtil.Format(ComputerResources.SystemRestoreServiceDisabled)); - WriteError(new ErrorRecord(e, "ServiceDisabled", ErrorCategory.InvalidOperation, null)); - } - else - { - ErrorRecord errorRecord = new ErrorRecord(exceptionfromnewthread, "COMException", ErrorCategory.InvalidOperation, null); - WriteError(errorRecord); - } - } - else if (exceptionfromnewthread is ManagementException) - { - if (((ManagementException)exceptionfromnewthread).ErrorCode.Equals(ManagementStatus.NotFound) || ((ManagementException)exceptionfromnewthread).ErrorCode.Equals(ManagementStatus.InvalidClass)) - { - Exception e = new ArgumentException(StringUtil.Format(ComputerResources.NotSupported)); - WriteError(new ErrorRecord(e, "CheckpointComputerNotSupported", ErrorCategory.InvalidOperation, null)); - } - else - { - ErrorRecord errorRecord = new ErrorRecord(exceptionfromnewthread, "GetWMIManagementException", ErrorCategory.InvalidOperation, null); - WriteError(errorRecord); - } - } - else - { - // For other exception we caught from the worker thread, we throw it out here - throw exceptionfromnewthread; - } - } - }//End BeginProcessing() + _timer = new Timer(OnTimedEvent, null, _timeoutInMilliseconds, System.Threading.Timeout.Infinite); - private bool CanCreateNewRestorePoint(DateTime startTime) - { - const string srRegistryKeyPath = @"Software\Microsoft\Windows NT\CurrentVersion\SystemRestore"; - const string srFrequencyKeyName = "SystemRestorePointCreationFrequency"; - Version osVersion = Environment.OSVersion.Version; - int timeInterval = 1440; // Default value: 24 hours * 60 min/hr - bool canCreate = true; - - // From Win8+, the default frequency of restore point creation is 24 hours, but the user - // can create the DWORD value SystemRestorePointCreationFrequency under the registry key - // HKLM\Software\Microsoft\Windows NT\CurrentVersion\SystemRestore, and the value of it - // can change the frequency of restore point creation. There are three cases here: - // 1. Such registry key doesn't exist. We use the default setting: 24 hours. - // 2. Registry key value is 0. No frequency limitation, new restore point can be created anytime. - // 3. Registry key value is integer N, the time interval is N minutes. - if ((osVersion.Major > 6) || (osVersion.Major == 6 && osVersion.Minor >= 2)) - { - using (RegistryKey key = Registry.LocalMachine.OpenSubKey(srRegistryKeyPath)) - { - if (key != null) + while (true) { - object value = key.GetValue(srFrequencyKeyName); - if (value is int) { timeInterval = (int)value; } - } - } + // (delay * 1000)/250ms + int loopCount = actualDelay * 4; + while (loopCount > 0) + { + WriteProgress(_indicator[(indicatorIndex++) % 4] + _activity, _status, _percent, ProgressRecordType.Processing); - var objectQuery = new ObjectQuery { QueryString = "select * from " + ComputerWMIHelper.WMI_Class_SystemRestore }; - var scope = new ManagementScope(ComputerWMIHelper.WMI_Path_Default); - scope.Connect(); + loopCount--; + _waitHandler.Wait(250); + if (_exit) + { + break; + } + } - try - { - DateTime lastCreationTime = DateTime.MinValue; - using (var searcher = new ManagementObjectSearcher(scope, objectQuery)) - { - foreach (ManagementObject obj in searcher.Get()) + if (first) { - using (obj) + actualDelay = _delay; + first = false; + + if (_waitOnComputers.Count > 1) { - DateTime creationTime = - ManagementDateTimeConverter.ToDateTime(obj.Properties["CreationTime"].Value.ToString()); - if (creationTime > lastCreationTime) - { - lastCreationTime = creationTime; - } + _status = StringUtil.Format(ComputerResources.WaitForMultipleComputers, machineCompleteRestart, _waitOnComputers.Count); + WriteProgress(_indicator[(indicatorIndex++) % 4] + _activity, _status, _percent, ProgressRecordType.Processing); } } - } - TimeSpan span = startTime.Subtract(lastCreationTime); - canCreate = (span.TotalMinutes >= timeInterval); - } - catch (Exception ex) - { - // Fail to retrieve restore points - if (ex is ManagementException || ex is COMException) - { - // Something is wrong with System Restore (it may not be supported). We continue to let the - // call to "CreateRestorePoint" happen, and will handle the exception generated by that call. - canCreate = true; - } - else - { - // Not sure what happened, so we'd better terminate the execution and report the error. - string errorMsg = StringUtil.Format(ComputerResources.FailToRetrieveLastRestorePoint, ex.Message); - ThrowTerminatingError( - new ErrorRecord(new InvalidOperationException(errorMsg), - "FailToRetrieveLastRestorePoint", - ErrorCategory.InvalidOperation, null)); - } - } - } + do + { + // Test restart stage. + // We check if the target machine has already rebooted by querying the LastBootUpTime from the Win32_OperatingSystem object. + // So after this step, we are sure that both the Network and the WMI or WinRM service have already come up. + if (_exit) + { + break; + } - if (!canCreate) - { - // A new restore point cannot be created yet, so we write out warning message. - WriteWarning(StringUtil.Format(ComputerResources.CannotCreateRestorePointWarning, timeInterval)); - } + if (restartStageTestList.Count > 0) + { + if (_waitOnComputers.Count == 1) + { + _status = ComputerResources.VerifyRebootStage; + _percent = CalculateProgressPercentage(StageVerification); + WriteProgress(_indicator[(indicatorIndex++) % 4] + _activity, _status, _percent, ProgressRecordType.Processing); + } - return canCreate; - } + List nextTestList = (isForWmi || isForPowershell) ? wmiTestList : winrmTestList; + restartStageTestList = TestRestartStageUsingWsman(restartStageTestList, nextTestList, _cancel.Token); + } - private bool IsRestorePointCreated(string description, DateTime starttime) - { - bool foundrestorepoint = false; - ManagementScope scope = new ManagementScope(ComputerWMIHelper.WMI_Path_Default); - scope.Connect(); - - ObjectQuery objquery = new ObjectQuery(); - StringBuilder sb = new StringBuilder("select * from "); - sb.Append(ComputerWMIHelper.WMI_Class_SystemRestore); - sb.Append(" where description = '"); - sb.Append(description.Replace("'", "\\'")); - sb.Append("'"); - objquery.QueryString = sb.ToString(); + // Test WMI service + if (_exit) + { + break; + } - try - { - using (ManagementObjectSearcher searcher = new ManagementObjectSearcher(scope, objquery)) - { - if (searcher.Get().Count > 0) - { - foreach (ManagementObject obj in searcher.Get()) - { - using (obj) + if (wmiTestList.Count > 0) { - // we are adding 1 second to creationTime to account for the fact that milliseconds - // are not reported in CreationTime property - it is possible to have - // startTime = 2009-07-20 9:35:18.123 - // creationTime = 2009-07-20 9:35:18 - // which would indicate creationTime < startTime - DateTime creationTime = ManagementDateTimeConverter.ToDateTime(obj.Properties["CreationTime"].Value.ToString()); - if (creationTime.AddSeconds(1.0) >= starttime) + // This statement block executes for both CLRs. + // In the "full" CLR, it serves as the else case. { - foundrestorepoint = true; + if (_waitOnComputers.Count == 1) + { + _status = ComputerResources.WaitForWMI; + _percent = CalculateProgressPercentage(WmiConnectionTest); + WriteProgress(_indicator[(indicatorIndex++) % 4] + _activity, _status, _percent, ProgressRecordType.Processing); + } + + wmiTestList = TestWmiConnectionUsingWsman(wmiTestList, winrmTestList, Credential, WsmanAuthentication, this, _cancel.Token); } } - } - } - } - } - catch (ManagementException) - { - foundrestorepoint = true; - } - catch (COMException) - { - foundrestorepoint = true; - } - return foundrestorepoint; - } - } -#endregion -#region Get-ComputerRestorePoint + if (isForWmi) + { + break; + } - /// - /// This cmdlet is to Get Computer Restore points. - /// - [Cmdlet(VerbsCommon.Get, "ComputerRestorePoint", DefaultParameterSetName = "ID", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=135215")] - [OutputType(@"System.Management.ManagementObject#root\default\SystemRestore")] - public sealed class GetComputerRestorePointCommand : PSCmdlet, IDisposable - { -#region Parameters + // Test WinRM service + if (_exit) + { + break; + } - /// - /// This cmdlet is to get Computer Restore points. - /// - [Parameter(Position = 0, ParameterSetName = "ID")] - [ValidateNotNullOrEmpty] - [ValidateRangeAttribute((int)1, int.MaxValue)] - [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] - public int[] RestorePoint { get; set; } + if (winrmTestList.Count > 0) + { + // This statement block executes for both CLRs. + // In the "full" CLR, it serves as the else case. + { + // CIM-WSMan in use. In this case, restart stage checking is done by using WMIv2, + // so the WinRM service on the target machine is already up at this point. + psTestList.AddRange(winrmTestList); + winrmTestList.Clear(); - /// - /// This cmdlet is to get Computer Restore points. - /// - [Parameter(ParameterSetName = "LastStatus", Mandatory = true)] - [ValidateNotNull] - public SwitchParameter LastStatus { get; set; } + if (_waitOnComputers.Count == 1) + { + // This is to simulate the test for WinRM service + _status = ComputerResources.WaitForWinRM; + _percent = CalculateProgressPercentage(WinrmConnectionTest); - #endregion + loopCount = actualDelay * 4; // (delay * 1000)/250ms + while (loopCount > 0) + { + WriteProgress(_indicator[(indicatorIndex++) % 4] + _activity, _status, _percent, ProgressRecordType.Processing); - private ManagementClass WMIClass; + loopCount--; + _waitHandler.Wait(250); + if (_exit) + { + break; + } + } + } + } + } -#region "IDisposable Members" + if (isForWinRm) + { + break; + } - /// - /// Dispose Method - /// - public void Dispose() - { - this.Dispose(true); - // Use SuppressFinalize in case a subclass - // of this type implements a finalizer. - GC.SuppressFinalize(this); - } - - /// - /// Dispose Method. - /// - /// - public void Dispose(bool disposing) - { - if (disposing) - { - if (WMIClass != null) - { - WMIClass.Dispose(); - } - } - } - -#endregion "IDisposable Members" - -#region overrides - /// - /// Gets the list of Computer Restore point. - /// ID parameter id used to refer the sequence no. When given searched with particular - /// sequence no. and returns the restore point - /// - protected override void BeginProcessing() - { - // system restore APIs are not supported on ARM platform - if (ComputerWMIHelper.SkipSystemRestoreOperationForARMPlatform(this)) - { - return; - } - - try - { - ManagementScope scope = new ManagementScope(ComputerWMIHelper.WMI_Path_Default); - scope.Connect(); - WMIClass = new ManagementClass(ComputerWMIHelper.WMI_Class_SystemRestore); - WMIClass.Scope = scope; - - if (ParameterSetName.Equals("LastStatus")) - { - int restoreStatus = Convert.ToInt32(WMIClass.InvokeMethod("GetLastRestoreStatus", null), System.Globalization.CultureInfo.CurrentCulture); - - if (restoreStatus.Equals(0)) - WriteObject(ComputerResources.RestoreFailed); - else if (restoreStatus.Equals(1)) - WriteObject(ComputerResources.RestoreSuccess); - else if (restoreStatus.Equals(2)) - WriteObject(ComputerResources.RestoreInterrupted); - } - Dictionary sequenceList = new Dictionary(); - if (ParameterSetName.Equals("ID")) - { - ObjectQuery objquery = new ObjectQuery(); - // Dictionary sequenceList = new Dictionary(); - if (RestorePoint == null) - { - objquery.QueryString = "select * from " + ComputerWMIHelper.WMI_Class_SystemRestore; - } - else - { - // sequenceList = new List(); - StringBuilder sb = new StringBuilder("select * from "); - sb.Append(ComputerWMIHelper.WMI_Class_SystemRestore); - sb.Append(" where SequenceNumber = "); - for (int i = 0; i <= RestorePoint.Length - 1; i++) - { - sb.Append(RestorePoint[i]); - if (i < RestorePoint.Length - 1) - sb.Append(" OR SequenceNumber = "); - if (!sequenceList.ContainsKey(RestorePoint[i])) - sequenceList.Add(RestorePoint[i], "true"); - } - objquery.QueryString = sb.ToString(); - } + // Test PowerShell + if (_exit) + { + break; + } - using (ManagementObjectSearcher searcher = new ManagementObjectSearcher(scope, objquery)) - { - foreach (ManagementObject obj in searcher.Get()) - { - using (obj) + if (psTestList.Count > 0) { - WriteObject(obj); - if (RestorePoint != null) + if (_waitOnComputers.Count == 1) { - int sequenceNo = Convert.ToInt32(obj.Properties["SequenceNumber"].Value, System.Globalization.CultureInfo.CurrentCulture); - sequenceList.Remove(sequenceNo); + _status = ComputerResources.WaitForPowerShell; + _percent = CalculateProgressPercentage(PowerShellConnectionTest); + WriteProgress(_indicator[(indicatorIndex++) % 4] + _activity, _status, _percent, ProgressRecordType.Processing); } + + psTestList = TestPowerShell(psTestList, allDoneList, _powershell, this.Credential); } - } - } + } while (false); - if (sequenceList != null) - { - if (sequenceList.Count > 0) + // if time is up or Ctrl+c is typed, break out + if (_exit) { - foreach (int id in sequenceList.Keys) - { - string message = StringUtil.Format(ComputerResources.NoRestorePoint, id); - ArgumentException e = new ArgumentException(message); - ErrorRecord errorrecord = new ErrorRecord(e, "NoRestorePoint", ErrorCategory.InvalidArgument, null); - WriteError(errorrecord); - } + break; } - } - } - } - catch (ManagementException e) - { - if ((e.ErrorCode.Equals(ManagementStatus.NotFound)) || (e.ErrorCode.Equals(ManagementStatus.InvalidClass))) - { - Exception Ex = new ArgumentException(StringUtil.Format(ComputerResources.NotSupported)); - WriteError(new ErrorRecord(Ex, "GetComputerRestorePointNotSupported", ErrorCategory.InvalidOperation, null)); - } - else - { - ErrorRecord errorRecord = new ErrorRecord(e, "GetWMIManagementException", ErrorCategory.InvalidOperation, null); - WriteError(errorRecord); - } - } - catch (COMException e) - { - if (string.IsNullOrEmpty(e.Message)) - { - Exception Ex = new ArgumentException(StringUtil.Format(ComputerResources.SystemRestoreServiceDisabled)); - WriteError(new ErrorRecord(Ex, "ServiceDisabled", ErrorCategory.InvalidOperation, null)); - } - else - { - ErrorRecord errorRecord = new ErrorRecord(e, "COMException", ErrorCategory.InvalidOperation, null); - WriteError(errorRecord); - } - } - } - - /// - /// to implement ^C - /// - protected override void StopProcessing() - { - if (WMIClass != null) - { - WMIClass.Dispose(); - } - } - -#endregion overrides - } - -#endregion - -#endif - -#region Restart-Computer - - /// - /// This exception is thrown when the timeout expires before a computer finishes restarting - /// - [Serializable] - public sealed class RestartComputerTimeoutException : RuntimeException - { - /// - /// Name of the computer that is restarting - /// - public string ComputerName { get; private set; } - - /// - /// The timeout value specified by the user. It indicates the seconds to wait before timeout. - /// - public int Timeout { get; private set; } - - /// - /// Construct a RestartComputerTimeoutException. - /// - /// - /// - /// - /// - internal RestartComputerTimeoutException(string computerName, int timeout, string message, string errorId) - : base(message) - { - SetErrorId(errorId); - SetErrorCategory(ErrorCategory.OperationTimeout); - ComputerName = computerName; - Timeout = timeout; - } - - /// - /// Construct a RestartComputerTimeoutException - /// - public RestartComputerTimeoutException() : base() { } - - /// - /// Constructs a RestartComputerTimeoutException - /// - /// - /// - /// The message used in the exception. - /// - public RestartComputerTimeoutException(string message) : base(message) { } - - /// - /// Constructs a RestartComputerTimeoutException - /// - /// - /// - /// The message used in the exception. - /// - /// - /// - /// An exception that led to this exception. - /// - public RestartComputerTimeoutException(string message, Exception innerException) : base(message, innerException) { } - -#region Serialization - /// - /// Serialization constructor for class RestartComputerTimeoutException - /// - /// - /// - /// serialization information - /// - /// - /// - /// streaming context - /// - private RestartComputerTimeoutException(SerializationInfo info, StreamingContext context) - : base(info, context) - { - if (info == null) - { - throw new PSArgumentNullException("info"); - } - - ComputerName = info.GetString("ComputerName"); - Timeout = info.GetInt32("Timeout"); - } - - /// - /// Serializes the RestartComputerTimeoutException. - /// - /// - /// - /// serialization information - /// - /// - /// - /// streaming context - /// - [SecurityPermission(SecurityAction.Demand, SerializationFormatter = true)] - public override void GetObjectData(SerializationInfo info, StreamingContext context) - { - if (info == null) - { - throw new PSArgumentNullException("info"); - } - - base.GetObjectData(info, context); - info.AddValue("ComputerName", ComputerName); - info.AddValue("Timeout", Timeout); - } -#endregion Serialization - } - - /// - /// Defines the services that Restart-Computer can wait on - /// - [SuppressMessage("Microsoft.Design", "CA1027:MarkEnumsWithFlags")] - public enum WaitForServiceTypes - { - /// - /// Wait for the WMI service to be ready - /// - Wmi = 0x0, - - /// - /// Wait for the WinRM service to be ready - /// - WinRM = 0x1, - - /// - /// Wait for the PowerShell to be ready - /// - PowerShell = 0x2, - } - - /// - /// Restarts the computer - /// - [Cmdlet(VerbsLifecycle.Restart, "Computer", SupportsShouldProcess = true, DefaultParameterSetName = DefaultParameterSet, - HelpUri = "https://go.microsoft.com/fwlink/?LinkID=135253", RemotingCapability = RemotingCapability.OwnedByCommand)] - public class RestartComputerCommand : PSCmdlet, IDisposable - { -#region "Parameters and PrivateData" - - private const string DefaultParameterSet = "DefaultSet"; - private const string AsJobParameterSet = "AsJobSet"; - - /// - /// Used to start a command remotely as a Job. The Job results are collected - /// and stored in the global cache on the client machine. - /// - [Parameter(ParameterSetName = AsJobParameterSet)] - public SwitchParameter AsJob { get; set; } = false; - - /// - /// The following is the definition of the input parameter "Authentication". - /// Specifies the authentication level to be used with WMI connection. Valid - /// values are: - /// - /// Unchanged = -1, - /// Default = 0, - /// None = 1, - /// Connect = 2, - /// Call = 3, - /// Packet = 4, - /// PacketIntegrity = 5, - /// PacketPrivacy = 6. - /// - [Parameter] - [Alias("Authentication")] - [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] - public AuthenticationLevel DcomAuthentication - { - get { return _dcomAuthentication; } - set - { - _dcomAuthentication = value; - _isDcomAuthenticationSpecified = true; - } - } - private AuthenticationLevel _dcomAuthentication = AuthenticationLevel.Packet; - private bool _isDcomAuthenticationSpecified = false; - - /// - /// The following is the definition of the input parameter "Impersonation". - /// Specifies the impersonation level to use when calling the WMI method. Valid - /// values are: - /// - /// Default = 0, - /// Anonymous = 1, - /// Identify = 2, - /// Impersonate = 3, - /// Delegate = 4. - /// - [Parameter] - public ImpersonationLevel Impersonation - { - get { return _impersonation; } - set - { - _impersonation = value; - _isImpersonationSpecified = true; - } - } - private ImpersonationLevel _impersonation = ImpersonationLevel.Impersonate; - private bool _isImpersonationSpecified = false; - - /// - /// The authentication options for CIM_WSMan connection - /// - [Parameter(ParameterSetName = DefaultParameterSet)] - [ValidateSet( - "Default", - "Basic", - "Negotiate", // can be used with and without credential (without -> PSRP mapped to NegotiateWithImplicitCredential) - "CredSSP", - "Digest", - "Kerberos")] // can be used with and without credential (not sure about implications) - [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] - public string WsmanAuthentication { get; set; } - - /// - /// Specify the protocol to use - /// - [Parameter(ParameterSetName = DefaultParameterSet)] - [ValidateSet(ComputerWMIHelper.DcomProtocol, ComputerWMIHelper.WsmanProtocol)] - public string Protocol - { - get { return _protocol; } - set - { - _protocol = value; - _isProtocolSpecified = true; - } - } - - //CoreClr does not support DCOM protocol - // This change makes sure that the the command works seamlessly if user did not explicitly entered the protocol -#if CORECLR - private string _protocol = ComputerWMIHelper.WsmanProtocol; -#else - private string _protocol = ComputerWMIHelper.DcomProtocol; -#endif - private bool _isProtocolSpecified = false; - - /// - /// Specifies the computer (s)Name on which this command is executed. - /// When this parameter is omitted, this cmdlet restarts the local computer. - /// Type the NETBIOS name, IP address, or fully-qualified domain name of one - /// or more computers in a comma-separated list. To specify the local computer, type the computername or "localhost". - /// - [Parameter(Position = 0, ValueFromPipeline = true, - ValueFromPipelineByPropertyName = true)] - [ValidateNotNullOrEmpty] - [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] - [Alias("CN", "__SERVER", "Server", "IPAddress")] - public String[] ComputerName { get; set; } = new string[] { "." }; - - private List _validatedComputerNames = new List(); - private readonly List _waitOnComputers = new List(); - private readonly HashSet _uniqueComputerNames = new HashSet(StringComparer.OrdinalIgnoreCase); - - /// - /// The following is the definition of the input parameter "Credential". - /// Specifies a user account that has permission to perform this action. Type a - /// user-name, such as "User01" or "Domain01\User01", or enter a PSCredential - /// object, such as one from the Get-Credential cmdlet - /// - [Parameter(Position = 1)] - [ValidateNotNullOrEmpty] - [Credential] - public PSCredential Credential { get; set; } - /// - /// Using Force in conjunction with Reboot on a - /// remote computer immediately reboots the remote computer. - /// - [Parameter] - [Alias("f")] - public SwitchParameter Force { get; set; } - - /// - /// Allows the user of the cmdlet to specify a throttling value - /// for throttling the number of remote operations that can - /// be executed simultaneously. - /// - [Parameter(ParameterSetName = AsJobParameterSet)] - [ValidateRange(int.MinValue, (int)1000)] - public Int32 ThrottleLimit - { - get { return _throttlelimit; } - set - { - _throttlelimit = value; - if (_throttlelimit <= 0) - _throttlelimit = 32; - } - } - private Int32 _throttlelimit = 32; - - /// - /// Specify the Wait parameter. Prompt will be blocked is the Timeout is not 0 - /// - [Parameter(ParameterSetName = DefaultParameterSet)] - public SwitchParameter Wait { get; set; } - - /// - /// Specify the Timeout parameter. - /// Negative value indicates wait infinitely. - /// Positive value indicates the seconds to wait before timeout. - /// - [Parameter(ParameterSetName = DefaultParameterSet)] - [Alias("TimeoutSec")] - [ValidateRange(-1, int.MaxValue)] - public int Timeout - { - get { return _timeout; } - set - { - _timeout = value; - _timeoutSpecified = true; - } - } - private int _timeout = -1; - private bool _timeoutSpecified = false; - - /// - /// Specify the For parameter. - /// Wait for the specific service before unblocking the prompt. - /// - [Parameter(ParameterSetName = DefaultParameterSet)] - public WaitForServiceTypes For - { - get { return _waitFor; } - set - { - _waitFor = value; - _waitForSpecified = true; - } - } - private WaitForServiceTypes _waitFor = WaitForServiceTypes.PowerShell; - private bool _waitForSpecified = false; - - /// - /// Specify the Delay parameter. - /// The specific time interval (in second) to wait between network pings or service queries. - /// - [Parameter(ParameterSetName = DefaultParameterSet)] - [ValidateRange(1, Int16.MaxValue)] - public Int16 Delay - { - get { return (Int16)_delay; } - set - { - _delay = value; - _delaySpecified = true; - } - } - private int _delay = 5; - private bool _delaySpecified = false; - - /// - /// Script to test if the PowerShell is ready - /// - private const string TestPowershellScript = @" -$array = @($input) -$result = @{} -foreach ($computerName in $array[1]) -{ - $ret = $null - if ($null -eq array[0]) - { - $ret = Invoke-Command -ComputerName $computerName {$true} -SessionOption (New-PSSessionOption -NoMachineProfile) -ErrorAction SilentlyContinue - } - else - { - $ret = Invoke-Command -ComputerName $computerName {$true} -SessionOption (New-PSSessionOption -NoMachineProfile) -ErrorAction SilentlyContinue -Credential $array[0] - } - - if ($ret -eq $true) - { - $result[$computerName] = $true - } - else - { - $result[$computerName] = $false - } -} -$result -"; - - /// - /// The indicator to use when show progress - /// - private string[] _indicator = { "|", "/", "-", "\\" }; - - /// - /// The activity id - /// - private int _activityId; - - /// - /// After call 'Shutdown' on the target computer, wait a few - /// seconds for the restart to begin. - /// - private const int SecondsToWaitForRestartToBegin = 25; - - /// - /// Actual time out in seconds - /// - private int _timeoutInMilliseconds; - - /// - /// Indicate to exit - /// - private bool _exit, _timeUp; - private readonly CancellationTokenSource _cancel = new CancellationTokenSource(); - - /// - /// A waithandler to wait on. Current thread will wait on it during the delay interval. - /// - private readonly ManualResetEventSlim _waitHandler = new ManualResetEventSlim(false); - private readonly Dictionary _computerInfos = new Dictionary(StringComparer.OrdinalIgnoreCase); - - // CLR 4.0 Port note - use https://msdn.microsoft.com/en-us/library/system.net.networkinformation.ipglobalproperties.hostname(v=vs.110).aspx - private readonly string _shortLocalMachineName = Dns.GetHostName(); - - // And for this, use PsUtils.GetHostname() - private readonly string _fullLocalMachineName = Dns.GetHostEntryAsync("").Result.HostName; - - private int _percent; - private string _status; - private string _activity; - private Timer _timer; - private System.Management.Automation.PowerShell _powershell; - - private const string StageVerification = "VerifyStage"; - private const string WmiConnectionTest = "WMI"; - private const string WinrmConnectionTest = "WinRM"; - private const string PowerShellConnectionTest = "PowerShell"; - -#endregion "parameters and PrivateData" - -#region "IDisposable Members" - - /// - /// Dispose Method - /// - public void Dispose() - { - this.Dispose(true); - // Use SuppressFinalize in case a subclass - // of this type implements a finalizer. - GC.SuppressFinalize(this); - } - - /// - /// Dispose Method. - /// - /// - public void Dispose(bool disposing) - { - if (disposing) - { - if (_timer != null) - { - _timer.Dispose(); - } - - _waitHandler.Dispose(); - _cancel.Dispose(); - if (_powershell != null) - { - _powershell.Dispose(); - } - } - } - -#endregion "IDisposable Members" - -#region "Private Methods" - - /// - /// Validate parameters for 'DefaultSet' - /// 1. When the Wait is specified, the computername cannot contain the local machine - /// 2. If the local machine is present, make sure it is at the end of the list (so the remote ones get restarted before the local machine reboot). - /// - private void ValidateComputerNames() - { - bool containLocalhost = false; - _validatedComputerNames.Clear(); - - foreach (string name in ComputerName) - { - ErrorRecord error = null; - string targetComputerName = ComputerWMIHelper.ValidateComputerName(name, _shortLocalMachineName, _fullLocalMachineName, ref error); - if (targetComputerName == null) - { - if (error != null) - { - WriteError(error); - } - continue; - } - - if (targetComputerName.Equals(ComputerWMIHelper.localhostStr, StringComparison.OrdinalIgnoreCase)) - { - containLocalhost = true; - } - else if (!_uniqueComputerNames.Contains(targetComputerName)) - { - _validatedComputerNames.Add(targetComputerName); - _uniqueComputerNames.Add(targetComputerName); - } - } - - if (Wait && containLocalhost) - { - // The local machine will be ignored, and an error will be emitted. - InvalidOperationException ex = new InvalidOperationException(ComputerResources.CannotWaitLocalComputer); - WriteError(new ErrorRecord(ex, "CannotWaitLocalComputer", ErrorCategory.InvalidOperation, null)); - containLocalhost = false; - } - - // Add the localhost to the end of the list, so we will restart remote machines - // before we restart the local one. - if (containLocalhost) - { - _validatedComputerNames.Add(ComputerWMIHelper.localhostStr); - } - } - - /// - /// Write out progress - /// - /// - /// - /// - /// - private void WriteProgress(string activity, string status, int percent, ProgressRecordType progressRecordType) - { - ProgressRecord progress = new ProgressRecord(_activityId, activity, status); - progress.PercentComplete = percent; - progress.RecordType = progressRecordType; - WriteProgress(progress); - } - - /// - /// Calculate the progress percentage - /// - /// - /// - private int CalculateProgressPercentage(string currentStage) - { - switch (currentStage) - { - case StageVerification: - return _waitFor.Equals(WaitForServiceTypes.Wmi) || _waitFor.Equals(WaitForServiceTypes.WinRM) - ? 33 - : 20; - case WmiConnectionTest: - return _waitFor.Equals(WaitForServiceTypes.Wmi) ? 66 : 40; - case WinrmConnectionTest: - return _waitFor.Equals(WaitForServiceTypes.WinRM) ? 66 : 60; - case PowerShellConnectionTest: - return 80; - default: - break; - } - - Dbg.Diagnostics.Assert(false, "CalculateProgressPercentage should never hit the default case"); - return 0; - } - - /// - /// Event handler for the timer - /// - /// - private void OnTimedEvent(object s) - { - _exit = _timeUp = true; - _cancel.Cancel(); - _waitHandler.Set(); - - if (_powershell != null) - { - _powershell.Stop(); - _powershell.Dispose(); - } - } - - private class ComputerInfo - { - internal string LastBootUpTime; - internal bool RebootComplete; - } - -#if !CORECLR - private List TestRestartStageUsingDcom(IEnumerable computerNames, List nextTestList, CancellationToken token, ConnectionOptions options) - { - var restartStageTestList = new List(); - var query = new ObjectQuery("Select * from " + ComputerWMIHelper.WMI_Class_OperatingSystem); - var enumOptions = new EnumerationOptions { UseAmendedQualifiers = true, DirectRead = true }; - - foreach (var computer in computerNames) - { - try - { - if (token.IsCancellationRequested) { break; } - var scope = new ManagementScope(ComputerWMIHelper.GetScopeString(computer, ComputerWMIHelper.WMI_Path_CIM), options); - using (var searcher = new ManagementObjectSearcher(scope, query, enumOptions)) - { - if (token.IsCancellationRequested) { break; } - - using (var mCollection = searcher.Get()) - { - if (mCollection.Count > 0) - { - foreach (ManagementBaseObject os in mCollection) - { - using (os) - { - string newLastBootUpTime = os.Properties["LastBootUpTime"].Value.ToString(); - string oldLastBootUpTime = _computerInfos[computer].LastBootUpTime; - - if (string.Compare(newLastBootUpTime, oldLastBootUpTime, StringComparison.OrdinalIgnoreCase) != 0) - { - _computerInfos[computer].RebootComplete = true; - nextTestList.Add(computer); - } - else - { - restartStageTestList.Add(computer); - } - } - } - } - else - { - restartStageTestList.Add(computer); - } - } - } - } - catch (ManagementException) - { - restartStageTestList.Add(computer); - } - catch (COMException) - { - restartStageTestList.Add(computer); - } - catch (UnauthorizedAccessException) - { - restartStageTestList.Add(computer); - } - } - - return restartStageTestList; - } -#endif - - private List TestRestartStageUsingWsman(IEnumerable computerNames, List nextTestList, CancellationToken token) - { - var restartStageTestList = new List(); - var operationOptions = new CimOperationOptions - { - Timeout = TimeSpan.FromMilliseconds(2000), - CancellationToken = token - }; - foreach (var computer in computerNames) - { - try - { - if (token.IsCancellationRequested) { break; } - using (CimSession cimSession = RemoteDiscoveryHelper.CreateCimSession(computer, Credential, WsmanAuthentication, token, this)) - { - bool itemRetrieved = false; - IEnumerable mCollection = cimSession.QueryInstances( - ComputerWMIHelper.CimOperatingSystemNamespace, - ComputerWMIHelper.CimQueryDialect, - "Select * from " + ComputerWMIHelper.WMI_Class_OperatingSystem, - operationOptions); - foreach (CimInstance os in mCollection) - { - itemRetrieved = true; - string newLastBootUpTime = os.CimInstanceProperties["LastBootUpTime"].Value.ToString(); - string oldLastBootUpTime = _computerInfos[computer].LastBootUpTime; - - if (string.Compare(newLastBootUpTime, oldLastBootUpTime, StringComparison.OrdinalIgnoreCase) != 0) - { - _computerInfos[computer].RebootComplete = true; - nextTestList.Add(computer); - } - else - { - restartStageTestList.Add(computer); - } - } - - if (!itemRetrieved) - { - restartStageTestList.Add(computer); - } - } - } - catch (CimException) - { - restartStageTestList.Add(computer); - } - catch (Exception) - { - restartStageTestList.Add(computer); - } - } - - return restartStageTestList; - } - -#if !CORECLR - private List SetUpComputerInfoUsingDcom(IEnumerable computerNames, ConnectionOptions options) - { - var validComputerNameList = new List(); - var query = new ObjectQuery("Select * From " + ComputerWMIHelper.WMI_Class_OperatingSystem); - var enumOptions = new EnumerationOptions { UseAmendedQualifiers = true, DirectRead = true }; - - foreach (var computer in computerNames) - { - try - { - var scope = new ManagementScope(ComputerWMIHelper.GetScopeString(computer, ComputerWMIHelper.WMI_Path_CIM), options); - using (var searcher = new ManagementObjectSearcher(scope, query, enumOptions)) - { - using (var mCollection = searcher.Get()) - { - if (mCollection.Count > 0) - { - foreach (ManagementBaseObject os in mCollection) - { - using (os) - { - if (!_computerInfos.ContainsKey(computer)) - { - var info = new ComputerInfo - { - LastBootUpTime = os.Properties["LastBootUpTime"].Value.ToString(), - RebootComplete = false - }; - _computerInfos.Add(computer, info); - validComputerNameList.Add(computer); - } - } - } - } - else - { - string errMsg = StringUtil.Format(ComputerResources.RestartComputerSkipped, computer, ComputerResources.CannotGetOperatingSystemObject); - var error = new ErrorRecord(new InvalidOperationException(errMsg), "RestartComputerSkipped", - ErrorCategory.OperationStopped, computer); - this.WriteError(error); - } - } - } - } - catch (ManagementException ex) - { - string errMsg = StringUtil.Format(ComputerResources.RestartComputerSkipped, computer, ex.Message); - var error = new ErrorRecord(new InvalidOperationException(errMsg), "RestartComputerSkipped", - ErrorCategory.OperationStopped, computer); - this.WriteError(error); - } - catch (COMException ex) - { - string errMsg = StringUtil.Format(ComputerResources.RestartComputerSkipped, computer, ex.Message); - var error = new ErrorRecord(new InvalidOperationException(errMsg), "RestartComputerSkipped", - ErrorCategory.OperationStopped, computer); - this.WriteError(error); - } - catch (UnauthorizedAccessException ex) - { - string errMsg = StringUtil.Format(ComputerResources.RestartComputerSkipped, computer, ex.Message); - var error = new ErrorRecord(new InvalidOperationException(errMsg), "RestartComputerSkipped", - ErrorCategory.OperationStopped, computer); - this.WriteError(error); - } - } - - return validComputerNameList; - } -#endif - - private List SetUpComputerInfoUsingWsman(IEnumerable computerNames, CancellationToken token) - { - var validComputerNameList = new List(); - var operationOptions = new CimOperationOptions - { - Timeout = TimeSpan.FromMilliseconds(2000), - CancellationToken = token - }; - foreach (var computer in computerNames) - { - try - { - using (CimSession cimSession = RemoteDiscoveryHelper.CreateCimSession(computer, Credential, WsmanAuthentication, token, this)) - { - bool itemRetrieved = false; - IEnumerable mCollection = cimSession.QueryInstances( - ComputerWMIHelper.CimOperatingSystemNamespace, - ComputerWMIHelper.CimQueryDialect, - "Select * from " + ComputerWMIHelper.WMI_Class_OperatingSystem, - operationOptions); - foreach (CimInstance os in mCollection) - { - itemRetrieved = true; - if (!_computerInfos.ContainsKey(computer)) - { - var info = new ComputerInfo - { - LastBootUpTime = os.CimInstanceProperties["LastBootUpTime"].Value.ToString(), - RebootComplete = false - }; - _computerInfos.Add(computer, info); - validComputerNameList.Add(computer); - } - } - - if (!itemRetrieved) - { - string errMsg = StringUtil.Format(ComputerResources.RestartComputerSkipped, computer, ComputerResources.CannotGetOperatingSystemObject); - var error = new ErrorRecord(new InvalidOperationException(errMsg), "RestartComputerSkipped", - ErrorCategory.OperationStopped, computer); - this.WriteError(error); - } - } - } - catch (CimException ex) - { - string errMsg = StringUtil.Format(ComputerResources.RestartComputerSkipped, computer, ex.Message); - var error = new ErrorRecord(new InvalidOperationException(errMsg), "RestartComputerSkipped", - ErrorCategory.OperationStopped, computer); - this.WriteError(error); - } - catch (Exception ex) - { - string errMsg = StringUtil.Format(ComputerResources.RestartComputerSkipped, computer, ex.Message); - var error = new ErrorRecord(new InvalidOperationException(errMsg), "RestartComputerSkipped", - ErrorCategory.OperationStopped, computer); - this.WriteError(error); - } - } - - return validComputerNameList; - } - - private void WriteOutTimeoutError(IEnumerable computerNames) - { - const string errorId = "RestartComputerTimeout"; - foreach (string computer in computerNames) - { - string errorMsg = StringUtil.Format(ComputerResources.RestartcomputerFailed, computer, ComputerResources.TimeoutError); - var exception = new RestartComputerTimeoutException(computer, Timeout, errorMsg, errorId); - var error = new ErrorRecord(exception, errorId, ErrorCategory.OperationTimeout, computer); - WriteError(error); - } - } - -#endregion "Private Methods" - -#region "Internal Methods" - - internal static List TestWmiConnectionUsingWsman(List computerNames, List nextTestList, CancellationToken token, PSCredential credential, string wsmanAuthentication, PSCmdlet cmdlet) - { - // Check if the WMI service "Winmgmt" is started - const string wmiServiceQuery = "Select * from " + ComputerWMIHelper.WMI_Class_Service + " Where name = 'Winmgmt'"; - var wmiTestList = new List(); - var operationOptions = new CimOperationOptions - { - Timeout = TimeSpan.FromMilliseconds(2000), - CancellationToken = token - }; - foreach (var computer in computerNames) - { - try - { - if (token.IsCancellationRequested) { break; } - using (CimSession cimSession = RemoteDiscoveryHelper.CreateCimSession(computer, credential, wsmanAuthentication, token, cmdlet)) - { - bool itemRetrieved = false; - IEnumerable mCollection = cimSession.QueryInstances( - ComputerWMIHelper.CimOperatingSystemNamespace, - ComputerWMIHelper.CimQueryDialect, - wmiServiceQuery, - operationOptions); - foreach (CimInstance service in mCollection) - { - itemRetrieved = true; - if (LanguagePrimitives.IsTrue(service.CimInstanceProperties["Started"].Value)) - { - nextTestList.Add(computer); - } - else - { - wmiTestList.Add(computer); - } - } - - if (!itemRetrieved) - { - wmiTestList.Add(computer); - } - } - } - catch (CimException) - { - wmiTestList.Add(computer); - } - catch (Exception) - { - wmiTestList.Add(computer); - } - } - - return wmiTestList; - } - -#if !CORECLR - /// - /// Test WinRM connectivity for the restarting machine - /// - /// - /// - /// - /// - internal static List TestWinrmConnection(List computerNames, List nextTestList, CancellationToken token) - { - List winrmTestList = new List(); - IWSManEx wsmanObject = (IWSManEx)new WSManClass(); - int sessionFlags = (int)WSManSessionFlags.WSManFlagUseNoAuthentication | (int)WSManSessionFlags.WSManFlagUtf8; - IWSManConnectionOptionsEx2 connObject = (IWSManConnectionOptionsEx2)wsmanObject.CreateConnectionOptions(); - - foreach (string computerName in computerNames) - { - try - { - if (token.IsCancellationRequested) { break; } - IWSManSession sessionObj = (IWSManSession)wsmanObject.CreateSession(computerName, sessionFlags, connObject); - if (token.IsCancellationRequested) { break; } - sessionObj.Timeout = 1500; - sessionObj.Identify(0); - nextTestList.Add(computerName); - } - catch (COMException) - { - winrmTestList.Add(computerName); - } - catch (Exception) - { - winrmTestList.Add(computerName); - } - } - return winrmTestList; - } -#endif - - /// - /// Test the PowerShell state for the restarting computer - /// - /// - /// - /// - /// - /// - internal static List TestPowerShell(List computerNames, List nextTestList, System.Management.Automation.PowerShell powershell, PSCredential credential) - { - List psList = new List(); - - try - { - Collection psObjectCollection = powershell.Invoke(new object[] { credential, computerNames.ToArray() }); - if (psObjectCollection == null) - { - Dbg.Diagnostics.Assert(false, "This should never happen. Invoke should never return null."); - } - - // If ^C or timeout happens when we are in powershell.Invoke(), the psObjectCollection might be empty - if (psObjectCollection.Count == 0) - { - return computerNames; - } - - object result = PSObject.Base(psObjectCollection[0]); - Hashtable data = result as Hashtable; - - Dbg.Diagnostics.Assert(data != null, "data should never be null"); - Dbg.Diagnostics.Assert(data.Count == computerNames.Count, "data should contain results for all computers in computerNames"); - - foreach (string computer in computerNames) - { - if (LanguagePrimitives.IsTrue(data[computer])) - { - nextTestList.Add(computer); - } - else - { - psList.Add(computer); - } - } - } - catch (PipelineStoppedException) - { - // powershell.Stop() is invoked because timeout expires, or Ctrl+C is pressed - } - catch (ObjectDisposedException) - { - // powershell.dispose() is invoked because timeout expires, or Ctrl+C is pressed - } - - return psList; - } - -#if !CORECLR - /// - /// Restart one computer - /// - /// - /// - /// - /// - /// - /// - /// True if the restart was successful - /// False otherwise - /// - internal static bool RestartOneComputerUsingDcom(PSCmdlet cmdlet, bool isLocalhost, string computerName, object[] flags, ConnectionOptions options) - { - bool isSuccess = false; - PlatformInvokes.TOKEN_PRIVILEGE currentPrivilegeState = new PlatformInvokes.TOKEN_PRIVILEGE(); - - try - { - if (!(isLocalhost && PlatformInvokes.EnableTokenPrivilege(ComputerWMIHelper.SE_SHUTDOWN_NAME, ref currentPrivilegeState)) && - !(!isLocalhost && PlatformInvokes.EnableTokenPrivilege(ComputerWMIHelper.SE_REMOTE_SHUTDOWN_NAME, ref currentPrivilegeState))) - { - string message = - StringUtil.Format(ComputerResources.PrivilegeNotEnabled, computerName, - isLocalhost ? ComputerWMIHelper.SE_SHUTDOWN_NAME : ComputerWMIHelper.SE_REMOTE_SHUTDOWN_NAME); - ErrorRecord errorRecord = new ErrorRecord(new InvalidOperationException(message), "PrivilegeNotEnabled", ErrorCategory.InvalidOperation, null); - cmdlet.WriteError(errorRecord); - return false; - } - - ManagementScope scope = new ManagementScope(ComputerWMIHelper.GetScopeString(isLocalhost ? "localhost" : computerName, ComputerWMIHelper.WMI_Path_CIM), options); - EnumerationOptions enumOptions = new EnumerationOptions { UseAmendedQualifiers = true, DirectRead = true }; - ObjectQuery query = new ObjectQuery("select * from " + ComputerWMIHelper.WMI_Class_OperatingSystem); - using (var searcher = new ManagementObjectSearcher(scope, query, enumOptions)) - { - foreach (ManagementObject operatingSystem in searcher.Get()) - { - using (operatingSystem) - { - object result = operatingSystem.InvokeMethod("Win32shutdown", flags); - int retVal = Convert.ToInt32(result.ToString(), CultureInfo.CurrentCulture); - if (retVal != 0) - { - var ex = new Win32Exception(retVal); - string errMsg = StringUtil.Format(ComputerResources.RestartcomputerFailed, computerName, ex.Message); - ErrorRecord error = new ErrorRecord( - new InvalidOperationException(errMsg), "RestartcomputerFailed", ErrorCategory.OperationStopped, computerName); - cmdlet.WriteError(error); - } - else - { - isSuccess = true; - } - } - } - } - } - catch (ManagementException ex) - { - string errMsg = StringUtil.Format(ComputerResources.RestartcomputerFailed, computerName, ex.Message); - ErrorRecord error = new ErrorRecord(new InvalidOperationException(errMsg), "RestartcomputerFailed", - ErrorCategory.OperationStopped, computerName); - cmdlet.WriteError(error); - } - catch (COMException ex) - { - string errMsg = StringUtil.Format(ComputerResources.RestartcomputerFailed, computerName, ex.Message); - ErrorRecord error = new ErrorRecord(new InvalidOperationException(errMsg), "RestartcomputerFailed", - ErrorCategory.OperationStopped, computerName); - cmdlet.WriteError(error); - } - catch (UnauthorizedAccessException ex) - { - string errMsg = StringUtil.Format(ComputerResources.RestartcomputerFailed, computerName, ex.Message); - ErrorRecord error = new ErrorRecord(new InvalidOperationException(errMsg), "RestartcomputerFailed", - ErrorCategory.OperationStopped, computerName); - cmdlet.WriteError(error); - } - finally - { - // Restore the previous privilege state if something unexpected happened - PlatformInvokes.RestoreTokenPrivilege( - isLocalhost ? ComputerWMIHelper.SE_SHUTDOWN_NAME : ComputerWMIHelper.SE_REMOTE_SHUTDOWN_NAME, ref currentPrivilegeState); - } - - return isSuccess; - } -#endif - -#endregion "Internal Methods" - -#region "Overrides" - - /// - /// BeginProcessing method. - /// - protected override void BeginProcessing() - { - if (ParameterSetName.Equals(DefaultParameterSet, StringComparison.OrdinalIgnoreCase)) - { - if (WsmanAuthentication != null && (_isDcomAuthenticationSpecified || _isImpersonationSpecified)) - { - string errorMsg = StringUtil.Format(ComputerResources.ParameterConfliction, - ComputerResources.ParameterUsage); - InvalidOperationException ex = new InvalidOperationException(errorMsg); - ThrowTerminatingError(new ErrorRecord(ex, "ParameterConfliction", ErrorCategory.InvalidOperation, null)); - } - - bool usingDcom = Protocol.Equals(ComputerWMIHelper.DcomProtocol, StringComparison.OrdinalIgnoreCase); - bool usingWsman = Protocol.Equals(ComputerWMIHelper.WsmanProtocol, StringComparison.OrdinalIgnoreCase); - - if (_isProtocolSpecified && usingDcom && WsmanAuthentication != null) - { - string errorMsg = StringUtil.Format(ComputerResources.InvalidParameterForDCOM, - ComputerResources.ParameterUsage); - InvalidOperationException ex = new InvalidOperationException(errorMsg); - ThrowTerminatingError(new ErrorRecord(ex, "InvalidParameterForDCOM", ErrorCategory.InvalidOperation, null)); - } - - if (_isProtocolSpecified && usingWsman && (_isDcomAuthenticationSpecified || _isImpersonationSpecified)) - { - string errorMsg = StringUtil.Format(ComputerResources.InvalidParameterForWSMan, - ComputerResources.ParameterUsage); - InvalidOperationException ex = new InvalidOperationException(errorMsg); - ThrowTerminatingError(new ErrorRecord(ex, "InvalidParameterForWSMan", ErrorCategory.InvalidOperation, null)); - } - - if (!_isProtocolSpecified && WsmanAuthentication != null) - { - // Change the protocol to be WSMan if the WsmanAuthentication is specified - Protocol = ComputerWMIHelper.WsmanProtocol; - } - } - -#if CORECLR - if (this.MyInvocation.BoundParameters.ContainsKey("DcomAuthentication")) - { - string errMsg = StringUtil.Format(ComputerResources.InvalidParameterForCoreClr, "DcomAuthentication"); - PSArgumentException ex = new PSArgumentException(errMsg, ComputerResources.InvalidParameterForCoreClr); - ThrowTerminatingError(new ErrorRecord(ex, "InvalidParameterForCoreClr", ErrorCategory.InvalidArgument, null)); - } - - if (this.MyInvocation.BoundParameters.ContainsKey("Impersonation")) - { - string errMsg = StringUtil.Format(ComputerResources.InvalidParameterForCoreClr, "Impersonation"); - PSArgumentException ex = new PSArgumentException(errMsg, ComputerResources.InvalidParameterForCoreClr); - ThrowTerminatingError(new ErrorRecord(ex, "InvalidParameterForCoreClr", ErrorCategory.InvalidArgument, null)); - } - - // DCOM Authentication is not supported for CoreCLR. Throw an error - // and request that the user specify WSMan Authentication. - if (_isDcomAuthenticationSpecified || - Protocol.Equals(ComputerWMIHelper.DcomProtocol, StringComparison.OrdinalIgnoreCase)) - { - InvalidOperationException ex = new InvalidOperationException(ComputerResources.InvalidParameterDCOMNotSupported); - ThrowTerminatingError(new ErrorRecord(ex, "InvalidParameterDCOMNotSupported", ErrorCategory.InvalidOperation, null)); - } - - // TODO:CORECLR This should be re-visited if we decide to add double hop remoting to CoreCLR (outgoing connections) - if (ParameterSetName.Equals(AsJobParameterSet, StringComparison.OrdinalIgnoreCase)) - { - InvalidOperationException ex = new InvalidOperationException(ComputerResources.InvalidParameterSetAsJob); - ThrowTerminatingError(new ErrorRecord(ex, "InvalidParameterSetAsJob", ErrorCategory.InvalidOperation, null)); - } -#endif - - // Timeout, For, Delay, Progress cannot be present if Wait is not present - if ((_timeoutSpecified || _waitForSpecified || _delaySpecified) && !Wait) - { - InvalidOperationException ex = new InvalidOperationException(ComputerResources.RestartComputerInvalidParameter); - ThrowTerminatingError(new ErrorRecord(ex, "RestartComputerInvalidParameter", ErrorCategory.InvalidOperation, null)); - } - - if (Wait) - { - _activityId = (new Random()).Next(); - if (_timeout == -1 || _timeout >= int.MaxValue / 1000) - { - _timeoutInMilliseconds = int.MaxValue; - } - else - { - _timeoutInMilliseconds = _timeout * 1000; - } - - // We don't support combined service types for now - switch (_waitFor) - { - case WaitForServiceTypes.Wmi: - case WaitForServiceTypes.WinRM: - break; - case WaitForServiceTypes.PowerShell: - _powershell = System.Management.Automation.PowerShell.Create(); - _powershell.AddScript(TestPowershellScript); - break; - default: - InvalidOperationException ex = new InvalidOperationException(ComputerResources.NoSupportForCombinedServiceType); - ErrorRecord error = new ErrorRecord(ex, "NoSupportForCombinedServiceType", ErrorCategory.InvalidOperation, (int)_waitFor); - ThrowTerminatingError(error); - break; - } - } - } - - /// - /// ProcessRecord method. - /// - [SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling")] - protected override void ProcessRecord() - { - // Validate parameters - ValidateComputerNames(); - - object[] flags = new object[] { 2, 0 }; - if (Force) flags[0] = 6; - -#if CORECLR - if (ParameterSetName.Equals(DefaultParameterSet, StringComparison.OrdinalIgnoreCase)) - { -#else // TODO:CORECLR Revisit if or when jobs are supported - if (ParameterSetName.Equals(AsJobParameterSet, StringComparison.OrdinalIgnoreCase)) - { - string[] names = _validatedComputerNames.ToArray(); - string strComputers = ComputerWMIHelper.GetMachineNames(names); - if (!ShouldProcess(strComputers)) - return; - - InvokeWmiMethod WmiInvokeCmd = new InvokeWmiMethod(); - WmiInvokeCmd.Path = ComputerWMIHelper.WMI_Class_OperatingSystem + "=@"; - WmiInvokeCmd.ComputerName = names; - WmiInvokeCmd.Authentication = DcomAuthentication; - WmiInvokeCmd.Impersonation = Impersonation; - WmiInvokeCmd.Credential = Credential; - WmiInvokeCmd.ThrottleLimit = ThrottleLimit; - WmiInvokeCmd.Name = "Win32Shutdown"; - WmiInvokeCmd.EnableAllPrivileges = SwitchParameter.Present; - WmiInvokeCmd.ArgumentList = flags; - PSWmiJob wmiJob = new PSWmiJob(WmiInvokeCmd, names, ThrottleLimit, Job.GetCommandTextFromInvocationInfo(this.MyInvocation)); - this.JobRepository.Add(wmiJob); - WriteObject(wmiJob); - } - else if (ParameterSetName.Equals(DefaultParameterSet, StringComparison.OrdinalIgnoreCase)) - { - // CoreCLR does not support DCOM, so there is no point checking - // it here. It was already validated in BeginProcessing(). - - bool dcomInUse = Protocol.Equals(ComputerWMIHelper.DcomProtocol, StringComparison.OrdinalIgnoreCase); - ConnectionOptions options = ComputerWMIHelper.GetConnectionOptions(this.DcomAuthentication, this.Impersonation, this.Credential); -#endif - if (Wait && _timeout != 0) - { - _validatedComputerNames = -#if !CORECLR - dcomInUse ? SetUpComputerInfoUsingDcom(_validatedComputerNames, options) : -#endif - SetUpComputerInfoUsingWsman(_validatedComputerNames, _cancel.Token); - } - - foreach (string computer in _validatedComputerNames) - { - bool isLocal = false; - string compname; - - if (computer.Equals("localhost", StringComparison.CurrentCultureIgnoreCase)) - { - compname = _shortLocalMachineName; - isLocal = true; - -#if !CORECLR - if (dcomInUse) - { - // The local machine will always at the end of the list. If the current target - // computer is the local machine, it's safe to set Username and Password to null. - options.Username = null; - options.SecurePassword = null; - } -#endif - } - else - { - compname = computer; - } - - // Generate target and action strings - string action = - StringUtil.Format( - ComputerResources.RestartComputerAction, - isLocal ? ComputerResources.LocalShutdownPrivilege : ComputerResources.RemoteShutdownPrivilege); - string target = - isLocal ? StringUtil.Format(ComputerResources.DoubleComputerName, "localhost", compname) : compname; - - if (!ShouldProcess(target, action)) - { - continue; - } - - bool isSuccess = -#if !CORECLR - dcomInUse ? RestartOneComputerUsingDcom(this, isLocal, compname, flags, options) : -#endif - ComputerWMIHelper.InvokeWin32ShutdownUsingWsman(this, isLocal, compname, flags, Credential, WsmanAuthentication, ComputerResources.RestartcomputerFailed, "RestartcomputerFailed", _cancel.Token); - - if (isSuccess && Wait && _timeout != 0) - { - _waitOnComputers.Add(computer); - } - }//end foreach - - if (_waitOnComputers.Count > 0) - { - var restartStageTestList = new List(_waitOnComputers); - var wmiTestList = new List(); - var winrmTestList = new List(); - var psTestList = new List(); - var allDoneList = new List(); - - bool isForWmi = _waitFor.Equals(WaitForServiceTypes.Wmi); - bool isForWinRm = _waitFor.Equals(WaitForServiceTypes.WinRM); - bool isForPowershell = _waitFor.Equals(WaitForServiceTypes.PowerShell); - - int indicatorIndex = 0; - int machineCompleteRestart = 0; - int actualDelay = SecondsToWaitForRestartToBegin; - bool first = true; - bool waitComplete = false; - - _percent = 0; - _status = ComputerResources.WaitForRestartToBegin; - _activity = _waitOnComputers.Count == 1 ? - StringUtil.Format(ComputerResources.RestartSingleComputerActivity, _waitOnComputers[0]) : - ComputerResources.RestartMultipleComputersActivity; - - _timer = new Timer(OnTimedEvent, null, _timeoutInMilliseconds, System.Threading.Timeout.Infinite); - - while (true) - { - int loopCount = actualDelay * 4; // (delay * 1000)/250ms - while (loopCount > 0) - { - WriteProgress(_indicator[(indicatorIndex++) % 4] + _activity, _status, _percent, ProgressRecordType.Processing); - - loopCount--; - _waitHandler.Wait(250); - if (_exit) { break; } - } - - if (first) - { - actualDelay = _delay; - first = false; - - if (_waitOnComputers.Count > 1) - { - _status = StringUtil.Format(ComputerResources.WaitForMultipleComputers, machineCompleteRestart, _waitOnComputers.Count); - WriteProgress(_indicator[(indicatorIndex++) % 4] + _activity, _status, _percent, ProgressRecordType.Processing); - } - } - - do - { - // Test restart stage. - // We check if the target machine has already rebooted by querying the LastBootUpTime from the Win32_OperatingSystem object. - // So after this step, we are sure that both the Network and the WMI or WinRM service have already come up. - if (_exit) { break; } - if (restartStageTestList.Count > 0) - { - if (_waitOnComputers.Count == 1) - { - _status = ComputerResources.VerifyRebootStage; - _percent = CalculateProgressPercentage(StageVerification); - WriteProgress(_indicator[(indicatorIndex++) % 4] + _activity, _status, _percent, ProgressRecordType.Processing); - } - List nextTestList = (isForWmi || isForPowershell) ? wmiTestList : winrmTestList; - restartStageTestList = -#if !CORECLR - dcomInUse ? TestRestartStageUsingDcom(restartStageTestList, nextTestList, _cancel.Token, options) : -#endif - TestRestartStageUsingWsman(restartStageTestList, nextTestList, _cancel.Token); - } - - // Test WMI service - if (_exit) { break; } - if (wmiTestList.Count > 0) - { -#if !CORECLR - if (dcomInUse) - { - // CIM-DCOM is in use. In this case, restart stage checking is done by using WMIv1, - // so the WMI service on the target machine is already up at this point. - winrmTestList.AddRange(wmiTestList); - wmiTestList.Clear(); - - if (_waitOnComputers.Count == 1) - { - // This is to simulate the test for WMI service - _status = ComputerResources.WaitForWMI; - _percent = CalculateProgressPercentage(WmiConnectionTest); - - loopCount = actualDelay * 4; // (delay * 1000)/250ms - while (loopCount > 0) - { - WriteProgress(_indicator[(indicatorIndex++) % 4] + _activity, _status, _percent, ProgressRecordType.Processing); - - loopCount--; - _waitHandler.Wait(250); - if (_exit) { break; } - } - } - } - else -#endif - // This statement block executes for both CLRs. - // In the "full" CLR, it serves as the else case. - { - if (_waitOnComputers.Count == 1) - { - _status = ComputerResources.WaitForWMI; - _percent = CalculateProgressPercentage(WmiConnectionTest); - WriteProgress(_indicator[(indicatorIndex++) % 4] + _activity, _status, _percent, ProgressRecordType.Processing); - } - wmiTestList = TestWmiConnectionUsingWsman(wmiTestList, winrmTestList, _cancel.Token, Credential, WsmanAuthentication, this); - } - } - if (isForWmi) { break; } - - // Test WinRM service - if (_exit) { break; } - if (winrmTestList.Count > 0) - { -#if !CORECLR - if (dcomInUse) - { - if (_waitOnComputers.Count == 1) - { - _status = ComputerResources.WaitForWinRM; - _percent = CalculateProgressPercentage(WinrmConnectionTest); - WriteProgress(_indicator[(indicatorIndex++) % 4] + _activity, _status, _percent, ProgressRecordType.Processing); - } - winrmTestList = TestWinrmConnection(winrmTestList, psTestList, _cancel.Token); - } - else -#endif - // This statement block executes for both CLRs. - // In the "full" CLR, it serves as the else case. - { - // CIM-WSMan in use. In this case, restart stage checking is done by using WMIv2, - // so the WinRM service on the target machine is already up at this point. - psTestList.AddRange(winrmTestList); - winrmTestList.Clear(); - - if (_waitOnComputers.Count == 1) - { - // This is to simulate the test for WinRM service - _status = ComputerResources.WaitForWinRM; - _percent = CalculateProgressPercentage(WinrmConnectionTest); - - loopCount = actualDelay * 4; // (delay * 1000)/250ms - while (loopCount > 0) - { - WriteProgress(_indicator[(indicatorIndex++) % 4] + _activity, _status, _percent, ProgressRecordType.Processing); - - loopCount--; - _waitHandler.Wait(250); - if (_exit) { break; } - } - } - } - } - if (isForWinRm) { break; } - - // Test PowerShell - if (_exit) { break; } - if (psTestList.Count > 0) - { - if (_waitOnComputers.Count == 1) - { - _status = ComputerResources.WaitForPowerShell; - _percent = CalculateProgressPercentage(PowerShellConnectionTest); - WriteProgress(_indicator[(indicatorIndex++) % 4] + _activity, _status, _percent, ProgressRecordType.Processing); - } - psTestList = TestPowerShell(psTestList, allDoneList, _powershell, this.Credential); - } - } while (false); - - // if time is up or Ctrl+c is typed, break out - if (_exit) { break; } - - // Check if the restart completes - switch (_waitFor) - { - case WaitForServiceTypes.Wmi: - waitComplete = (winrmTestList.Count == _waitOnComputers.Count); - machineCompleteRestart = winrmTestList.Count; - break; - case WaitForServiceTypes.WinRM: - waitComplete = (psTestList.Count == _waitOnComputers.Count); - machineCompleteRestart = psTestList.Count; - break; - case WaitForServiceTypes.PowerShell: - waitComplete = (allDoneList.Count == _waitOnComputers.Count); - machineCompleteRestart = allDoneList.Count; - break; - } - - // Wait is done or time is up - if (waitComplete || _exit) - { - if (waitComplete) - { - _status = ComputerResources.RestartComplete; - WriteProgress(_indicator[indicatorIndex % 4] + _activity, _status, 100, ProgressRecordType.Completed); - _timer.Change(System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite); - } - break; - } - - if (_waitOnComputers.Count > 1) - { - _status = StringUtil.Format(ComputerResources.WaitForMultipleComputers, machineCompleteRestart, _waitOnComputers.Count); - _percent = machineCompleteRestart * 100 / _waitOnComputers.Count; - } - }// end while(true) - - if (_timeUp) - { - // The timeout expires. Write out timeout error messages for the computers that haven't finished restarting - do - { - if (restartStageTestList.Count > 0) { WriteOutTimeoutError(restartStageTestList); } - if (wmiTestList.Count > 0) { WriteOutTimeoutError(wmiTestList); } - // Wait for WMI. All computers that finished restarting are put in "winrmTestList" - if (isForWmi) { break; } - - // Wait for WinRM. All computers that finished restarting are put in "psTestList" - if (winrmTestList.Count > 0) { WriteOutTimeoutError(winrmTestList); } - if (isForWinRm) { break; } - - if (psTestList.Count > 0) { WriteOutTimeoutError(psTestList); } - // Wait for PowerShell. All computers that finished restarting are put in "allDoneList" - } while (false); - } - }// end if(waitOnComputer.Count > 0) - }//end DefaultParameter - }//End Processrecord - - /// - /// to implement ^C - /// - protected override void StopProcessing() - { - _exit = true; - _cancel.Cancel(); - _waitHandler.Set(); - - if (_timer != null) - { - _timer.Change(System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite); - } - - if (_powershell != null) - { - _powershell.Stop(); - _powershell.Dispose(); - } - } - -#endregion "Overrides" - } - -#endregion Restart-Computer - -#region Stop-Computer - - /// - /// cmdlet to stop computer - /// - [Cmdlet(VerbsLifecycle.Stop, "Computer", SupportsShouldProcess = true, - HelpUri = "https://go.microsoft.com/fwlink/?LinkID=135263", RemotingCapability = RemotingCapability.SupportedByCommand)] - public sealed class StopComputerCommand : PSCmdlet, IDisposable - { -#region Private Members - -#if !CORECLR - private ManagementObjectSearcher _searcher; -#endif - private readonly CancellationTokenSource _cancel = new CancellationTokenSource(); - private TransportProtocol _transportProtocol = TransportProtocol.DCOM; - -#endregion - -#region "Parameters" - - /// - /// parameter - /// - [Parameter] - public SwitchParameter AsJob { get; set; } = false; - - /// - /// The following is the definition of the input parameter "DcomAuthentication". - /// Specifies the authentication level to be used with WMI connection. Valid - /// values are: - /// - /// Unchanged = -1, - /// Default = 0, - /// None = 1, - /// Connect = 2, - /// Call = 3, - /// Packet = 4, - /// PacketIntegrity = 5, - /// PacketPrivacy = 6. - /// - [Parameter] - [Alias("Authentication")] - public AuthenticationLevel DcomAuthentication { get; set; } = AuthenticationLevel.Packet; - - /// - /// The authentication options for CIM_WSMan connection - /// - [Parameter] - [ValidateSet( - "Default", - "Basic", - "Negotiate", // can be used with and without credential (without -> PSRP mapped to NegotiateWithImplicitCredential) - "CredSSP", - "Digest", - "Kerberos")] // can be used with and without credential (not sure about implications) - [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] - public string WsmanAuthentication { get; set; } = "Default"; - - /// - /// Specify the protocol to use - /// - [Parameter] - [ValidateSet(ComputerWMIHelper.DcomProtocol, ComputerWMIHelper.WsmanProtocol)] - public string Protocol { get; set; } = -#if CORECLR - //CoreClr does not support DCOM protocol - // This change makes sure that the the command works seamlessly if user did not explicitly entered the protocol - ComputerWMIHelper.WsmanProtocol; -#else - ComputerWMIHelper.DcomProtocol; -#endif - - /// - /// The following is the definition of the input parameter "ComputerName". - /// Value of the address requested. The form of the value can be either the - /// computer name ("wxyz1234"), IPv4 address ("192.168.177.124"), or IPv6 - /// address ("2010:836B:4179::836B:4179"). - /// - [Parameter(Position = 0, ValueFromPipelineByPropertyName = true)] - [ValidateNotNullOrEmpty] - [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] - [Alias("CN", "__SERVER", "Server", "IPAddress")] - public String[] ComputerName { get; set; } = new string[] { "." }; - - - /// - /// The following is the definition of the input parameter "Credential". - /// Specifies a user account that has permission to perform this action. Type a - /// user-name, such as "User01" or "Domain01\User01", or enter a PSCredential - /// object, such as one from the Get-Credential cmdlet - /// - [Parameter(Position = 1)] - [ValidateNotNullOrEmpty] - [Credential] - public PSCredential Credential { get; set; } - - /// - /// The following is the definition of the input parameter "Impersonation". - /// Specifies the impersonation level to use when calling the WMI method. Valid - /// values are: - /// - /// Default = 0, - /// Anonymous = 1, - /// Identify = 2, - /// Impersonate = 3, - /// Delegate = 4. - /// - [Parameter] - public ImpersonationLevel Impersonation { get; set; } = ImpersonationLevel.Impersonate; - - /// - /// The following is the definition of the input parameter "ThrottleLimit". - /// The number of concurrent computers on which the command will be allowed to - /// execute - /// - [Parameter] - [ValidateRange(int.MinValue, (int)1000)] - public Int32 ThrottleLimit - { - get { return _throttlelimit; } - set - { - _throttlelimit = value; - if (_throttlelimit <= 0) - _throttlelimit = 32; - } - } - private Int32 _throttlelimit = 32; - - /// - /// The following is the definition of the input parameter "ThrottleLimit". - /// The number of concurrent computers on which the command will be allowed to - /// execute - /// - [Parameter] - public SwitchParameter Force { get; set; } = false; - - #endregion "parameters" - -#region "IDisposable Members" - - /// - /// Dispose Method - /// - public void Dispose() - { - try - { - _cancel.Dispose(); - } - catch (ObjectDisposedException) { } - } - -#endregion "IDisposable Members" - -#region "Overrides" - - /// - /// BeginProcessing - /// - protected override void BeginProcessing() - { - base.BeginProcessing(); - - // Verify parameter set - bool haveProtocolParam = this.MyInvocation.BoundParameters.ContainsKey("Protocol"); - bool haveWsmanAuthenticationParam = this.MyInvocation.BoundParameters.ContainsKey("WsmanAuthentication"); - bool haveDcomAuthenticationParam = this.MyInvocation.BoundParameters.ContainsKey("DcomAuthentication"); - bool haveDcomImpersonation = this.MyInvocation.BoundParameters.ContainsKey("Impersonation"); - _transportProtocol = (this.Protocol.Equals(ComputerWMIHelper.WsmanProtocol, StringComparison.OrdinalIgnoreCase) || (haveWsmanAuthenticationParam && !haveProtocolParam)) ? - TransportProtocol.WSMan : TransportProtocol.DCOM; - - if (haveWsmanAuthenticationParam && (haveDcomAuthenticationParam || haveDcomImpersonation)) - { - string errMsg = StringUtil.Format(ComputerResources.StopCommandParamWSManAuthConflict, ComputerResources.StopCommandParamMessage); - ThrowTerminatingError( - new ErrorRecord( - new PSArgumentException(errMsg), - "InvalidParameter", - ErrorCategory.InvalidArgument, - this)); - } - - if ((_transportProtocol == TransportProtocol.DCOM) && haveWsmanAuthenticationParam) - { - string errMsg = StringUtil.Format(ComputerResources.StopCommandWSManAuthProtocolConflict, ComputerResources.StopCommandParamMessage); - ThrowTerminatingError( - new ErrorRecord( - new PSArgumentException(errMsg), - "InvalidParameter", - ErrorCategory.InvalidArgument, - this)); - } - - if ((_transportProtocol == TransportProtocol.WSMan) && (haveDcomAuthenticationParam || haveDcomImpersonation)) - { - string errMsg = StringUtil.Format(ComputerResources.StopCommandAuthProtocolConflict, ComputerResources.StopCommandParamMessage); - ThrowTerminatingError( - new ErrorRecord( - new PSArgumentException(errMsg), - "InvalidParameter", - ErrorCategory.InvalidArgument, - this)); - } - -#if CORECLR - if (this.MyInvocation.BoundParameters.ContainsKey("DcomAuthentication")) - { - string errMsg = StringUtil.Format(ComputerResources.InvalidParameterForCoreClr, "DcomAuthentication"); - PSArgumentException ex = new PSArgumentException(errMsg, ComputerResources.InvalidParameterForCoreClr); - ThrowTerminatingError(new ErrorRecord(ex, "InvalidParameterForCoreClr", ErrorCategory.InvalidArgument, null)); - } - - if (this.MyInvocation.BoundParameters.ContainsKey("Impersonation")) - { - string errMsg = StringUtil.Format(ComputerResources.InvalidParameterForCoreClr, "Impersonation"); - PSArgumentException ex = new PSArgumentException(errMsg, ComputerResources.InvalidParameterForCoreClr); - ThrowTerminatingError(new ErrorRecord(ex, "InvalidParameterForCoreClr", ErrorCategory.InvalidArgument, null)); - } - - if(this.Protocol.Equals(ComputerWMIHelper.DcomProtocol , StringComparison.OrdinalIgnoreCase)) - { - InvalidOperationException ex = new InvalidOperationException(ComputerResources.InvalidParameterDCOMNotSupported); - ThrowTerminatingError(new ErrorRecord(ex, "InvalidParameterDCOMNotSupported", ErrorCategory.InvalidOperation, null)); - } -#endif - } - - /// - /// ProcessRecord - /// - [SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling")] - protected override void ProcessRecord() - { - object[] flags = new object[] { 1, 0 }; - if (Force.IsPresent) - flags[0] = 5; - - switch (_transportProtocol) - { -#if !CORECLR - case TransportProtocol.DCOM: - ProcessDCOMProtocol(flags); - break; -#endif - - case TransportProtocol.WSMan: - ProcessWSManProtocol(flags); - break; - } - }//End Processrecord - - /// - /// to implement ^C - /// - protected override void StopProcessing() - { -#if !CORECLR - ManagementObjectSearcher searcher = _searcher; - if (searcher != null) - { - try - { - searcher.Dispose(); - } - catch (ObjectDisposedException) { } - } -#endif - - try - { - _cancel.Cancel(); - } - catch (ObjectDisposedException) { } - catch (AggregateException) { } - } - -#endregion "Overrides" - -#region Private Methods - -#if !CORECLR - private void ProcessDCOMProtocol(object[] flags) - { - if (AsJob.IsPresent) - { - string strComputers = ComputerWMIHelper.GetMachineNames(ComputerName); - if (!ShouldProcess(strComputers)) - return; - InvokeWmiMethod WmiInvokeCmd = new InvokeWmiMethod(); - WmiInvokeCmd.Path = ComputerWMIHelper.WMI_Class_OperatingSystem + "=@"; - WmiInvokeCmd.ComputerName = ComputerName; - WmiInvokeCmd.Authentication = DcomAuthentication; - WmiInvokeCmd.Impersonation = Impersonation; - WmiInvokeCmd.Credential = Credential; - WmiInvokeCmd.ThrottleLimit = _throttlelimit; - WmiInvokeCmd.Name = "Win32Shutdown"; - WmiInvokeCmd.EnableAllPrivileges = SwitchParameter.Present; - WmiInvokeCmd.ArgumentList = flags; - PSWmiJob wmiJob = new PSWmiJob(WmiInvokeCmd, ComputerName, _throttlelimit, Job.GetCommandTextFromInvocationInfo(this.MyInvocation)); - this.JobRepository.Add(wmiJob); - WriteObject(wmiJob); - } - else - { - string compname = string.Empty; - string strLocal = string.Empty; - - foreach (string computer in ComputerName) - { - if ((computer.Equals("localhost", StringComparison.CurrentCultureIgnoreCase)) || (computer.Equals(".", StringComparison.OrdinalIgnoreCase))) - { - compname = Dns.GetHostName(); - strLocal = "localhost"; - } - else - { - compname = computer; - } - - if (!ShouldProcess(StringUtil.Format(ComputerResources.DoubleComputerName, strLocal, compname))) - { - continue; - } - else - { - try - { - ConnectionOptions options = ComputerWMIHelper.GetConnectionOptions(DcomAuthentication, this.Impersonation, this.Credential); - ManagementScope scope = new ManagementScope(ComputerWMIHelper.GetScopeString(computer, ComputerWMIHelper.WMI_Path_CIM), options); - EnumerationOptions enumOptions = new EnumerationOptions(); - enumOptions.UseAmendedQualifiers = true; - enumOptions.DirectRead = true; - ObjectQuery query = new ObjectQuery("select * from " + ComputerWMIHelper.WMI_Class_OperatingSystem); - using (_searcher = new ManagementObjectSearcher(scope, query, enumOptions)) - { - foreach (ManagementObject obj in _searcher.Get()) - { - using (obj) - { - object result = obj.InvokeMethod("Win32shutdown", flags); - int retVal = Convert.ToInt32(result.ToString(), CultureInfo.CurrentCulture); - if (retVal != 0) - { - ComputerWMIHelper.WriteNonTerminatingError(retVal, this, compname); - } - } - } - } - _searcher = null; - } - catch (ManagementException e) - { - ErrorRecord errorRecord = new ErrorRecord(e, "StopComputerException", ErrorCategory.InvalidOperation, compname); - WriteError(errorRecord); - continue; - } - catch (System.Runtime.InteropServices.COMException e) - { - ErrorRecord errorRecord = new ErrorRecord(e, "StopComputerException", ErrorCategory.InvalidOperation, compname); - WriteError(errorRecord); - continue; - } - } - } - } - } -#endif - - private void ProcessWSManProtocol(object[] flags) - { - if (AsJob.IsPresent) - { - // TODO: Need job for MI.Net WSMan protocol - // Early return of job object. - throw new PSNotSupportedException(); - } - - foreach (string computer in ComputerName) - { - string compname = string.Empty; - string strLocal = string.Empty; - bool isLocalHost = false; - - if (_cancel.Token.IsCancellationRequested) { break; } - - if ((computer.Equals("localhost", StringComparison.CurrentCultureIgnoreCase)) || (computer.Equals(".", StringComparison.OrdinalIgnoreCase))) - { - compname = Dns.GetHostName(); - strLocal = "localhost"; - isLocalHost = true; - } - else - { - compname = computer; - } - - if (!ShouldProcess(StringUtil.Format(ComputerResources.DoubleComputerName, strLocal, compname))) - { - continue; - } - else - { - ComputerWMIHelper.InvokeWin32ShutdownUsingWsman( - this, - isLocalHost, - compname, - flags, - Credential, - WsmanAuthentication, - ComputerResources.StopcomputerFailed, - "StopComputerException", - _cancel.Token); - } - } - } - -#endregion - } - -#endregion - -#if !CORECLR // TODO:CORECLR Enable once moved to MI .Net - -#region Restore-Computer - - /// - /// This cmdlet is to Restore Computer - /// - [Cmdlet(VerbsData.Restore, "Computer", SupportsShouldProcess = true, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=135254")] - public sealed class RestoreComputerCommand : PSCmdlet, IDisposable - { -#region Parameters - /// - /// Restorepoint parameter - /// - - [Parameter(Position = 0, Mandatory = true)] - [ValidateNotNull] - [ValidateRangeAttribute((int)1, int.MaxValue)] - [Alias("SequenceNumber", "SN", "RP")] - public int RestorePoint { get; set; } - - #endregion - - private ManagementClass WMIClass; - -#region "IDisposable Members" - - /// - /// Dispose Method - /// - public void Dispose() - { - this.Dispose(true); - // Use SuppressFinalize in case a subclass - // of this type implements a finalizer. - GC.SuppressFinalize(this); - } - - /// - /// Dispose Method. - /// - /// - public void Dispose(bool disposing) - { - if (disposing) - { - if (WMIClass != null) - { - WMIClass.Dispose(); - } - } - } - -#endregion "IDisposable Members" - -#region overrides - /// - /// Restores the computer with - /// - protected override void BeginProcessing() - { - // system restore APIs are not supported on ARM platform - if (ComputerWMIHelper.SkipSystemRestoreOperationForARMPlatform(this)) - { - return; - } - - try - { - ConnectionOptions conn = ComputerWMIHelper.GetConnectionOptions(AuthenticationLevel.Packet, ImpersonationLevel.Impersonate, null); - ManagementPath mPath = new ManagementPath(); - mPath.Path = ComputerWMIHelper.WMI_Path_Default; - ManagementScope scope = new ManagementScope(mPath, conn); - scope.Connect(); - WMIClass = new ManagementClass(ComputerWMIHelper.WMI_Class_SystemRestore); - WMIClass.Scope = scope; - - //query to get the list of restore points - ObjectQuery oquery = new ObjectQuery("select * from " + ComputerWMIHelper.WMI_Class_SystemRestore + " where SequenceNumber = " + RestorePoint); - using (ManagementObjectSearcher R_results = new ManagementObjectSearcher(scope, oquery)) - { - //check if the entered restore point is a valid one - if (R_results.Get().Count == 0) - { - string message = StringUtil.Format(ComputerResources.InvalidRestorePoint, RestorePoint); - ArgumentException e = new ArgumentException(message); - ErrorRecord errorrecord = new ErrorRecord(e, "InvalidRestorePoint", ErrorCategory.InvalidArgument, null); - WriteError(errorrecord); - return; - } - } - //confirm with the user before restoring - string computerName = Environment.MachineName; - if (!ShouldProcess(computerName)) - { - return; - } - else - { - //add the restorepoint parameter and invoke th emethod - Object[] arr = new Object[] { RestorePoint }; - WMIClass.InvokeMethod("Restore", arr); - //Restore requires a Reboot and while reboot only the restore actually happens - //code to restart computer - mPath.Path = ComputerWMIHelper.WMI_Path_CIM; - scope.Path = mPath; - ManagementClass OsWMIClass = new ManagementClass(ComputerWMIHelper.WMI_Class_OperatingSystem); - OsWMIClass.Scope = scope; - ObjectQuery objQuery = new ObjectQuery("Select * from " + ComputerWMIHelper.WMI_Class_OperatingSystem); - using (ManagementObjectSearcher results = new ManagementObjectSearcher(scope, objQuery)) - { - foreach (ManagementObject mobj in results.Get()) - { - using (mobj) - { - string[] param = { "" }; - mobj.InvokeMethod("Reboot", param); - } - } - } - } - } - catch (ManagementException e) - { - if (e.ErrorCode.ToString().Equals("NotFound") || e.ErrorCode.ToString().Equals("InvalidClass")) - { - Exception Ex = new ArgumentException(StringUtil.Format(ComputerResources.NotSupported)); - WriteError(new ErrorRecord(Ex, "RestoreComputerNotSupported", ErrorCategory.InvalidOperation, null)); - } - else - { - ErrorRecord errorRecord = new ErrorRecord(e, "GetWMIManagementException", ErrorCategory.InvalidOperation, null); - WriteError(errorRecord); - } - } - catch (COMException e) - { - if (string.IsNullOrEmpty(e.Message)) - { - Exception Ex = new ArgumentException(StringUtil.Format(ComputerResources.SystemRestoreServiceDisabled)); - WriteError(new ErrorRecord(Ex, "ServiceDisabled", ErrorCategory.InvalidOperation, null)); - } - else - { - ErrorRecord errorRecord = new ErrorRecord(e, "COMException", ErrorCategory.InvalidOperation, null); - WriteError(errorRecord); - } - } - } - - /// - /// to implement ^C - /// - protected override void StopProcessing() - { - if (WMIClass != null) - { - WMIClass.Dispose(); - } - } - -#endregion overrides - } -#endregion - -#region Add-Computer - - /// - /// Options for joining a computer to a domain - /// - [Flags] - public enum JoinOptions - { - /// - /// Create account on the domain - /// - AccountCreate = 0x2, - - /// - /// Join operation is part of an upgrade - /// - Win9XUpgrade = 0x10, - - /// - /// Perform an unsecure join - /// - UnsecuredJoin = 0x40, - - /// - /// Indicate that the password passed to the join operation is the local machine account password, not a user password. - /// It's valid only for unsecure join - /// - PasswordPass = 0x80, - - /// - /// Writing SPN and DNSHostName attributes on the computer object should be deferred until the rename operation that - /// follows the join operation - /// - DeferSPNSet = 0x100, - - /// - /// Join the target machine with a new name queried from the registry. This options is used if the rename has been called prior - /// to rebooting the machine - /// - JoinWithNewName = 0x400, - - /// - /// Use a readonly domain controller - /// - JoinReadOnly = 0x800, - - /// - /// Invoke during install - /// - InstallInvoke = 0x40000 - } - - /// - /// Adds the specified computer(s) to the Domain or Work Group. If the account - /// does not already exist on the domain, it also creates one (see notes for - /// implementation details). - /// If the computer is already joined to a domain, it can be moved to a new - /// domain (see notes for implementation details). - /// - [SuppressMessage("Microsoft.PowerShell", "PS1004AcceptForceParameterWhenCallingShouldContinue")] - [Cmdlet(VerbsCommon.Add, "Computer", SupportsShouldProcess = true, DefaultParameterSetName = DomainParameterSet, - HelpUri = "https://go.microsoft.com/fwlink/?LinkID=135194", RemotingCapability = RemotingCapability.SupportedByCommand)] - [OutputType(typeof(ComputerChangeInfo))] - public class AddComputerCommand : PSCmdlet - { -#region parameter - - private const string DomainParameterSet = "Domain"; - private const string WorkgroupParameterSet = "Workgroup"; - - /// - /// Target computer names - /// - [Parameter(ValueFromPipeline = true, ValueFromPipelineByPropertyName = true)] - [ValidateNotNullOrEmpty] - [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] - public string[] ComputerName { get; set; } = { "localhost" }; - - /// - /// The local admin credential to the target computer - /// - [Parameter] - [Credential] - [ValidateNotNullOrEmpty] - public PSCredential LocalCredential { get; set; } - - /// - /// The domain credential used to unjoin a domain - /// - [Parameter(ParameterSetName = DomainParameterSet)] - [Credential] - [ValidateNotNullOrEmpty] - [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] - public PSCredential UnjoinDomainCredential { get; set; } - - /// - /// The domain credential. - /// In DomainParameterSet, it is for the domain to join to. - /// In WorkgroupParameterSet, it is for the domain to disjoin from. - /// - [Parameter(ParameterSetName = DomainParameterSet, Mandatory = true)] - [Parameter(ParameterSetName = WorkgroupParameterSet)] - [Alias("DomainCredential")] - [Credential] - [ValidateNotNullOrEmpty] - public PSCredential Credential { get; set; } - - /// - /// Name of the domain to join - /// - [Parameter(Position = 0, Mandatory = true, ParameterSetName = DomainParameterSet)] - [Alias("DN", "Domain")] - [ValidateNotNullOrEmpty] - public String DomainName { get; set; } - - /// - /// The organization unit (OU). It's the path on the AD under which the new account will - /// be created - /// - [Parameter(ParameterSetName = DomainParameterSet)] - [Alias("OU")] - [ValidateNotNullOrEmpty] - public string OUPath { get; set; } - - /// - /// The name of a domain controller that performs the add. - /// - [Parameter(ParameterSetName = DomainParameterSet)] - [Alias("DC")] - [ValidateNotNullOrEmpty] - public string Server { get; set; } - - /// - /// Perform an unsecure join. - /// - [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] - [Parameter(ParameterSetName = DomainParameterSet)] - public SwitchParameter Unsecure { get; set; } - - /// - /// Additional options for the "join domain" operation - /// - [Parameter(ParameterSetName = DomainParameterSet)] - public JoinOptions Options { get; set; } = JoinOptions.AccountCreate; - - /// - /// Name of the workgroup to join in. - /// - [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "WorkGroup")] - [Parameter(Position = 0, Mandatory = true, ParameterSetName = WorkgroupParameterSet)] - [Alias("WGN")] - [ValidateNotNullOrEmpty] - public string WorkgroupName { get; set; } - - /// - /// Restart the target computer - /// - [Parameter] - public SwitchParameter Restart { get; set; } = false; - - /// - /// Emit the output. - /// - [Parameter] - public SwitchParameter PassThru { get; set; } - - /// - /// New names for the target computers - /// - [Parameter(ValueFromPipelineByPropertyName = true)] - [ValidateNotNullOrEmpty] - public string NewName { get; set; } - - /// - /// To suppress ShouldContinue - /// - [Parameter] - public SwitchParameter Force - { - get { return _force; } - set { _force = value; } - } - private bool _force; - - private int _joinDomainflags = 1; - private bool _containsLocalHost = false; - private string _newNameForLocalHost = null; - - private readonly string _shortLocalMachineName = Dns.GetHostName(); - private readonly string _fullLocalMachineName = Dns.GetHostEntry("").HostName; - -#endregion parameter - -#region private - - /// - /// Unjoin the computer from its current domain - /// - /// In the DomainParameterSet, the UnjoinDomainCredential is our first choice to unjoin a domain. - /// But if the UnjoinDomainCredential is not specified, the DomainCredential will be our second - /// choice. This is to keep the backward compatibility. In Win7, we can do: - /// Add-Computer -DomainName domain1 -Credential $credForDomain1AndDomain2 - /// to switch the local machine that is currently in domain2 to domain1. - /// - /// Since DomainCredential has an alias "Credential", the same command should still work for the - /// new Add-Computer cmdlet. - /// - /// In the WorkgroupParameterSet, the UnjoinDomainCredential is the only choice. - /// - /// - /// - /// - /// - /// - /// - /// - private int UnjoinDomain(ManagementObject computerSystem, string computerName, string curDomainName, string dUserName, string dPassword) - { - ManagementBaseObject unjoinDomainParameter = computerSystem.GetMethodParameters("UnjoinDomainOrWorkgroup"); - unjoinDomainParameter.SetPropertyValue("UserName", dUserName); - unjoinDomainParameter.SetPropertyValue("Password", dPassword); - unjoinDomainParameter.SetPropertyValue("FUnjoinOptions", 4); // default option, disable the active directory - - ManagementBaseObject result = computerSystem.InvokeMethod("UnjoinDomainOrWorkgroup", unjoinDomainParameter, null); - Dbg.Diagnostics.Assert(result != null, "result cannot be null if the Unjoin method is invoked"); - int returnCode = Convert.ToInt32(result["ReturnValue"], CultureInfo.CurrentCulture); - if (returnCode != 0) - { - var ex = new Win32Exception(returnCode); - WriteErrorHelper(ComputerResources.FailToUnjoinDomain, "FailToUnjoinDomain", computerName, - ErrorCategory.OperationStopped, false, computerName, curDomainName, ex.Message); - } - return returnCode; - } - - /// - /// Join a domain from a workgroup - /// - /// - /// If a computer is already in a domain, we first unjoin it from its current domain, and - /// then do the join operation to the new domain. So when this method is invoked, we are - /// currently in a workgroup - /// - /// - /// - /// - /// - /// - private int JoinDomain(ManagementObject computerSystem, string computerName, string oldDomainName, string curWorkgroupName) - { - string joinDomainUserName = Credential != null ? Credential.UserName : null; - string joinDomainPassword = Credential != null ? Utils.GetStringFromSecureString(Credential.Password) : null; - - ManagementBaseObject joinDomainParameter = computerSystem.GetMethodParameters("JoinDomainOrWorkgroup"); - joinDomainParameter.SetPropertyValue("Name", DomainName); - joinDomainParameter.SetPropertyValue("UserName", joinDomainUserName); - joinDomainParameter.SetPropertyValue("Password", joinDomainPassword); - joinDomainParameter.SetPropertyValue("AccountOU", OUPath); - joinDomainParameter.SetPropertyValue("FJoinOptions", _joinDomainflags); - - ManagementBaseObject result = computerSystem.InvokeMethod("JoinDomainOrWorkgroup", joinDomainParameter, null); - Dbg.Diagnostics.Assert(result != null, "result cannot be null if the Join method is invoked"); - int returnCode = Convert.ToInt32(result["ReturnValue"], CultureInfo.CurrentCulture); - if (returnCode != 0) - { - var ex = new Win32Exception(returnCode); - string errMsg; - string errorId; - if (oldDomainName != null) - { - errMsg = StringUtil.Format(ComputerResources.FailToJoinNewDomainAfterUnjoinOldDomain, - computerName, oldDomainName, DomainName, ex.Message); - errorId = "FailToJoinNewDomainAfterUnjoinOldDomain"; - } - else - { - errMsg = StringUtil.Format(ComputerResources.FailToJoinDomainFromWorkgroup, computerName, - DomainName, curWorkgroupName, ex.Message); - errorId = "FailToJoinDomainFromWorkgroup"; - } - - WriteErrorHelper(errMsg, errorId, computerName, ErrorCategory.OperationStopped, false); - } - return returnCode; - } - - /// - /// Join in a new workgroup from the current workgroup - /// - /// - /// - /// - /// - private int JoinWorkgroup(ManagementObject computerSystem, string computerName, string oldDomainName) - { - ManagementBaseObject joinWorkgroupParam = computerSystem.GetMethodParameters("JoinDomainOrWorkgroup"); - joinWorkgroupParam.SetPropertyValue("Name", WorkgroupName); - joinWorkgroupParam.SetPropertyValue("UserName", null); - joinWorkgroupParam.SetPropertyValue("Password", null); - joinWorkgroupParam.SetPropertyValue("FJoinOptions", 0); // join a workgroup - - ManagementBaseObject result = computerSystem.InvokeMethod("JoinDomainOrWorkgroup", joinWorkgroupParam, null); - Dbg.Diagnostics.Assert(result != null, "result cannot be null if the Join method is invoked"); - int returnCode = Convert.ToInt32(result["ReturnValue"], CultureInfo.CurrentCulture); - if (returnCode != 0) - { - var ex = new Win32Exception(returnCode); - string errMsg; - if (oldDomainName != null) - { - errMsg = - StringUtil.Format(ComputerResources.FailToSwitchFromDomainToWorkgroup, computerName, - oldDomainName, WorkgroupName, ex.Message); - } - else - { - errMsg = StringUtil.Format(ComputerResources.FailToJoinWorkGroup, computerName, WorkgroupName, - ex.Message); - } - - WriteErrorHelper(errMsg, "FailToJoinWorkGroup", computerName, ErrorCategory.OperationStopped, false); - } - return returnCode; - } - - /// - /// Rename the computer in workgroup - /// - /// - /// - /// - /// - private int RenameComputer(ManagementObject computerSystem, string computerName, string newName) - { - string domainUserName = null; - string domainPassword = null; - - if (DomainName != null && Credential != null) - { - // The rename operation happens after the computer is joined to the new domain, so we should provide - // the domain user name and password to the rename operation - domainUserName = Credential.UserName; - domainPassword = Utils.GetStringFromSecureString(Credential.Password); - } - - ManagementBaseObject renameParameter = computerSystem.GetMethodParameters("Rename"); - renameParameter.SetPropertyValue("Name", newName); - renameParameter.SetPropertyValue("UserName", domainUserName); - renameParameter.SetPropertyValue("Password", domainPassword); - - ManagementBaseObject result = computerSystem.InvokeMethod("Rename", renameParameter, null); - Dbg.Diagnostics.Assert(result != null, "result cannot be null if the Rename method is invoked"); - int returnCode = Convert.ToInt32(result["ReturnValue"], CultureInfo.CurrentCulture); - if (returnCode != 0) - { - var ex = new Win32Exception(returnCode); - string errMsg; - string errorId; - if (WorkgroupName != null) - { - errMsg = StringUtil.Format(ComputerResources.FailToRenameAfterJoinWorkgroup, computerName, - WorkgroupName, newName, ex.Message); - errorId = "FailToRenameAfterJoinWorkgroup"; - } - else - { - errMsg = StringUtil.Format(ComputerResources.FailToRenameAfterJoinDomain, computerName, DomainName, - newName, ex.Message); - errorId = "FailToRenameAfterJoinDomain"; - } - - WriteErrorHelper(errMsg, errorId, computerName, ErrorCategory.OperationStopped, false); - } - return returnCode; - } - - /// - /// Helper method to write out non-terminating errors - /// - /// - /// - /// - /// - /// - /// - private void WriteErrorHelper(string resourceString, string errorId, object targetObj, ErrorCategory category, bool terminating, params object[] args) - { - string errMsg; - if (null == args || 0 == args.Length) - { - // Don't format in case the string contains literal curly braces - errMsg = resourceString; - } - else - { - errMsg = StringUtil.Format(resourceString, args); - } - - if (String.IsNullOrEmpty(errMsg)) - { - Dbg.Diagnostics.Assert(false, "Could not load text for error record '" + errorId + "'"); - } - - ErrorRecord error = new ErrorRecord(new InvalidOperationException(errMsg), errorId, - category, targetObj); - if (terminating) - { - ThrowTerminatingError(error); - } - else - { - WriteError(error); - } - } - - private void DoAddComputerAction(string computer, string newName, bool isLocalhost, ConnectionOptions options, EnumerationOptions enumOptions, ObjectQuery computerSystemQuery) - { - int returnCode = 0; - bool success = false; - string computerName = isLocalhost ? _shortLocalMachineName : computer; - - if (ParameterSetName == DomainParameterSet) - { - string action = StringUtil.Format(ComputerResources.AddComputerActionDomain, DomainName); - if (!ShouldProcess(computerName, action)) - { - return; - } - } - else - { - string action = StringUtil.Format(ComputerResources.AddComputerActionWorkgroup, WorkgroupName); - if (!ShouldProcess(computerName, action)) - { - return; - } - } - - // Check the length of the new name - if (newName != null && newName.Length > ComputerWMIHelper.NetBIOSNameMaxLength) - { - string truncatedName = newName.Substring(0, ComputerWMIHelper.NetBIOSNameMaxLength); - string query = StringUtil.Format(ComputerResources.TruncateNetBIOSName, truncatedName); - string caption = ComputerResources.TruncateNetBIOSNameCaption; - if (!Force && !ShouldContinue(query, caption)) - { - return; - } - } - - // If LocalCred is given, use the local credential for WMI connection. Otherwise, use - // the current caller's context (Username = null, Password = null) - if (LocalCredential != null) - { - options.SecurePassword = LocalCredential.Password; - options.Username = ComputerWMIHelper.GetLocalAdminUserName(computerName, LocalCredential); - } - - // The local machine will always be processed in the very end. If the - // current target computer is the local machine, it's the last one to - // be processed, so we can safely set the Username and Password to be - // null. We cannot use a user credential when connecting to the local - // machine. - if (isLocalhost) - { - options.Username = null; - options.SecurePassword = null; - } - - ManagementScope scope = new ManagementScope(ComputerWMIHelper.GetScopeString(computer, ComputerWMIHelper.WMI_Path_CIM), options); - - try - { - using (var searcher = new ManagementObjectSearcher(scope, computerSystemQuery, enumOptions)) - { - foreach (ManagementObject computerSystem in searcher.Get()) - { - using (computerSystem) - { - // If we are not using the new computer name, check the length of the target machine name - string hostName = (string)computerSystem["DNSHostName"]; - if (newName == null && hostName.Length > ComputerWMIHelper.NetBIOSNameMaxLength) - { - string truncatedName = hostName.Substring(0, ComputerWMIHelper.NetBIOSNameMaxLength); - string query = StringUtil.Format(ComputerResources.TruncateNetBIOSName, truncatedName); - string caption = ComputerResources.TruncateNetBIOSNameCaption; - if (!Force && !ShouldContinue(query, caption)) - { - continue; - } - } - - if (newName != null && hostName.Equals(newName, StringComparison.OrdinalIgnoreCase)) - { - WriteErrorHelper( - ComputerResources.NewNameIsOldName, - "NewNameIsOldName", - newName, - ErrorCategory.InvalidArgument, - false, - computerName, newName); - continue; - } - - if (ParameterSetName == DomainParameterSet) - { - if ((bool)computerSystem["PartOfDomain"]) - { - string curDomainName = (string)LanguagePrimitives.ConvertTo(computerSystem["Domain"], typeof(string), CultureInfo.InvariantCulture); - string shortDomainName = ""; - if (curDomainName.Contains(".")) - { - int dotIndex = curDomainName.IndexOf(".", StringComparison.OrdinalIgnoreCase); - shortDomainName = curDomainName.Substring(0, dotIndex); - } - - // If the target computer is already in the specified domain, throw an error - if (curDomainName.Equals(DomainName, StringComparison.OrdinalIgnoreCase) || shortDomainName.Equals(DomainName, StringComparison.OrdinalIgnoreCase)) - { - WriteErrorHelper(ComputerResources.AddComputerToSameDomain, - "AddComputerToSameDomain", computerName, - ErrorCategory.InvalidOperation, false, computerName, DomainName); - continue; - } - - // Switch between domains - // If the UnjoinDomainCredential is not specified, we assume the DomainCredential can be used for both removing - // the computer from its current domain, and adding the computer to the new domain. This behavior is supported on - // Win7, we don't want to break it. - PSCredential credTobeUsed = UnjoinDomainCredential ?? Credential; - string unjoinDomainUserName = credTobeUsed != null ? credTobeUsed.UserName : null; - string unjoinDomainPassword = credTobeUsed != null ? Utils.GetStringFromSecureString(credTobeUsed.Password) : null; - - // Leave the current domain - returnCode = UnjoinDomain(computerSystem, computerName, curDomainName, unjoinDomainUserName, unjoinDomainPassword); - if (returnCode == 0) - { - // Successfully unjoin the old domain, join the computer to the new domain - returnCode = JoinDomain(computerSystem, computerName, curDomainName, null); - - if (returnCode == 0 && newName != null) - { - // Rename the computer in the new domain - returnCode = RenameComputer(computerSystem, computerName, newName); - } - } - - success = returnCode == 0; - } - else - { - // Add a workgroup computer to domain - string curWorkgroupName = (string)LanguagePrimitives.ConvertTo(computerSystem["Domain"], typeof(string), CultureInfo.InvariantCulture); - returnCode = JoinDomain(computerSystem, computerName, null, curWorkgroupName); - - if (returnCode == 0 && newName != null) - { - returnCode = RenameComputer(computerSystem, computerName, newName); - } - - success = returnCode == 0; - } - } - else // WorkgroupParameterSet - { - if ((bool)computerSystem["PartOfDomain"]) - { - // Remind the user to have local admin credential only if the computer is domain joined - string shouldContinueMsg = ComputerResources.RemoveComputerConfirm; - if (!Force && !ShouldContinue(shouldContinueMsg, null /* null = default caption */ )) - { - continue; - } - - // Leave the current domain - string curDomainName = (string)LanguagePrimitives.ConvertTo(computerSystem["Domain"], typeof(string), CultureInfo.InvariantCulture); - string dUserName = Credential != null ? Credential.UserName : null; - string dPassword = Credential != null ? Utils.GetStringFromSecureString(Credential.Password) : null; - - returnCode = UnjoinDomain(computerSystem, computerName, curDomainName, dUserName, dPassword); - if (returnCode == 0) - { - // Join the specified workgroup - returnCode = JoinWorkgroup(computerSystem, computerName, curDomainName); - if (returnCode == 0 && newName != null) - { - // Rename the computer - returnCode = RenameComputer(computerSystem, computerName, newName); - } - } - - success = returnCode == 0; - } - else // in workgroup - { - string curWorkgroup = (string)LanguagePrimitives.ConvertTo(computerSystem["Domain"], typeof(string), CultureInfo.InvariantCulture); - if (curWorkgroup.Equals(WorkgroupName, StringComparison.OrdinalIgnoreCase)) - { - WriteErrorHelper(ComputerResources.AddComputerToSameWorkgroup, - "AddComputerToSameWorkgroup", computerName, - ErrorCategory.InvalidOperation, false, computerName, WorkgroupName); - continue; - } - - // Join to another workgroup - returnCode = JoinWorkgroup(computerSystem, computerName, null); - if (returnCode == 0 && newName != null) - { - returnCode = RenameComputer(computerSystem, computerName, newName); - } - - success = returnCode == 0; - } - }// end of else -- WorkgroupParameterSet - - if (PassThru) - { - WriteObject(ComputerWMIHelper.GetComputerStatusObject(returnCode, computerName)); - } - } - }// end of foreach - - // If successful and the Restart parameter is specified, restart the computer - if (success && Restart) - { - object[] flags = new object[] { 6, 0 }; - RestartComputerCommand.RestartOneComputerUsingDcom(this, isLocalhost, computerName, flags, options); - } - - // If successful and the Restart parameter is not specified, write out warning - if (success && !Restart) - { - WriteWarning(StringUtil.Format(ComputerResources.RestartNeeded, null, computerName)); - } - } - } // end of try - catch (ManagementException ex) - { - WriteErrorHelper(ComputerResources.FailToConnectToComputer, "AddComputerException", computerName, - ErrorCategory.OperationStopped, false, computerName, ex.Message); - } - catch (COMException ex) - { - WriteErrorHelper(ComputerResources.FailToConnectToComputer, "AddComputerException", computerName, - ErrorCategory.OperationStopped, false, computerName, ex.Message); - } - catch (UnauthorizedAccessException ex) - { - WriteErrorHelper(ComputerResources.FailToConnectToComputer, "AddComputerException", computerName, - ErrorCategory.OperationStopped, false, computerName, ex.Message); - } - } - - private string ValidateComputerName(string computer, bool validateNewName) - { - ErrorRecord error = null; - string targetComputer = ComputerWMIHelper.ValidateComputerName(computer, _shortLocalMachineName, _fullLocalMachineName, ref error); - if (targetComputer == null) - { - if (error != null) - { - WriteError(error); - } - return null; - } - - bool isLocalhost = targetComputer.Equals(ComputerWMIHelper.localhostStr, StringComparison.OrdinalIgnoreCase); - - if (validateNewName && NewName != null) - { - if (!ComputerWMIHelper.IsComputerNameValid(NewName)) - { - WriteErrorHelper( - ComputerResources.InvalidNewName, - "InvalidNewName", - NewName, - ErrorCategory.InvalidArgument, - false, - isLocalhost ? _shortLocalMachineName : targetComputer, NewName); - - return null; - } - } - - return targetComputer; - } - -#endregion private - -#region override - - /// - /// BeginProcessing method - /// - protected override void BeginProcessing() - { - if (ParameterSetName == DomainParameterSet) - { - if ((Options & JoinOptions.PasswordPass) != 0 && (Options & JoinOptions.UnsecuredJoin) == 0) - { - WriteErrorHelper(ComputerResources.InvalidJoinOptions, "InvalidJoinOptions", Options, - ErrorCategory.InvalidArgument, true, JoinOptions.PasswordPass.ToString(), - JoinOptions.UnsecuredJoin.ToString()); - } - - if ((Options & JoinOptions.AccountCreate) != 0) - { - _joinDomainflags |= (int)JoinOptions.AccountCreate; - } - if ((Options & JoinOptions.Win9XUpgrade) != 0) - { - _joinDomainflags |= (int)JoinOptions.Win9XUpgrade; - } - if ((Options & JoinOptions.UnsecuredJoin) != 0) - { - _joinDomainflags |= (int)JoinOptions.UnsecuredJoin; - } - if ((Options & JoinOptions.PasswordPass) != 0) - { - _joinDomainflags |= (int)JoinOptions.PasswordPass; - } - if ((Options & JoinOptions.DeferSPNSet) != 0) - { - _joinDomainflags |= (int)JoinOptions.DeferSPNSet; - } - if ((Options & JoinOptions.JoinWithNewName) != 0) - { - _joinDomainflags |= (int)JoinOptions.JoinWithNewName; - } - if ((Options & JoinOptions.JoinReadOnly) != 0) - { - _joinDomainflags |= (int)JoinOptions.JoinReadOnly; - } - if ((Options & JoinOptions.InstallInvoke) != 0) - { - _joinDomainflags |= (int)JoinOptions.InstallInvoke; - } - - if (Unsecure) - { - _joinDomainflags |= (int)(JoinOptions.UnsecuredJoin | JoinOptions.PasswordPass); - } - - if (Server != null) - { - // It's the name of a domain controller. We need to check if the specified domain controller actually exists - try - { - Dns.GetHostEntry(Server); - } - catch (Exception) - { - WriteErrorHelper(ComputerResources.CannotResolveServerName, "AddressResolutionException", - Server, ErrorCategory.InvalidArgument, true, Server); - } - DomainName = DomainName + "\\" + Server; - } - } - } - - /// - /// ProcessRecord method - /// - protected override void ProcessRecord() - { - if (NewName != null && ComputerName.Length != 1) - { - WriteErrorHelper(ComputerResources.CannotRenameMultipleComputers, "CannotRenameMultipleComputers", - NewName, ErrorCategory.InvalidArgument, false); - return; - } - - // If LocalCred is not provided, we use the current caller's context - ConnectionOptions options = new ConnectionOptions - { - Authentication = AuthenticationLevel.PacketPrivacy, - Impersonation = ImpersonationLevel.Impersonate, - EnablePrivileges = true - }; - - EnumerationOptions enumOptions = new EnumerationOptions { UseAmendedQualifiers = true, DirectRead = true }; - ObjectQuery computerSystemQuery = new ObjectQuery("select * from " + ComputerWMIHelper.WMI_Class_ComputerSystem); - - int oldJoinDomainFlags = _joinDomainflags; - if (NewName != null && ParameterSetName == DomainParameterSet) - { - // We rename the computer after it's joined to the target domain, so writing SPN and DNSHostName attributes - // on the computer object should be deferred until the rename operation that follows the join operation - _joinDomainflags |= (int)JoinOptions.DeferSPNSet; - } - - try - { - foreach (string computer in ComputerName) - { - string targetComputer = ValidateComputerName(computer, NewName != null); - if (targetComputer == null) - { - continue; - } - - bool isLocalhost = targetComputer.Equals("localhost", StringComparison.OrdinalIgnoreCase); - if (isLocalhost) - { - if (!_containsLocalHost) - _containsLocalHost = true; - _newNameForLocalHost = NewName; - - continue; - } - - DoAddComputerAction(targetComputer, NewName, false, options, enumOptions, computerSystemQuery); - }// end of foreach - } - finally - { - // Reverting the domainflags to previous status if DeferSPNSet is added to the domain flags. - if (NewName != null && ParameterSetName == DomainParameterSet) - { - _joinDomainflags = oldJoinDomainFlags; - } - } - }// end of ProcessRecord - - /// - /// EndProcessing method - /// - protected override void EndProcessing() - { - if (!_containsLocalHost) return; - - // If LocalCred is not provided, we use the current caller's context - ConnectionOptions options = new ConnectionOptions - { - Authentication = AuthenticationLevel.PacketPrivacy, - Impersonation = ImpersonationLevel.Impersonate, - EnablePrivileges = true - }; - - EnumerationOptions enumOptions = new EnumerationOptions { UseAmendedQualifiers = true, DirectRead = true }; - ObjectQuery computerSystemQuery = new ObjectQuery("select * from " + ComputerWMIHelper.WMI_Class_ComputerSystem); - - DoAddComputerAction("localhost", _newNameForLocalHost, true, options, enumOptions, computerSystemQuery); - } - -#endregion override - }//End Class - -#endregion Add-Computer - -#region RemoveComputer - - /// - /// Removes the Specified Computer(s) from the relevant Domain or Work Group - /// - - [Cmdlet(VerbsCommon.Remove, "Computer", SupportsShouldProcess = true, DefaultParameterSetName = LocalParameterSet, - HelpUri = "https://go.microsoft.com/fwlink/?LinkID=135246", RemotingCapability = RemotingCapability.SupportedByCommand)] - [OutputType(typeof(ComputerChangeInfo))] - public class RemoveComputerCommand : PSCmdlet - { -#region "Parameters and Private Data" - - private const string LocalParameterSet = "Local"; - private const string RemoteParameterSet = "Remote"; - - /// - /// The domain credential is used for authenticating to the domain controller. - /// - [Parameter(ParameterSetName = RemoteParameterSet, Mandatory = true)] - [Parameter(Position = 0, ParameterSetName = LocalParameterSet)] - [Alias("Credential")] - [ValidateNotNullOrEmpty] - [Credential] - [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] - public PSCredential UnjoinDomainCredential { get; set; } - - /// - /// The local admin credential for authenticating to the target computer - /// - [Parameter(ParameterSetName = RemoteParameterSet)] - [ValidateNotNullOrEmpty] - [Credential] - public PSCredential LocalCredential { get; set; } - - /// - /// Restart parameter - /// - [Parameter] - public SwitchParameter Restart { get; set; } = false; - - /// - /// The target computer names to remove from the domain - /// - [Parameter(ParameterSetName = RemoteParameterSet, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true)] - [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] - public string[] ComputerName { get; set; } = { "localhost" }; - - /// - /// Force parameter (to suppress the shouldprocess and shouldcontinue) - /// - [Parameter] - public SwitchParameter Force - { - get { return _force; } - set { _force = value; } - } - private bool _force; - - /// - /// Only emit if passthru is specified. One bool/string pair for each - /// computer that was joined. Bool = success/failure. String = ComputerName. - /// - [Parameter] - public SwitchParameter PassThru { get; set; } - - /// - /// Specify the workgroup name to join in if the target machine is removed from - /// the domain - /// - [Parameter] - [ValidateNotNullOrEmpty] - public string WorkgroupName { get; set; } = "WORKGROUP"; - - private bool _containsLocalHost = false; - private readonly string _shortLocalMachineName = Dns.GetHostName(); - private readonly string _fullLocalMachineName = Dns.GetHostEntry("").HostName; - -#endregion "Parameters and Private Data" - -#region "Private Methods" - - private void DoRemoveComputerAction(string computer, bool isLocalhost, ConnectionOptions options, EnumerationOptions enumOptions, ObjectQuery computerSystemQuery) - { - bool successful = false; - string computerName = isLocalhost ? _shortLocalMachineName : computer; - - if (!ShouldProcess(computerName)) - { - return; - } - - // If LocalCred is given, use the local credential for WMI connection - if (LocalCredential != null) - { - options.SecurePassword = LocalCredential.Password; - options.Username = ComputerWMIHelper.GetLocalAdminUserName(computerName, LocalCredential); - } - - // The local machine will always be processed in the very end. If the - // current target computer is the local machine, it's the last one to - // be processed, so we can safely set the Username and Password to be - // null. We cannot use a user credential when connecting to the local - // machine. - if (isLocalhost) - { - options.Username = null; - options.SecurePassword = null; - } - - ManagementScope scope = new ManagementScope(ComputerWMIHelper.GetScopeString(computer, ComputerWMIHelper.WMI_Path_CIM), options); - try - { - using (var searcher = new ManagementObjectSearcher(scope, computerSystemQuery, enumOptions)) - { - foreach (ManagementObject computerSystem in searcher.Get()) - { - using (computerSystem) - { - if (!(bool)computerSystem["PartOfDomain"]) - { - // Not in a domain, throw out non-terminating error - string errMsg = StringUtil.Format(ComputerResources.ComputerNotInDomain, computerName); - ErrorRecord error = new ErrorRecord(new InvalidOperationException(errMsg), "ComputerNotInDomain", ErrorCategory.InvalidOperation, computerName); - WriteError(error); - continue; - } - - // Remind the user to have local admin credential only if the computer is domain joined - string shouldContinueMsg = ComputerResources.RemoveComputerConfirm; - if (!Force && !ShouldContinue(shouldContinueMsg, null /* null = default caption */ )) - { - continue; - } - - int returnCode = 0; - string curDomainName = (string)LanguagePrimitives.ConvertTo(computerSystem["Domain"], typeof(string), CultureInfo.InvariantCulture); - string dUserName = UnjoinDomainCredential != null ? UnjoinDomainCredential.UserName : null; - string dPassword = UnjoinDomainCredential != null ? Utils.GetStringFromSecureString(UnjoinDomainCredential.Password) : null; - - ManagementBaseObject unjoinParameter = computerSystem.GetMethodParameters("UnjoinDomainOrWorkgroup"); - unjoinParameter.SetPropertyValue("UserName", dUserName); - unjoinParameter.SetPropertyValue("Password", dPassword); - unjoinParameter.SetPropertyValue("FUnjoinOptions", 4); // default option, disable the active directory account - - ManagementBaseObject result = computerSystem.InvokeMethod("UnjoinDomainOrWorkgroup", unjoinParameter, null); - Dbg.Diagnostics.Assert(result != null, "result cannot be null if the Unjoin method is invoked"); - returnCode = Convert.ToInt32(result["ReturnValue"], CultureInfo.CurrentCulture); - - // Error code 1355 - The specified domain either does not exist or could not be contacted. - // This might happen when the old domain is gone or unreachable. - // Error code 53 - The network path was not found. - // This might happen when the network is not available. - if ((returnCode == 1355 || returnCode == 53) && Force) - { - // When -Force is specified, we unjoin the domain without disable the AD account - unjoinParameter.SetPropertyValue("FUnjoinOptions", 0); - result = computerSystem.InvokeMethod("UnjoinDomainOrWorkgroup", unjoinParameter, null); - Dbg.Diagnostics.Assert(result != null, "result cannot be null if the Unjoin method is invoked"); - returnCode = Convert.ToInt32(result["ReturnValue"], CultureInfo.CurrentCulture); - } - - if (returnCode != 0) - { - var ex = new Win32Exception(returnCode); - string errMsg = StringUtil.Format(ComputerResources.FailToUnjoinDomain, computerName, curDomainName, ex.Message); - ErrorRecord error = new ErrorRecord(new InvalidOperationException(errMsg), "FailToUnjoinDomain", ErrorCategory.OperationStopped, computerName); - WriteError(error); - } - else - { - // Join into the specified workgroup if it's given - successful = true; - if (WorkgroupName != null) - { - ManagementBaseObject joinParameter = computerSystem.GetMethodParameters("JoinDomainOrWorkgroup"); - joinParameter.SetPropertyValue("Name", WorkgroupName); - joinParameter.SetPropertyValue("Password", null); - joinParameter.SetPropertyValue("UserName", null); - joinParameter.SetPropertyValue("FJoinOptions", 0); // Join in a workgroup - - result = computerSystem.InvokeMethod("JoinDomainOrWorkgroup", joinParameter, null); - Dbg.Diagnostics.Assert(result != null, "result cannot be null if the Join method is invoked"); - returnCode = Convert.ToInt32(result["ReturnValue"], CultureInfo.CurrentCulture); - if (returnCode != 0) - { - var ex = new Win32Exception(returnCode); - string errMsg = StringUtil.Format(ComputerResources.FailToSwitchFromDomainToWorkgroup, computerName, curDomainName, WorkgroupName, ex.Message); - ErrorRecord error = new ErrorRecord(new InvalidOperationException(errMsg), "FailToJoinWorkGroup", ErrorCategory.OperationStopped, computerName); - WriteError(error); - } - } - } - - if (PassThru) - { - WriteObject(ComputerWMIHelper.GetComputerStatusObject(returnCode, computerName)); - } - } - } - } - - // If successful and the Restart parameter is specified, restart the computer - if (successful && Restart) - { - object[] flags = new object[] { 6, 0 }; - RestartComputerCommand.RestartOneComputerUsingDcom(this, isLocalhost, computerName, flags, options); - } - - // If successful and the Restart parameter is not specified, write out warning - if (successful && !Restart) - { - WriteWarning(StringUtil.Format(ComputerResources.RestartNeeded, null, computerName)); - } - } - catch (ManagementException ex) - { - string errMsg = StringUtil.Format(ComputerResources.FailToConnectToComputer, computerName, ex.Message); - ErrorRecord error = new ErrorRecord(new InvalidOperationException(errMsg), "RemoveComputerException", ErrorCategory.OperationStopped, computerName); - WriteError(error); - } - catch (COMException ex) - { - string errMsg = StringUtil.Format(ComputerResources.FailToConnectToComputer, computerName, ex.Message); - ErrorRecord error = new ErrorRecord(new InvalidOperationException(errMsg), "RemoveComputerException", ErrorCategory.OperationStopped, computerName); - WriteError(error); - } - catch (UnauthorizedAccessException ex) - { - string errMsg = StringUtil.Format(ComputerResources.FailToConnectToComputer, computerName, ex.Message); - ErrorRecord error = new ErrorRecord(new InvalidOperationException(errMsg), "RemoveComputerException", ErrorCategory.OperationStopped, computerName); - WriteError(error); - } - } - - private string ValidateComputerName(string computer) - { - ErrorRecord error = null; - string targetComputer = ComputerWMIHelper.ValidateComputerName(computer, _shortLocalMachineName, _fullLocalMachineName, ref error); - if (targetComputer == null) - { - if (error != null) - { - WriteError(error); - } - return null; - } - - return targetComputer; - } - -#endregion "Private Methods" - -#region "Override Methods" - - /// - /// ProcessRecord method. - /// - protected override void ProcessRecord() - { - // If both LocalCred and DomainCred are not provided, we use the default options - ConnectionOptions options = new ConnectionOptions - { - Authentication = AuthenticationLevel.PacketPrivacy, - Impersonation = ImpersonationLevel.Impersonate, - EnablePrivileges = true - }; - - // For Remove-Computer, usually the domain credential is also the local admin credential - // for the target computer. So use the local credential if given, otherwise use the domain - // credential to connect to the target machine. - // If the LocalCred is not given but the DomainCred is available, use the DomainCred for WMI connection. - if (LocalCredential == null && UnjoinDomainCredential != null) - { - options.SecurePassword = UnjoinDomainCredential.Password; - options.Username = UnjoinDomainCredential.UserName; - } - - EnumerationOptions enumOptions = new EnumerationOptions { UseAmendedQualifiers = true, DirectRead = true }; - ObjectQuery computerSystemQuery = new ObjectQuery("select * from " + ComputerWMIHelper.WMI_Class_ComputerSystem); - - foreach (string computer in ComputerName) - { - string targetComputer = ValidateComputerName(computer); - if (targetComputer == null) - { - continue; - } - - bool isLocalhost = targetComputer.Equals("localhost", StringComparison.OrdinalIgnoreCase); - if (isLocalhost) - { - if (!_containsLocalHost) - _containsLocalHost = true; - - continue; - } - - DoRemoveComputerAction(computer, false, options, enumOptions, computerSystemQuery); - } - }//End ProcessRecord() - - /// - /// EndProcessing method: deal with the local computer in the end - /// - protected override void EndProcessing() - { - if (!_containsLocalHost) return; - - // If both LocalCred and DomainCred are not provided, we use the default options - ConnectionOptions options = new ConnectionOptions - { - Authentication = AuthenticationLevel.PacketPrivacy, - Impersonation = ImpersonationLevel.Impersonate, - EnablePrivileges = true - }; - - // For Remove-Computer, usually the domain credential is also the local admin credential - // for the target computer. So use the local credential if given, otherwise use the domain - // credential to connect to the target machine. - // If the LocalCred is not given but the DomainCred is available, use the DomainCred for WMI connection. - if (LocalCredential == null && UnjoinDomainCredential != null) - { - options.SecurePassword = UnjoinDomainCredential.Password; - options.Username = UnjoinDomainCredential.UserName; - } - - EnumerationOptions enumOptions = new EnumerationOptions { UseAmendedQualifiers = true, DirectRead = true }; - ObjectQuery computerSystemQuery = new ObjectQuery("select * from " + ComputerWMIHelper.WMI_Class_ComputerSystem); - - DoRemoveComputerAction("localhost", true, options, enumOptions, computerSystemQuery); - } - -#endregion "Override Methods" - }//End Class - -#endregion Remove-Computer - -#endif - -#region Rename-Computer - - /// - /// Renames a domain computer and its corresponding domain account or a - /// workgroup computer. Use this command to rename domain workstations and local - /// machines only. It cannot be used to rename Domain Controllers. - /// - - [Cmdlet(VerbsCommon.Rename, "Computer", SupportsShouldProcess = true, - HelpUri = "https://go.microsoft.com/fwlink/?LinkID=219990", RemotingCapability = RemotingCapability.SupportedByCommand)] - public class RenameComputerCommand : PSCmdlet - { -#region Private Members - - private bool _containsLocalHost = false; - private string _newNameForLocalHost = null; - - private TransportProtocol _transportProtocol = TransportProtocol.DCOM; - - private readonly string _shortLocalMachineName = Dns.GetHostName(); - private readonly string _fullLocalMachineName = Dns.GetHostEntryAsync("").Result.HostName; - -#endregion - -#region Parameters - - /// - /// Target computers to rename - /// - [Parameter(ValueFromPipelineByPropertyName = true)] - [ValidateNotNullOrEmpty] - public string ComputerName { get; set; } = "localhost"; - - /// - /// Emit the output. - /// - //[Alias("Restart")] - [Parameter] - public SwitchParameter PassThru { get; set; } - - /// - /// The domain credential of the domain the target computer joined - /// - [Parameter] - [ValidateNotNullOrEmpty] - [Credential] - public PSCredential DomainCredential { get; set; } - - /// - /// The administrator credential of the target computer - /// - [Parameter] - [ValidateNotNullOrEmpty] - [Credential] - public PSCredential LocalCredential { get; set; } - - /// - /// New names for the target computers - /// - [Parameter(Mandatory = true, Position = 0, ValueFromPipelineByPropertyName = true)] - [ValidateNotNullOrEmpty] - public string NewName { get; set; } - - /// - /// Suppress the ShouldContinue - /// - [Parameter] - public SwitchParameter Force - { - get { return _force; } - set { _force = value; } - } - private bool _force; - - /// - /// To restart the target computer after rename it - /// - [Parameter] - public SwitchParameter Restart - { - get { return _restart; } - set { _restart = value; } - } - private bool _restart; - - /// - /// The authentication options for CIM_WSMan connection - /// - [Parameter] - [ValidateSet( - "Default", - "Basic", - "Negotiate", // can be used with and without credential (without -> PSRP mapped to NegotiateWithImplicitCredential) - "CredSSP", - "Digest", - "Kerberos")] // can be used with and without credential (not sure about implications) - [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] - public string WsmanAuthentication { get; set; } = "Default"; - - /// - /// Specify the protocol to use - /// - [Parameter] - [ValidateSet(ComputerWMIHelper.DcomProtocol, ComputerWMIHelper.WsmanProtocol)] - public string Protocol { get; set; } = -#if CORECLR - //CoreClr does not support DCOM protocol - // This change makes sure that the the command works seamlessly if user did not explicitly entered the protocol - ComputerWMIHelper.WsmanProtocol; -#else - ComputerWMIHelper.DcomProtocol; -#endif - -#endregion - -#region "Private Methods" - - /// - /// Check to see if the target computer is the local machine - /// - private string ValidateComputerName() - { - // Validate target name. - ErrorRecord targetError = null; - string targetComputer = ComputerWMIHelper.ValidateComputerName(ComputerName, _shortLocalMachineName, _fullLocalMachineName, ref targetError); - if (targetComputer == null) - { - if (targetError != null) - { - WriteError(targetError); - } - return null; - } - - // Validate *new* name. Validate the format of the new name. Check if the old name is the same as the - // new name later. - if (!ComputerWMIHelper.IsComputerNameValid(NewName)) - { - bool isLocalhost = targetComputer.Equals(ComputerWMIHelper.localhostStr, StringComparison.OrdinalIgnoreCase); - string errMsg = StringUtil.Format(ComputerResources.InvalidNewName, isLocalhost ? _shortLocalMachineName : targetComputer, NewName); - ErrorRecord error = new ErrorRecord( - new InvalidOperationException(errMsg), "InvalidNewName", - ErrorCategory.InvalidArgument, NewName); - WriteError(error); - return null; - } - - return targetComputer; - } - - private void DoRenameComputerAction(string computer, string newName, bool isLocalhost) - { - string computerName = isLocalhost ? _shortLocalMachineName : computer; - - if (!ShouldProcess(computerName)) - { - return; - } - - // Check the length of the new name - if (newName != null && newName.Length > ComputerWMIHelper.NetBIOSNameMaxLength) - { - string truncatedName = newName.Substring(0, ComputerWMIHelper.NetBIOSNameMaxLength); - string query = StringUtil.Format(ComputerResources.TruncateNetBIOSName, truncatedName); - string caption = ComputerResources.TruncateNetBIOSNameCaption; - if (!Force && !ShouldContinue(query, caption)) - { - return; - } - } - - switch (_transportProtocol) - { - case TransportProtocol.WSMan: - DoRenameComputerWsman(computer, computerName, newName, isLocalhost); - break; -#if !CORECLR - case TransportProtocol.DCOM: - DoRenameComputerDcom(computer, computerName, newName, isLocalhost); - break; -#endif - } - } - - private void DoRenameComputerWsman(string computer, string computerName, string newName, bool isLocalhost) - { - bool successful = false; - PSCredential credToUse = isLocalhost ? null : (LocalCredential ?? DomainCredential); - - try - { - using (CancellationTokenSource cancelTokenSource = new CancellationTokenSource()) - using (CimSession cimSession = RemoteDiscoveryHelper.CreateCimSession(computer, credToUse, WsmanAuthentication, cancelTokenSource.Token, this)) - { - var operationOptions = new CimOperationOptions - { - Timeout = TimeSpan.FromMilliseconds(10000), - CancellationToken = cancelTokenSource.Token, - //This prefix works against all versions of the WinRM server stack, both win8 and win7 - ResourceUriPrefix = new Uri(ComputerWMIHelper.CimUriPrefix) - }; - - IEnumerable mCollection = cimSession.QueryInstances( - ComputerWMIHelper.CimOperatingSystemNamespace, - ComputerWMIHelper.CimQueryDialect, - "Select * from " + ComputerWMIHelper.WMI_Class_ComputerSystem, - operationOptions); - - foreach (CimInstance cimInstance in mCollection) - { - var oldName = cimInstance.CimInstanceProperties["DNSHostName"].Value.ToString(); - if (oldName.Equals(newName, StringComparison.OrdinalIgnoreCase)) - { - string errMsg = StringUtil.Format(ComputerResources.NewNameIsOldName, computerName, newName); - ErrorRecord error = new ErrorRecord( - new InvalidOperationException(errMsg), "NewNameIsOldName", - ErrorCategory.InvalidArgument, newName); - WriteError(error); - - continue; - } - - // If the target computer is in a domain, always use the DomainCred. If the DomainCred is not given, - // use null for UserName and Password, so the context of the caller will be used. - // If the target computer is not in a domain, just use null for the UserName and Password - string dUserName = null; - string dPassword = null; - if (((bool)cimInstance.CimInstanceProperties["PartOfDomain"].Value) && (DomainCredential != null)) - { - dUserName = DomainCredential.UserName; - dPassword = Utils.GetStringFromSecureString(DomainCredential.Password); - } - - var methodParameters = new CimMethodParametersCollection(); - methodParameters.Add(CimMethodParameter.Create( - "Name", - newName, - Microsoft.Management.Infrastructure.CimType.String, - CimFlags.None)); - - methodParameters.Add(CimMethodParameter.Create( - "UserName", - dUserName, - Microsoft.Management.Infrastructure.CimType.String, - (dUserName == null) ? CimFlags.NullValue : CimFlags.None)); - - methodParameters.Add( - CimMethodParameter.Create( - "Password", - dPassword, - Microsoft.Management.Infrastructure.CimType.String, - (dPassword == null) ? CimFlags.NullValue : CimFlags.None)); - - CimMethodResult result = cimSession.InvokeMethod( - ComputerWMIHelper.CimOperatingSystemNamespace, - cimInstance, - "Rename", - methodParameters, - operationOptions); - - int retVal = Convert.ToInt32(result.ReturnValue.Value, CultureInfo.CurrentCulture); - if (retVal != 0) - { - var ex = new Win32Exception(retVal); - string errMsg = StringUtil.Format(ComputerResources.FailToRename, computerName, newName, ex.Message); - ErrorRecord error = new ErrorRecord(new InvalidOperationException(errMsg), "FailToRenameComputer", ErrorCategory.OperationStopped, computerName); - WriteError(error); - } - else - { - successful = true; - } - - if (PassThru) + // Check if the restart completes + switch (_waitFor) { - WriteObject(ComputerWMIHelper.GetRenameComputerStatusObject(retVal, newName, computerName)); + case WaitForServiceTypes.Wmi: + waitComplete = (winrmTestList.Count == _waitOnComputers.Count); + machineCompleteRestart = winrmTestList.Count; + break; + case WaitForServiceTypes.WinRM: + waitComplete = (psTestList.Count == _waitOnComputers.Count); + machineCompleteRestart = psTestList.Count; + break; + case WaitForServiceTypes.PowerShell: + waitComplete = (allDoneList.Count == _waitOnComputers.Count); + machineCompleteRestart = allDoneList.Count; + break; } - if (successful) + // Wait is done or time is up + if (waitComplete || _exit) { - if (_restart) - { - // If successful and the Restart parameter is specified, restart the computer - object[] flags = new object[] { 6, 0 }; - ComputerWMIHelper.InvokeWin32ShutdownUsingWsman( - this, - isLocalhost, - computerName, - flags, - credToUse, - WsmanAuthentication, - ComputerResources.RestartcomputerFailed, - "RestartcomputerFailed", - cancelTokenSource.Token); - } - else + if (waitComplete) { - WriteWarning(StringUtil.Format(ComputerResources.RestartNeeded, null, computerName)); + _status = ComputerResources.RestartComplete; + WriteProgress(_indicator[indicatorIndex % 4] + _activity, _status, 100, ProgressRecordType.Completed); + _timer.Change(System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite); } - } - } // end foreach - } // end using - } - catch (CimException ex) - { - string errMsg = StringUtil.Format(ComputerResources.FailToConnectToComputer, computerName, ex.Message); - ErrorRecord error = new ErrorRecord(new InvalidOperationException(errMsg), "RenameComputerException", - ErrorCategory.OperationStopped, computerName); - WriteError(error); - } - catch (Exception ex) - { - string errMsg = StringUtil.Format(ComputerResources.FailToConnectToComputer, computerName, ex.Message); - ErrorRecord error = new ErrorRecord(new InvalidOperationException(errMsg), "RenameComputerException", - ErrorCategory.OperationStopped, computerName); - WriteError(error); - } - } - -#if !CORECLR - private void DoRenameComputerDcom(string computer, string computerName, string newName, bool isLocalhost) - { - EnumerationOptions enumOptions = new EnumerationOptions { UseAmendedQualifiers = true, DirectRead = true }; - ObjectQuery computerSystemQuery = new ObjectQuery("select * from " + ComputerWMIHelper.WMI_Class_ComputerSystem); - // If both LocalCred and DomainCred are not provided, we use the default options - ConnectionOptions options = new ConnectionOptions - { - Authentication = AuthenticationLevel.PacketPrivacy, - Impersonation = ImpersonationLevel.Impersonate, - EnablePrivileges = true - }; + break; + } - if (isLocalhost) - { - options.Username = null; - options.SecurePassword = null; - } - // If the LocalCred is not given but the DomainCred is available, use the DomainCred for WMI connection. - // If LocalCred is given, use the local credential for WMI connection - else if (LocalCredential != null) - { - options.SecurePassword = LocalCredential.Password; - options.Username = ComputerWMIHelper.GetLocalAdminUserName(computerName, LocalCredential); - } - else if (DomainCredential != null) - { - options.SecurePassword = DomainCredential.Password; - options.Username = DomainCredential.UserName; - } + if (_waitOnComputers.Count > 1) + { + _status = StringUtil.Format(ComputerResources.WaitForMultipleComputers, machineCompleteRestart, _waitOnComputers.Count); + _percent = machineCompleteRestart * 100 / _waitOnComputers.Count; + } + } - bool successful = false; - ManagementScope scope = new ManagementScope(ComputerWMIHelper.GetScopeString(computer, ComputerWMIHelper.WMI_Path_CIM), options); - try - { - using (var searcher = new ManagementObjectSearcher(scope, computerSystemQuery, enumOptions)) - { - foreach (ManagementObject computerSystem in searcher.Get()) + if (_timeUp) { - using (computerSystem) + // The timeout expires. Write out timeout error messages for the computers that haven't finished restarting + do { - string oldName = (string)computerSystem["DNSHostName"]; - if (oldName.Equals(newName, StringComparison.OrdinalIgnoreCase)) + if (restartStageTestList.Count > 0) { - string errMsg = StringUtil.Format(ComputerResources.NewNameIsOldName, computerName, newName); - ErrorRecord error = new ErrorRecord( - new InvalidOperationException(errMsg), "NewNameIsOldName", - ErrorCategory.InvalidArgument, newName); - WriteError(error); - continue; + WriteOutTimeoutError(restartStageTestList); } - string dUserName = null; - string dPassword = null; - if ((bool)computerSystem["PartOfDomain"]) + if (wmiTestList.Count > 0) { - // If the target computer is in a domain, always use the DomainCred. If the DomainCred is not given, - // use null for UserName and Password, so the context of the caller will be used. - dUserName = DomainCredential != null ? DomainCredential.UserName : null; - dPassword = DomainCredential != null ? Utils.GetStringFromSecureString(DomainCredential.Password) : null; + WriteOutTimeoutError(wmiTestList); } - // If the target computer is not in a domain, just use null for the UserName and Password - ManagementBaseObject renameParameter = computerSystem.GetMethodParameters("Rename"); - renameParameter.SetPropertyValue("Name", newName); - renameParameter.SetPropertyValue("UserName", dUserName); - renameParameter.SetPropertyValue("Password", dPassword); + // Wait for WMI. All computers that finished restarting are put in "winrmTestList" + if (isForWmi) + { + break; + } - ManagementBaseObject result = computerSystem.InvokeMethod("Rename", renameParameter, null); - Dbg.Diagnostics.Assert(result != null, "result cannot be null if the Rename method is invoked"); - int retVal = Convert.ToInt32(result["ReturnValue"], CultureInfo.CurrentCulture); - if (retVal != 0) + // Wait for WinRM. All computers that finished restarting are put in "psTestList" + if (winrmTestList.Count > 0) { - var ex = new Win32Exception(retVal); - string errMsg = StringUtil.Format(ComputerResources.FailToRename, computerName, newName, ex.Message); - ErrorRecord error = new ErrorRecord(new InvalidOperationException(errMsg), "FailToRenameComputer", ErrorCategory.OperationStopped, computerName); - WriteError(error); + WriteOutTimeoutError(winrmTestList); } - else + + if (isForWinRm) { - successful = true; + break; } - if (PassThru) + if (psTestList.Count > 0) { - WriteObject(ComputerWMIHelper.GetRenameComputerStatusObject(retVal, newName, computerName)); + WriteOutTimeoutError(psTestList); } - } + + // Wait for PowerShell. All computers that finished restarting are put in "allDoneList" + } while (false); } } + } + } - // If successful and the Restart parameter is specified, restart the computer - if (successful && _restart) - { - object[] flags = new object[] { 6, 0 }; - RestartComputerCommand.RestartOneComputerUsingDcom(this, isLocalhost, computerName, flags, options); - } + /// + /// To implement ^C. + /// + protected override void StopProcessing() + { + _exit = true; + _cancel.Cancel(); + _waitHandler.Set(); - // If successful and the Restart parameter is not specified, write out warning - if (successful && !_restart) - { - WriteWarning(StringUtil.Format(ComputerResources.RestartNeeded, null, computerName)); - } - } - catch (ManagementException ex) - { - string errMsg = StringUtil.Format(ComputerResources.FailToConnectToComputer, computerName, ex.Message); - ErrorRecord error = new ErrorRecord(new InvalidOperationException(errMsg), "RenameComputerException", - ErrorCategory.OperationStopped, computerName); - WriteError(error); - } - catch (COMException ex) - { - string errMsg = StringUtil.Format(ComputerResources.FailToConnectToComputer, computerName, ex.Message); - ErrorRecord error = new ErrorRecord(new InvalidOperationException(errMsg), "RenameComputerException", - ErrorCategory.OperationStopped, computerName); - WriteError(error); - } - catch (UnauthorizedAccessException ex) + _timer?.Change(System.Threading.Timeout.Infinite, System.Threading.Timeout.Infinite); + + if (_powershell != null) { - string errMsg = StringUtil.Format(ComputerResources.FailToConnectToComputer, computerName, ex.Message); - ErrorRecord error = new ErrorRecord(new InvalidOperationException(errMsg), "RenameComputerException", - ErrorCategory.OperationStopped, computerName); - WriteError(error); + _powershell.Stop(); + _powershell.Dispose(); } } -#endif -#endregion "Private Methods" + #endregion "Overrides" + } + + #endregion Restart-Computer + + #region Stop-Computer + + /// + /// Cmdlet to stop computer. + /// + [Cmdlet(VerbsLifecycle.Stop, "Computer", SupportsShouldProcess = true, + HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2097151", RemotingCapability = RemotingCapability.SupportedByCommand)] + public sealed class StopComputerCommand : PSCmdlet, IDisposable + { + #region Private Members + + private readonly CancellationTokenSource _cancel = new(); + + private const int forcedShutdown = 5; // See https://msdn.microsoft.com/library/aa394058(v=vs.85).aspx + + #endregion -#region "Override Methods" + #region "Parameters" /// - /// Begin Processing + /// The authentication options for CIM_WSMan connection. /// - protected override void BeginProcessing() - { - base.BeginProcessing(); + [Parameter] + [ValidateSet( + "Default", + "Basic", + "Negotiate", // can be used with and without credential (without -> PSRP mapped to NegotiateWithImplicitCredential) + "CredSSP", + "Digest", + "Kerberos")] // can be used with explicit or implicit credential + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] + public string WsmanAuthentication { get; set; } = "Default"; + + /// + /// The following is the definition of the input parameter "ComputerName". + /// Value of the address requested. The form of the value can be either the + /// computer name ("wxyz1234"), IPv4 address ("192.168.177.124"), or IPv6 + /// address ("2010:836B:4179::836B:4179"). + /// + [Parameter(Position = 0, ValueFromPipelineByPropertyName = true)] + [ValidateNotNullOrEmpty] + [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] + [Alias("CN", "__SERVER", "Server", "IPAddress")] + public string[] ComputerName { get; set; } = new string[] { "." }; - bool haveWsmanAuthenticationParam = this.MyInvocation.BoundParameters.ContainsKey("WsmanAuthentication"); - bool haveProtocolParam = this.MyInvocation.BoundParameters.ContainsKey("Protocol"); - _transportProtocol = (this.Protocol.Equals(ComputerWMIHelper.WsmanProtocol, StringComparison.OrdinalIgnoreCase) || (haveWsmanAuthenticationParam && !haveProtocolParam)) ? - TransportProtocol.WSMan : TransportProtocol.DCOM; + /// + /// The following is the definition of the input parameter "Credential". + /// Specifies a user account that has permission to perform this action. Type a + /// user-name, such as "User01" or "Domain01\User01", or enter a PSCredential + /// object, such as one from the Get-Credential cmdlet. + /// + [Parameter(Position = 1)] + [ValidateNotNullOrEmpty] + [Credential] + public PSCredential Credential { get; set; } - // Verify parameter set - if ((_transportProtocol == TransportProtocol.DCOM) && haveWsmanAuthenticationParam) - { - ThrowTerminatingError( - new ErrorRecord( - new PSArgumentException(ComputerResources.RenameCommandWsmanAuthParamConflict), - "InvalidParameter", - ErrorCategory.InvalidArgument, - this)); - } + /// + /// Force the operation to take place if possible. + /// + [Parameter] + public SwitchParameter Force { get; set; } = false; -#if CORECLR - // DCOM Authentication is not supported for CoreCLR. Throw an error - // and request that the user specify WSMan Authentication. - if (_transportProtocol == TransportProtocol.DCOM) - { - PSArgumentException ex = new PSArgumentException(ComputerResources.InvalidParameterDCOMNotSupported); - ThrowTerminatingError(new ErrorRecord(ex, "InvalidParameterDCOMNotSupported", ErrorCategory.InvalidArgument, null)); - } -#endif - } + #endregion "parameters" + + #region "IDisposable Members" /// - /// ProcessRecord method. + /// Dispose Method. /// - protected override void ProcessRecord() + public void Dispose() { - string targetComputer = ValidateComputerName(); - if (targetComputer == null) return; + _cancel.Dispose(); + } - bool isLocalhost = targetComputer.Equals("localhost", StringComparison.OrdinalIgnoreCase); - if (isLocalhost) - { - if (!_containsLocalHost) - _containsLocalHost = true; - _newNameForLocalHost = NewName; + #endregion "IDisposable Members" - return; - } + #region "Overrides" - DoRenameComputerAction(targetComputer, NewName, false); + /// + /// ProcessRecord. + /// + [SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling")] + protected override void ProcessRecord() + { + object[] flags = new object[] { 1, 0 }; + if (Force.IsPresent) + flags[0] = forcedShutdown; + + ProcessWSManProtocol(flags); } /// - /// EndProcessing method + /// To implement ^C. /// - protected override void EndProcessing() + protected override void StopProcessing() { - if (!_containsLocalHost) return; + try + { + _cancel.Cancel(); + } + catch (ObjectDisposedException) { } + catch (AggregateException) { } + } - DoRenameComputerAction("localhost", _newNameForLocalHost, true); + #endregion "Overrides" + + #region Private Methods + + private void ProcessWSManProtocol(object[] flags) + { + foreach (string computer in ComputerName) + { + string compname = string.Empty; + string strLocal = string.Empty; + bool isLocalHost = false; + + if (_cancel.Token.IsCancellationRequested) + { + break; + } + + if ((computer.Equals("localhost", StringComparison.OrdinalIgnoreCase)) || (computer.Equals(".", StringComparison.OrdinalIgnoreCase))) + { + compname = Dns.GetHostName(); + strLocal = "localhost"; + isLocalHost = true; + } + else + { + compname = computer; + } + + if (!ShouldProcess(StringUtil.Format(ComputerResources.DoubleComputerName, strLocal, compname))) + { + continue; + } + else + { + ComputerWMIHelper.InvokeWin32ShutdownUsingWsman( + this, + isLocalHost, + compname, + flags, + Credential, + WsmanAuthentication, + ComputerResources.StopcomputerFailed, + "StopComputerException", + _cancel.Token); + } + } } -#endregion "Override Methods" + #endregion } -#endregion Rename-Computer - -#if !CORECLR - -#region Test-ComputerSecureChannel + #endregion + #region Rename-Computer /// - /// This cmdlet queries the status of trust relationships and will remove and - /// rebuild the trust if specified. + /// Renames a domain computer and its corresponding domain account or a + /// workgroup computer. Use this command to rename domain workstations and local + /// machines only. It cannot be used to rename Domain Controllers. /// - - [Cmdlet(VerbsDiagnostic.Test, "ComputerSecureChannel", SupportsShouldProcess = true, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=137749")] - [OutputType(typeof(Boolean))] - public class TestComputerSecureChannelCommand : PSCmdlet + [Cmdlet(VerbsCommon.Rename, "Computer", SupportsShouldProcess = true, + HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2097054", RemotingCapability = RemotingCapability.SupportedByCommand)] + [OutputType(typeof(RenameComputerChangeInfo))] + public class RenameComputerCommand : PSCmdlet { -#region Parameters + #region Private Members + + private bool _containsLocalHost = false; + private string _newNameForLocalHost = null; + + private readonly string _shortLocalMachineName = Dns.GetHostName(); + private readonly string _fullLocalMachineName = Dns.GetHostEntryAsync(string.Empty).Result.HostName; + + #endregion + + #region Parameters /// - /// Repair the secure channel between the local machine with the domain, if it's broken + /// Target computers to rename. /// - [Parameter] - public SwitchParameter Repair { get; set; } + [Parameter(ValueFromPipelineByPropertyName = true)] + [ValidateNotNullOrEmpty] + public string ComputerName { get; set; } = "localhost"; /// - /// The trusted domain controller to operate "Repair" on. + /// Emit the output. /// - [Parameter, ValidateNotNullOrEmpty] - public string Server { get; set; } + // [Alias("Restart")] + [Parameter] + public SwitchParameter PassThru { get; set; } /// - /// The domain credential for authenticating to the domain the local machine joined + /// The domain credential of the domain the target computer joined. /// - [Parameter, ValidateNotNullOrEmpty, Credential] - public PSCredential Credential { get; set; } - - private const uint NETLOGON_CONTROL_REDISCOVER = 5; - private const uint NETLOGON_CONTROL_TC_QUERY = 6; - private const uint NETLOGON_INFO_2 = 2; + [Parameter] + [ValidateNotNullOrEmpty] + [Credential] + public PSCredential DomainCredential { get; set; } -#endregion Parameters + /// + /// The administrator credential of the target computer. + /// + [Parameter] + [ValidateNotNullOrEmpty] + [Credential] + public PSCredential LocalCredential { get; set; } -#region "Private Methods" + /// + /// New names for the target computers. + /// + [Parameter(Mandatory = true, Position = 0, ValueFromPipelineByPropertyName = true)] + [ValidateNotNullOrEmpty] + public string NewName { get; set; } - [SuppressMessage("Microsoft.Usage", "CA1806:DoNotIgnoreMethodResults", Justification = "Return results are used in debug asserts.")] - private bool VerifySecureChannel(string domain, string localMachineName) + /// + /// Suppress the ShouldContinue. + /// + [Parameter] + public SwitchParameter Force { - IntPtr queryInfo = IntPtr.Zero; - IntPtr domainPtr = Marshal.StringToCoTaskMemAuto(domain); - bool scInGoodState = false; + get { return _force; } - try - { - int errorCode = SAMAPI.I_NetLogonControl2(null, NETLOGON_CONTROL_TC_QUERY, NETLOGON_INFO_2, ref domainPtr, out queryInfo); + set { _force = value; } + } - if (errorCode != 0) - { - var ex = new Win32Exception(errorCode); - string errMsg = StringUtil.Format(ComputerResources.FailToTestSecureChannel, ex.Message); - ErrorRecord error = new ErrorRecord(new InvalidOperationException(errMsg), "FailToTestSecureChannel", - ErrorCategory.OperationStopped, localMachineName); - ThrowTerminatingError(error); - } + private bool _force; - var infoData = (SAMAPI.NetLogonInfo2)Marshal.PtrToStructure(queryInfo, typeof(SAMAPI.NetLogonInfo2)); - scInGoodState = infoData.PdcConnectionStatus == 0; - } - finally - { - if (domainPtr != IntPtr.Zero) - { - Marshal.FreeCoTaskMem(domainPtr); - } - if (queryInfo != IntPtr.Zero) - { - int freeResult = SAMAPI.NetApiBufferFree(queryInfo); - Dbg.Diagnostics.Assert(freeResult == 0, "NetApiBufferFree returned non-zero value"); - } - } + /// + /// To restart the target computer after rename it. + /// + [Parameter] + public SwitchParameter Restart + { + get { return _restart; } - return scInGoodState; + set { _restart = value; } } - [SuppressMessage("Microsoft.Usage", "CA1806:DoNotIgnoreMethodResults", Justification = "Return results are used in debug asserts.")] - private bool ResetSecureChannel(string domain) - { - IntPtr queryInfo = IntPtr.Zero; - IntPtr domainPtr = Marshal.StringToCoTaskMemAuto(domain); - bool scInGoodState = false; - - try - { - int errorCode = SAMAPI.I_NetLogonControl2(null, NETLOGON_CONTROL_REDISCOVER, NETLOGON_INFO_2, ref domainPtr, out queryInfo); - if (errorCode == 0) - { - var infoData = (SAMAPI.NetLogonInfo2)Marshal.PtrToStructure(queryInfo, typeof(SAMAPI.NetLogonInfo2)); - scInGoodState = infoData.TrustedDcName != null; - } - } - finally - { - if (domainPtr != IntPtr.Zero) - { - Marshal.FreeCoTaskMem(domainPtr); - } - if (queryInfo != IntPtr.Zero) - { - int freeResult = SAMAPI.NetApiBufferFree(queryInfo); - Dbg.Diagnostics.Assert(freeResult == 0, "NetApiBufferFree returned non-zero value"); - } - } + private bool _restart; - return scInGoodState; - } + /// + /// The authentication options for CIM_WSMan connection. + /// + [Parameter] + [ValidateSet( + "Default", + "Basic", + "Negotiate", // can be used with and without credential (without -> PSRP mapped to NegotiateWithImplicitCredential) + "CredSSP", + "Digest", + "Kerberos")] // can be used with implicit or explicit credential + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] + public string WsmanAuthentication { get; set; } = "Default"; -#endregion "Private Methods" + #endregion -#region "Override Methods" + #region "Private Methods" /// - /// BeginProcessing method + /// Check to see if the target computer is the local machine. /// - protected override void BeginProcessing() + private string ValidateComputerName() { - if (Server != null) + // Validate target name. + ErrorRecord targetError = null; + string targetComputer = ComputerWMIHelper.ValidateComputerName(ComputerName, _shortLocalMachineName, _fullLocalMachineName, ref targetError); + if (targetComputer == null) { - if (Server.Length == 1 && Server[0] == '.') + if (targetError != null) { - Server = "localhost"; + WriteError(targetError); } - try - { - string resolveFullName = Dns.GetHostEntry(Server).HostName; - } - catch (Exception exception) - { - string errMsg = StringUtil.Format(ComputerResources.CannotResolveComputerName, Server, exception.Message); - ErrorRecord error = new ErrorRecord(new InvalidOperationException(errMsg), "AddressResolutionException", ErrorCategory.InvalidArgument, Server); - ThrowTerminatingError(error); - } + return null; } - } + // Validate *new* name. Validate the format of the new name. Check if the old name is the same as the + // new name later. + if (!ComputerWMIHelper.IsComputerNameValid(NewName)) + { + bool isLocalhost = targetComputer.Equals(ComputerWMIHelper.localhostStr, StringComparison.OrdinalIgnoreCase); + string errMsg = StringUtil.Format(ComputerResources.InvalidNewName, isLocalhost ? _shortLocalMachineName : targetComputer, NewName); + ErrorRecord error = new( + new InvalidOperationException(errMsg), "InvalidNewName", + ErrorCategory.InvalidArgument, NewName); + WriteError(error); + return null; + } - /// - /// ProcessRecord method. - /// Suppress the message about NetApiBufferFree. The retuned results are - /// actually used, but only in checked builds - /// - [SuppressMessage("Microsoft.Usage", "CA1806:DoNotIgnoreMethodResults")] - protected override void ProcessRecord() + return targetComputer; + } + + private void DoRenameComputerAction(string computer, string newName, bool isLocalhost) { - string localMachineName = Dns.GetHostName(); - string domain = null; - Exception exception = null; + string computerName = isLocalhost ? _shortLocalMachineName : computer; - if (!ShouldProcess(localMachineName)) + if (!ShouldProcess(computerName)) { return; } - try + // Check the length of the new name + if (newName != null && newName.Length > ComputerWMIHelper.NetBIOSNameMaxLength) { - ManagementObject computerSystemInstance = new ManagementObject("Win32_ComputerSystem.Name=\"" + localMachineName + "\""); - if (!(bool)computerSystemInstance["PartOfDomain"]) + string truncatedName = newName.Substring(0, ComputerWMIHelper.NetBIOSNameMaxLength); + string query = StringUtil.Format(ComputerResources.TruncateNetBIOSName, truncatedName); + string caption = ComputerResources.TruncateNetBIOSNameCaption; + if (!Force && !ShouldContinue(query, caption)) { - string errMsg = ComputerResources.TestComputerNotInDomain; - ErrorRecord error = new ErrorRecord(new InvalidOperationException(errMsg), "ComputerNotInDomain", - ErrorCategory.InvalidOperation, localMachineName); - ThrowTerminatingError(error); + return; } - domain = (string)LanguagePrimitives.ConvertTo(computerSystemInstance["Domain"], typeof(string), CultureInfo.InvariantCulture); - } - catch (ManagementException ex) - { - exception = ex; } - catch (COMException ex) - { - exception = ex; - } - catch (UnauthorizedAccessException ex) - { - exception = ex; - } - if (exception != null) - { - string errMsg = StringUtil.Format(ComputerResources.FailToGetDomainInformation, exception.Message); - ErrorRecord error = new ErrorRecord(new InvalidOperationException(errMsg), "FailToGetDomainInformation", - ErrorCategory.OperationStopped, localMachineName); - ThrowTerminatingError(error); - } - - Dbg.Diagnostics.Assert(domain != null, "domain should not be null at this point"); - bool scInGoodState = false; - string verboseMsg = null; - if (Repair) - { - ResetComputerMachinePasswordCommand. - ResetMachineAccountPassword(domain, localMachineName, Server, Credential, this); - scInGoodState = ResetSecureChannel(domain); - verboseMsg = scInGoodState - ? StringUtil.Format(ComputerResources.RepairSecureChannelSucceed, domain) - : StringUtil.Format(ComputerResources.RepairSecureChannelFail, domain); - } - else - { - scInGoodState = VerifySecureChannel(domain, localMachineName); - verboseMsg = scInGoodState - ? StringUtil.Format(ComputerResources.SecureChannelAlive, domain) - : StringUtil.Format(ComputerResources.SecureChannelBroken, domain); - } - - WriteObject(scInGoodState); - WriteVerbose(verboseMsg); + DoRenameComputerWsman(computer, computerName, newName, isLocalhost); } -#endregion "Override Methods" - }//End Class - - -#endregion - -#region Reset-ComputerMachinePassword - /// - /// Resets the computer machine password used to authenticate with DCs. - /// - - [Cmdlet(VerbsCommon.Reset, "ComputerMachinePassword", - SupportsShouldProcess = true, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=135252")] - public class ResetComputerMachinePasswordCommand : PSCmdlet - { -#region "Parameter and PrivateData" - - /// - /// The following is the definition of the input parameter "Server". - /// Specifies the name of the domain controller to use for setting the machine - /// account password. - /// - [Parameter] - [ValidateNotNullOrEmpty] - public string Server { get; set; } - - /// - /// The domain credential for authenticating to the domain the local machine joined - /// - [Parameter] - [ValidateNotNullOrEmpty] - [Credential] - public PSCredential Credential { get; set; } + private void DoRenameComputerWsman(string computer, string computerName, string newName, bool isLocalhost) + { + bool successful = false; + int retVal; + PSCredential credToUse = isLocalhost ? null : (LocalCredential ?? DomainCredential); - private const uint STATUS_ACCESS_DENIED = 0xc0000022; - private const uint STATUS_OBJECT_NAME_NOT_FOUND = 0xc0000034; + try + { + using (CancellationTokenSource cancelTokenSource = new()) + using (CimSession cimSession = RemoteDiscoveryHelper.CreateCimSession(computer, credToUse, WsmanAuthentication, isLocalhost, this, cancelTokenSource.Token)) + { + var operationOptions = new CimOperationOptions + { + Timeout = TimeSpan.FromMilliseconds(10000), + CancellationToken = cancelTokenSource.Token, + // This prefix works against all versions of the WinRM server stack, both win8 and win7 + ResourceUriPrefix = new Uri(ComputerWMIHelper.CimUriPrefix) + }; - private const uint SECRET_SET_VALUE = 0x00000001; - private const uint SECRET_QUERY_VALUE = 0x00000002; + IEnumerable mCollection = cimSession.QueryInstances( + ComputerWMIHelper.CimOperatingSystemNamespace, + ComputerWMIHelper.CimQueryDialect, + "Select * from " + ComputerWMIHelper.WMI_Class_ComputerSystem, + operationOptions); - // This number comes from the GenerateRandomPassword implementation of NetDom.exe. - // There is a reason behind this number. - private const int PasswordLength = 120; - private const string SecretKey = "$MACHINE.ACC"; + foreach (CimInstance cimInstance in mCollection) + { + var oldName = cimInstance.CimInstanceProperties["DNSHostName"].Value.ToString(); + if (oldName.Equals(newName, StringComparison.OrdinalIgnoreCase)) + { + string errMsg = StringUtil.Format(ComputerResources.NewNameIsOldName, computerName, newName); + ErrorRecord error = new( + new InvalidOperationException(errMsg), "NewNameIsOldName", + ErrorCategory.InvalidArgument, newName); + WriteError(error); -#endregion "Parameter and PrivateData" + continue; + } -#region "Private Methods" + // If the target computer is in a domain, always use the DomainCred. If the DomainCred is not given, + // use null for UserName and Password, so the context of the caller will be used. + // If the target computer is not in a domain, just use null for the UserName and Password + string dUserName = null; + string dPassword = null; + if (((bool)cimInstance.CimInstanceProperties["PartOfDomain"].Value) && (DomainCredential != null)) + { + dUserName = DomainCredential.UserName; + dPassword = Utils.GetStringFromSecureString(DomainCredential.Password); + } - /// - /// Throw out terminating error for LSA function invocations - /// - /// - /// - private static void ThrowOutLsaError(uint ret, PSCmdlet cmdlet) - { - var ex = new Win32Exception(SAMAPI.LsaNtStatusToWinError((int)ret)); - string errMsg = StringUtil.Format(ComputerResources.FailToResetPasswordOnLocalMachine, ex.Message); - ErrorRecord error = new ErrorRecord(new InvalidOperationException(errMsg), "FailToResetPasswordOnLocalMachine", - ErrorCategory.OperationStopped, Dns.GetHostName()); - cmdlet.ThrowTerminatingError(error); - } + var methodParameters = new CimMethodParametersCollection(); + methodParameters.Add(CimMethodParameter.Create( + "Name", + newName, + Microsoft.Management.Infrastructure.CimType.String, + CimFlags.None)); -#endregion "Private Methods" + methodParameters.Add(CimMethodParameter.Create( + "UserName", + dUserName, + Microsoft.Management.Infrastructure.CimType.String, + (dUserName == null) ? CimFlags.NullValue : CimFlags.None)); -#region "Internal Methods" + methodParameters.Add( + CimMethodParameter.Create( + "Password", + dPassword, + Microsoft.Management.Infrastructure.CimType.String, + (dPassword == null) ? CimFlags.NullValue : CimFlags.None)); - /// - /// Reset machine account password - /// - /// - /// - /// - /// - /// - [SuppressMessage("Microsoft.Usage", "CA1806:DoNotIgnoreMethodResults", Justification = "Return results are used in debug asserts.")] - internal static void ResetMachineAccountPassword(string domain, string localMachineName, string server, PSCredential credential, PSCmdlet cmdlet) - { - // Get domain directory entry and reset the password on the machine account of the local machine - string newPassword = null; - string domainOrServerName = server ?? domain; + if (!InternalTestHooks.TestRenameComputer) + { + CimMethodResult result = cimSession.InvokeMethod( + ComputerWMIHelper.CimOperatingSystemNamespace, + cimInstance, + "Rename", + methodParameters, + operationOptions); + + retVal = Convert.ToInt32(result.ReturnValue.Value, CultureInfo.CurrentCulture); + } + else + { + retVal = InternalTestHooks.TestRenameComputerResults; + } - try - { - string dUserName = credential != null ? credential.UserName : null; - string dPassword = credential != null ? Utils.GetStringFromSecureString(credential.Password) : null; - - using (var domainEntry = new DirectoryEntry( - "LDAP://" + domainOrServerName, - dUserName, - dPassword, - AuthenticationTypes.Secure)) - { - using (var searcher = new DirectorySearcher(domainEntry)) - { - searcher.Filter = "(&(objectClass=computer)(|(cn=" + localMachineName + ")(dn=" + localMachineName + ")))"; - SearchResult result = searcher.FindOne(); + if (retVal != 0) + { + var ex = new Win32Exception(retVal); + string errMsg = StringUtil.Format(ComputerResources.FailToRename, computerName, newName, ex.Message); + ErrorRecord error = new(new InvalidOperationException(errMsg), "FailToRenameComputer", ErrorCategory.OperationStopped, computerName); + WriteError(error); + } + else + { + successful = true; + } - if (result == null) + if (PassThru) { - string format = server != null - ? ComputerResources.CannotFindMachineAccountFromServer - : ComputerResources.CannotFindMachineAccountFromDomain; - string errMsg = StringUtil.Format(format, domainOrServerName); - ErrorRecord error = new ErrorRecord(new InvalidOperationException(errMsg), "CannotFindMachineAccount", - ErrorCategory.OperationStopped, localMachineName); - cmdlet.ThrowTerminatingError(error); + WriteObject(ComputerWMIHelper.GetRenameComputerStatusObject(retVal, newName, computerName)); } - else + + if (successful) { - // Generate a random password of length 120, and reset the password on the machine account - using (var targetEntry = result.GetDirectoryEntry()) + if (_restart) + { + // If successful and the Restart parameter is specified, restart the computer + object[] flags = new object[] { 6, 0 }; + ComputerWMIHelper.InvokeWin32ShutdownUsingWsman( + this, + isLocalhost, + computerName, + flags, + credToUse, + WsmanAuthentication, + ComputerResources.RestartcomputerFailed, + "RestartcomputerFailed", + cancelTokenSource.Token); + } + else { - newPassword = ComputerWMIHelper.GetRandomPassword(PasswordLength); - targetEntry.Invoke("SetPassword", new object[] { newPassword }); - targetEntry.Properties["LockOutTime"].Value = 0; + WriteWarning(StringUtil.Format(ComputerResources.RestartNeeded, null, computerName)); } } } } } - catch (DirectoryServicesCOMException ex) - { - string errMsg = StringUtil.Format(ComputerResources.FailToResetPasswordOnDomain, ex.Message); - ErrorRecord error = new ErrorRecord(new InvalidOperationException(errMsg), "FailToResetPasswordOnDomain", - ErrorCategory.OperationStopped, localMachineName); - cmdlet.ThrowTerminatingError(error); - } - catch (TargetInvocationException ex) - { - string errMsg = StringUtil.Format(ComputerResources.FailToResetPasswordOnDomain, ex.InnerException.Message); - ErrorRecord error = new ErrorRecord(new InvalidOperationException(errMsg), "FailToResetPasswordOnDomain", - ErrorCategory.OperationStopped, localMachineName); - cmdlet.ThrowTerminatingError(error); - } - catch (COMException ex) - { - string errMsg = StringUtil.Format(ComputerResources.FailToResetPasswordOnDomain, ex.Message); - ErrorRecord error = new ErrorRecord(new InvalidOperationException(errMsg), "FailToResetPasswordOnDomain", - ErrorCategory.OperationStopped, localMachineName); - cmdlet.ThrowTerminatingError(error); - } - - // Set the same password to the local machine - Dbg.Diagnostics.Assert(newPassword != null, "the newPassword should not be null at this point"); - - // A direct translation of function NetpManageMachineSecret2 in //depot/winmain/ds/netapi/netjoin/joinutl.c - // Initialize the LSA_OBJECT_ATTRIBUTES - var lsaAttr = new SAMAPI.LSA_OBJECT_ATTRIBUTES(); - lsaAttr.RootDirectory = IntPtr.Zero; - lsaAttr.ObjectName = IntPtr.Zero; - lsaAttr.Attributes = 0; - lsaAttr.SecurityDescriptor = IntPtr.Zero; - lsaAttr.SecurityQualityOfService = IntPtr.Zero; - lsaAttr.Length = Marshal.SizeOf(typeof(SAMAPI.LSA_OBJECT_ATTRIBUTES)); - - // Initialize the policy handle and secret handle - IntPtr policyHandle = IntPtr.Zero; - IntPtr secretHandle = IntPtr.Zero; - - // Initialize variables for LsaQuerySecret call - IntPtr currentPassword = IntPtr.Zero; - - // Declare the key, newData and currentData - var key = new SAMAPI.LSA_UNICODE_STRING { Buffer = IntPtr.Zero }; - var newData = new SAMAPI.LSA_UNICODE_STRING { Buffer = IntPtr.Zero }; - - // Initialize the systemName for the localhost - var localhost = new SAMAPI.LSA_UNICODE_STRING(); - localhost.Buffer = IntPtr.Zero; - localhost.Length = 0; - localhost.MaximumLength = 0; - - try + catch (CimException ex) { - // Open the LSA policy - uint ret = SAMAPI.LsaOpenPolicy(ref localhost, ref lsaAttr, (int)SAMAPI.LSA_ACCESS.AllAccess, out policyHandle); - if (ret == STATUS_ACCESS_DENIED) - { - string errMsg = ComputerResources.NeedAdminPrivilegeToResetPassword; - ErrorRecord error = new ErrorRecord(new InvalidOperationException(errMsg), "UnauthorizedAccessException", - ErrorCategory.InvalidOperation, localMachineName); - cmdlet.ThrowTerminatingError(error); - } - if (ret != 0) - { - ThrowOutLsaError(ret, cmdlet); - } - - // Initialize secret key, new secret - SAMAPI.InitLsaString(SecretKey, ref key); - SAMAPI.InitLsaString(newPassword, ref newData); - bool secretCreated = false; - - // Open the secret. If the secret is not found, create the secret - ret = SAMAPI.LsaOpenSecret(policyHandle, ref key, SECRET_SET_VALUE | SECRET_QUERY_VALUE, out secretHandle); - if (ret == STATUS_OBJECT_NAME_NOT_FOUND) - { - ret = SAMAPI.LsaCreateSecret(policyHandle, ref key, SECRET_SET_VALUE, out secretHandle); - secretCreated = true; - } - if (ret != 0) - { - ThrowOutLsaError(ret, cmdlet); - } - - SAMAPI.LSA_UNICODE_STRING currentData; - // Get the current password - if (secretCreated) - { - // Use the new password as the current one - currentData = newData; - } - else - { - // Query for the current password - ret = SAMAPI.LsaQuerySecret(secretHandle, out currentPassword, IntPtr.Zero, IntPtr.Zero, IntPtr.Zero); - if (ret != 0) - { - ThrowOutLsaError(ret, cmdlet); - } - - currentData = (SAMAPI.LSA_UNICODE_STRING)Marshal.PtrToStructure(currentPassword, typeof(SAMAPI.LSA_UNICODE_STRING)); - } - - ret = SAMAPI.LsaSetSecret(secretHandle, ref newData, ref currentData); - if (ret != 0) - { - ThrowOutLsaError(ret, cmdlet); - } + string errMsg = StringUtil.Format(ComputerResources.FailToConnectToComputer, computerName, ex.Message); + ErrorRecord error = new(new InvalidOperationException(errMsg), "RenameComputerException", + ErrorCategory.OperationStopped, computerName); + WriteError(error); } - finally + catch (Exception ex) { - // Release pointers - if (currentPassword != IntPtr.Zero) - { - int releaseResult = SAMAPI.LsaFreeMemory(currentPassword); - Dbg.Diagnostics.Assert(releaseResult == 0, "LsaFreeMemory returned non-zero value"); - } - - // Release handles - if (policyHandle != IntPtr.Zero) - { - int releaseResult = SAMAPI.LsaClose(policyHandle); - Dbg.Diagnostics.Assert(releaseResult == 0, "LsaClose returned non-zero value"); - } - - if (secretHandle != IntPtr.Zero) - { - int releaseResult = SAMAPI.LsaClose(secretHandle); - Dbg.Diagnostics.Assert(releaseResult == 0, "LsaClose returned non-zero value"); - } - - // Release LSA_UNICODE_STRING - SAMAPI.FreeLsaString(ref key); - SAMAPI.FreeLsaString(ref newData); + string errMsg = StringUtil.Format(ComputerResources.FailToConnectToComputer, computerName, ex.Message); + ErrorRecord error = new(new InvalidOperationException(errMsg), "RenameComputerException", + ErrorCategory.OperationStopped, computerName); + WriteError(error); } } -#endregion "Internal Methods" - -#region "Override Methods" + #endregion "Private Methods" - /// - /// BeginProcessing method. - /// - protected override void BeginProcessing() - { - if (Server != null) - { - if (Server.Length == 1 && Server[0] == '.') - { - Server = "localhost"; - } - - try - { - string resolveFullName = Dns.GetHostEntry(Server).HostName; - } - catch (Exception exception) - { - string errMsg = StringUtil.Format(ComputerResources.CannotResolveComputerName, Server, exception.Message); - ErrorRecord error = new ErrorRecord(new InvalidOperationException(errMsg), "AddressResolutionException", ErrorCategory.InvalidArgument, Server); - ThrowTerminatingError(error); - } - } - }//End BeginProcessing() + #region "Override Methods" /// - /// ProcessRecord method - /// Suppress the message about LsaFreeMemory and LsaClose. The retuned results are - /// actually used, but only in checked builds + /// ProcessRecord method. /// - [SuppressMessage("Microsoft.Usage", "CA1806:DoNotIgnoreMethodResults")] protected override void ProcessRecord() { - // Not to use Environment.MachineName to avoid the injection attack - string localMachineName = Dns.GetHostName(); - string domainName = null; - Exception exception = null; - - if (!ShouldProcess(localMachineName)) + string targetComputer = ValidateComputerName(); + if (targetComputer == null) { return; } - try - { - ManagementObject computerSystemInstance = new ManagementObject("Win32_ComputerSystem.Name=\"" + localMachineName + "\""); - if (!(bool)computerSystemInstance["PartOfDomain"]) - { - string errMsg = ComputerResources.ResetComputerNotInDomain; - ErrorRecord error = new ErrorRecord(new InvalidOperationException(errMsg), "ComputerNotInDomain", - ErrorCategory.InvalidOperation, localMachineName); - ThrowTerminatingError(error); - } - domainName = (string)LanguagePrimitives.ConvertTo(computerSystemInstance["Domain"], typeof(string), CultureInfo.InvariantCulture); - } - catch (ManagementException ex) - { - exception = ex; - } - catch (COMException ex) - { - exception = ex; - } - catch (UnauthorizedAccessException ex) - { - exception = ex; - } - if (exception != null) + bool isLocalhost = targetComputer.Equals("localhost", StringComparison.OrdinalIgnoreCase); + if (isLocalhost) { - string errMsg = StringUtil.Format(ComputerResources.FailToGetDomainInformation, exception.Message); - ErrorRecord error = new ErrorRecord(new InvalidOperationException(errMsg), "FailToGetDomainInformation", - ErrorCategory.OperationStopped, localMachineName); - ThrowTerminatingError(error); - } - - // Get domain directory entry and reset the password on the machine account of the local machine - Dbg.Diagnostics.Assert(domainName != null, "domainOrServerName should not be null at this point"); - ResetMachineAccountPassword(domainName, localMachineName, Server, Credential, this); - } - -#endregion "Override Methods" - }//End Class - -#endregion Reset-ComputerMachinePassword - -#region SAMCmdletsHelper - - /// - /// the static class for calling the the NetJoinDomain function. - /// - internal static class SAMAPI - { - /// - /// Structure for the LSA unicode string - /// - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] - internal struct LSA_UNICODE_STRING - { - internal ushort Length; - internal ushort MaximumLength; - [SuppressMessage("Microsoft.Reliability", "CA2006:UseSafeHandleToEncapsulateNativeResources")] - internal IntPtr Buffer; - } - - /// - /// Structure for the LSA object attributes - /// - [StructLayout(LayoutKind.Sequential)] - internal struct LSA_OBJECT_ATTRIBUTES - { - internal int Length; - internal IntPtr RootDirectory; - internal IntPtr ObjectName; - internal int Attributes; - internal IntPtr SecurityDescriptor; - internal IntPtr SecurityQualityOfService; - } - - /// - /// The LSA access mask - /// - internal enum LSA_ACCESS - { - Read = 0x20006, - AllAccess = 0x00F0FFF, - Execute = 0X20801, - Write = 0X207F8 - } - - /// - /// LsaOpenPolicy function - /// - /// - /// - /// - /// - /// - [DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)] - internal static extern uint LsaOpenPolicy( - ref LSA_UNICODE_STRING systemName, - ref LSA_OBJECT_ATTRIBUTES objectAttributes, - uint desiredAccess, - out IntPtr policyHandle); - - /// - /// LsaOpenSecret function - /// - /// - /// - /// - /// - /// - [DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)] - internal static extern uint LsaOpenSecret( - IntPtr policyHandle, - ref LSA_UNICODE_STRING secretName, - uint accessMask, - out IntPtr secretHandle); - - /// - /// LsaCreateSecret function - /// - /// - /// - /// - /// - /// - [DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)] - internal static extern uint LsaCreateSecret( - IntPtr policyHandle, - ref LSA_UNICODE_STRING secretName, - uint desiredAccess, - out IntPtr secretHandle); - - /// - /// LsaQuerySecret function - /// - /// - /// - /// - /// - /// - /// - [DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)] - internal static extern uint LsaQuerySecret( - IntPtr secretHandle, - out IntPtr currentValue, - IntPtr currentValueSetTime, - IntPtr oldValue, - IntPtr oldValueSetTime); - - /// - /// LsaSetSecret function - /// - /// - /// - /// - /// - [DllImport("advapi32.dll", CharSet = CharSet.Unicode, SetLastError = true)] - internal static extern uint LsaSetSecret( - IntPtr secretHandle, - ref LSA_UNICODE_STRING currentValue, - ref LSA_UNICODE_STRING oldValue); - - /// - /// LsaNtStatusToWinError function - /// - /// - /// - [DllImport("advapi32")] - internal static extern int LsaNtStatusToWinError(int ntStatus); - - /// - /// LsaClose function - /// - /// - /// - [DllImport("advapi32")] - internal static extern int LsaClose(IntPtr policyHandle); + if (!_containsLocalHost) + _containsLocalHost = true; + _newNameForLocalHost = NewName; - /// - /// LsaFreeMemory function - /// - /// - /// - [DllImport("advapi32")] - internal static extern int LsaFreeMemory(IntPtr buffer); + return; + } - /// - /// Initialize a LSA_UNICODE_STRING - /// - /// - /// - /// - internal static void InitLsaString(string s, ref LSA_UNICODE_STRING lus) - { - // Unicode strings max 32KB. The max value for MaximumLength should be ushort.MaxValue-1 - // because UnicodeEncoding.CharSize is 2. So the length of the string s should not be larger - // than (ushort.MaxValue - 1)/UnicodeEncoding.CharSize - 1, which is 0x7ffe (32766) - ushort maxLength = (ushort.MaxValue - 1) / UnicodeEncoding.CharSize - 1; - if (s.Length > maxLength) - throw new ArgumentException("String too long"); - lus.Buffer = Marshal.StringToHGlobalUni(s); - lus.Length = (ushort)(s.Length * UnicodeEncoding.CharSize); - lus.MaximumLength = (ushort)((s.Length + 1) * UnicodeEncoding.CharSize); + DoRenameComputerAction(targetComputer, NewName, false); } /// - /// Free the LSA_UNICODE_STRING + /// EndProcessing method. /// - /// - internal static void FreeLsaString(ref LSA_UNICODE_STRING s) + protected override void EndProcessing() { - if (s.Buffer == IntPtr.Zero) return; - - Marshal.FreeHGlobal(s.Buffer); - s.Buffer = IntPtr.Zero; - } + if (!_containsLocalHost) + { + return; + } - /// - /// The NETLOGON_INFO_2 struct used for function I_NetLogonControl2 - /// - [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Auto)] - internal struct NetLogonInfo2 - { - internal uint Flags; - /// - /// Secure channel status with the primary domain controller - /// - internal uint PdcConnectionStatus; - /// - /// Name of the trusted domain controller - /// - internal string TrustedDcName; - /// - /// Secure channel status with the specified trusted domain controller - /// - internal uint TdcConnectionStatus; + DoRenameComputerAction("localhost", _newNameForLocalHost, true); } - /// - /// To Reset a password for a computer in domain. - /// - [DllImport("netapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)] - internal static extern int I_NetLogonControl2( - [In] string lpServerName, - uint lpFunctionCode, - uint lpQueryLevel, - ref IntPtr lpInputData, - out IntPtr queryInformation); - - - [DllImport("Netapi32.dll", SetLastError = true)] - internal static extern int NetApiBufferFree(IntPtr Buffer); - - internal const int WorkGroupMachine = 2692; - internal const int MaxMachineNameLength = 15; + #endregion "Override Methods" } -#endregion - -#endif + #endregion Rename-Computer -#region "Public API" + #region "Public API" /// /// The object returned by SAM Computer cmdlets representing the status of the target machine. /// @@ -6281,7 +1632,7 @@ public sealed class ComputerChangeInfo private const string MatchFormat = "{0}:{1}"; /// - /// The HasSucceeded which shows the operation was success or not + /// The HasSucceeded which shows the operation was success or not. /// public bool HasSucceeded { get; set; } @@ -6305,7 +1656,7 @@ public override string ToString() /// /// /// - private string FormatLine(string HasSucceeded, string computername) + private static string FormatLine(string HasSucceeded, string computername) { return StringUtil.Format(MatchFormat, HasSucceeded, computername); } @@ -6349,14 +1700,14 @@ public override string ToString() /// /// /// - private string FormatLine(string HasSucceeded, string newcomputername, string oldcomputername) + private static string FormatLine(string HasSucceeded, string newcomputername, string oldcomputername) { return StringUtil.Format(MatchFormat, HasSucceeded, newcomputername, oldcomputername); } } -#endregion "Public API" + #endregion "Public API" -#region Helper + #region Helper /// /// Helper Class used by Stop-Computer,Restart-Computer and Test-Connection /// Also Contain constants used by System Restore related Cmdlets. @@ -6364,27 +1715,27 @@ private string FormatLine(string HasSucceeded, string newcomputername, string ol internal static class ComputerWMIHelper { /// - /// The maximum length of a valid NetBIOS name + /// The maximum length of a valid NetBIOS name. /// internal const int NetBIOSNameMaxLength = 15; /// - /// System Restore Class used by Cmdlets + /// System Restore Class used by Cmdlets. /// internal const string WMI_Class_SystemRestore = "SystemRestore"; /// - /// OperatingSystem WMI class used by Cmdlets + /// OperatingSystem WMI class used by Cmdlets. /// internal const string WMI_Class_OperatingSystem = "Win32_OperatingSystem"; /// - /// Service WMI class used by Cmdlets + /// Service WMI class used by Cmdlets. /// internal const string WMI_Class_Service = "Win32_Service"; /// - /// Win32_ComputerSystem WMI class used by Cmdlets + /// Win32_ComputerSystem WMI class used by Cmdlets. /// internal const string WMI_Class_ComputerSystem = "Win32_ComputerSystem"; @@ -6394,12 +1745,12 @@ internal static class ComputerWMIHelper internal const string WMI_Class_PingStatus = "Win32_PingStatus"; /// - /// CIMV2 path + /// CIMV2 path. /// internal const string WMI_Path_CIM = "\\root\\cimv2"; /// - /// Default path + /// Default path. /// internal const string WMI_Path_Default = "\\root\\default"; @@ -6414,53 +1765,42 @@ internal static class ComputerWMIHelper internal const int ErrorCode_Service = 1056; /// - /// The name of the privilege to shutdown a local system + /// The name of the privilege to shutdown a local system. /// internal const string SE_SHUTDOWN_NAME = "SeShutdownPrivilege"; /// - /// The name of the privilege to shutdown a remote system + /// The name of the privilege to shutdown a remote system. /// internal const string SE_REMOTE_SHUTDOWN_NAME = "SeRemoteShutdownPrivilege"; /// - /// DCOM protocol - /// - internal const string DcomProtocol = "DCOM"; - - /// - /// WSMan protocol - /// - internal const string WsmanProtocol = "WSMan"; - - /// - /// CimUriPrefix + /// CimUriPrefix. /// internal const string CimUriPrefix = "http://schemas.microsoft.com/wbem/wsman/1/wmi/root/cimv2"; /// - /// CimOperatingSystemNamespace + /// CimOperatingSystemNamespace. /// internal const string CimOperatingSystemNamespace = "root/cimv2"; /// - /// CimOperatingSystemShutdownMethod + /// CimOperatingSystemShutdownMethod. /// internal const string CimOperatingSystemShutdownMethod = "Win32shutdown"; /// - /// CimQueryDialect + /// CimQueryDialect. /// internal const string CimQueryDialect = "WQL"; /// - /// Local host name + /// Local host name. /// internal const string localhostStr = "localhost"; - /// - /// Get the local admin user name from a local NetworkCredential + /// Get the local admin user name from a local NetworkCredential. /// /// /// @@ -6470,20 +1810,20 @@ internal static string GetLocalAdminUserName(string computerName, PSCredential p string localUserName = null; // The format of local admin username should be "ComputerName\AdminName" - if (psLocalCredential.UserName.Contains("\\")) + if (psLocalCredential.UserName.Contains('\\')) { localUserName = psLocalCredential.UserName; } else { - int dotIndex = computerName.IndexOf(".", StringComparison.OrdinalIgnoreCase); + int dotIndex = computerName.IndexOf('.'); if (dotIndex == -1) { localUserName = computerName + "\\" + psLocalCredential.UserName; } else { - localUserName = computerName.Substring(0, dotIndex) + "\\" + psLocalCredential.UserName; + localUserName = string.Concat(computerName.AsSpan(0, dotIndex), "\\", psLocalCredential.UserName); } } @@ -6491,7 +1831,7 @@ internal static string GetLocalAdminUserName(string computerName, PSCredential p } /// - /// Generate a random password + /// Generate a random password. /// /// /// @@ -6513,42 +1853,16 @@ internal static string GetRandomPassword(int passwordLength) return new string(chars); } -#if !CORECLR // TODO:CORECLR Remove once ported to MI .Net - - /// - /// Get the Connection Options - /// - /// - /// - /// - /// - internal static ConnectionOptions GetConnectionOptions(AuthenticationLevel Authentication, ImpersonationLevel Impersonation, PSCredential Credential) - { - ConnectionOptions options = new ConnectionOptions(); - options.Authentication = Authentication; - options.EnablePrivileges = true; - options.Impersonation = Impersonation; - if (Credential != null) - { - options.Username = Credential.UserName; - options.SecurePassword = Credential.Password; - } - return options; - } - -#endif - /// - /// Gets the Scope - /// + /// Gets the Scope. /// /// /// /// internal static string GetScopeString(string computer, string namespaceParameter) { - StringBuilder returnValue = new StringBuilder("\\\\"); - if (computer.Equals("::1", StringComparison.CurrentCultureIgnoreCase) || computer.Equals("[::1]", StringComparison.CurrentCultureIgnoreCase)) + StringBuilder returnValue = new("\\\\"); + if (computer.Equals("::1", StringComparison.OrdinalIgnoreCase) || computer.Equals("[::1]", StringComparison.OrdinalIgnoreCase)) { returnValue.Append("localhost"); } @@ -6556,6 +1870,7 @@ internal static string GetScopeString(string computer, string namespaceParameter { returnValue.Append(computer); } + returnValue.Append(namespaceParameter); return returnValue.ToString(); } @@ -6572,10 +1887,11 @@ internal static bool IsValidDrive(string drive) { if (logicalDrive.DriveType.Equals(DriveType.Fixed)) { - if (drive.ToString().Equals(logicalDrive.Name.ToString(), System.StringComparison.OrdinalIgnoreCase)) + if (drive.Equals(logicalDrive.Name, System.StringComparison.OrdinalIgnoreCase)) return true; } } + return false; } @@ -6590,20 +1906,21 @@ internal static bool ContainsSystemDrive(string[] drives, string sysdrive) string driveApp; foreach (string drive in drives) { - if (!drive.EndsWith("\\", StringComparison.CurrentCultureIgnoreCase)) + if (!drive.EndsWith('\\')) { - driveApp = String.Concat(drive, "\\"); + driveApp = string.Concat(drive, "\\"); } else driveApp = drive; - if (driveApp.Equals(sysdrive, StringComparison.CurrentCultureIgnoreCase)) + if (driveApp.Equals(sysdrive, StringComparison.OrdinalIgnoreCase)) return true; } + return false; } /// - /// Returns the given computernames in a string + /// Returns the given computernames in a string. /// /// internal static string GetMachineNames(string[] computerNames) @@ -6620,7 +1937,7 @@ internal static string GetMachineNames(string[] computerNames) } string compname = string.Empty; - StringBuilder strComputers = new StringBuilder(); + StringBuilder strComputers = new(); int i = 0; foreach (string computer in computerNames) { @@ -6633,7 +1950,7 @@ internal static string GetMachineNames(string[] computerNames) i++; } - if ((computer.Equals("localhost", StringComparison.CurrentCultureIgnoreCase)) || (computer.Equals(".", StringComparison.OrdinalIgnoreCase))) + if ((computer.Equals("localhost", StringComparison.OrdinalIgnoreCase)) || (computer.Equals(".", StringComparison.OrdinalIgnoreCase))) { compname = Dns.GetHostName(); } @@ -6650,7 +1967,7 @@ internal static string GetMachineNames(string[] computerNames) internal static ComputerChangeInfo GetComputerStatusObject(int errorcode, string computername) { - ComputerChangeInfo computerchangeinfo = new ComputerChangeInfo(); + ComputerChangeInfo computerchangeinfo = new(); computerchangeinfo.ComputerName = computername; if (errorcode != 0) { @@ -6660,12 +1977,13 @@ internal static ComputerChangeInfo GetComputerStatusObject(int errorcode, string { computerchangeinfo.HasSucceeded = true; } + return computerchangeinfo; } internal static RenameComputerChangeInfo GetRenameComputerStatusObject(int errorcode, string newcomputername, string oldcomputername) { - RenameComputerChangeInfo renamecomputerchangeinfo = new RenameComputerChangeInfo(); + RenameComputerChangeInfo renamecomputerchangeinfo = new(); renamecomputerchangeinfo.OldComputerName = oldcomputername; renamecomputerchangeinfo.NewComputerName = newcomputername; if (errorcode != 0) @@ -6676,77 +1994,49 @@ internal static RenameComputerChangeInfo GetRenameComputerStatusObject(int error { renamecomputerchangeinfo.HasSucceeded = true; } + return renamecomputerchangeinfo; } - internal static void WriteNonTerminatingError(int errorcode, PSCmdlet cmdlet, string computername) { - Win32Exception ex = new Win32Exception(errorcode); - string additionalmessage = String.Empty; + Win32Exception ex = new(errorcode); + string additionalmessage = string.Empty; if (ex.NativeErrorCode.Equals(0x00000035)) { additionalmessage = StringUtil.Format(ComputerResources.NetworkPathNotFound, computername); } + string message = StringUtil.Format(ComputerResources.OperationFailed, ex.Message, computername, additionalmessage); - ErrorRecord er = new ErrorRecord(new InvalidOperationException(message), "InvalidOperationException", ErrorCategory.InvalidOperation, computername); + ErrorRecord er = new(new InvalidOperationException(message), "InvalidOperationException", ErrorCategory.InvalidOperation, computername); cmdlet.WriteError(er); } /// - /// Check whether the new computer name is valid + /// Check whether the new computer name is valid. /// /// /// internal static bool IsComputerNameValid(string computerName) { - bool allDigits = true; + bool hasAsciiLetterOrHyphen = false; if (computerName.Length >= 64) return false; foreach (char t in computerName) { - if (t >= 'A' && t <= 'Z' || - t >= 'a' && t <= 'z') - { - allDigits = false; - continue; - } - else if (t >= '0' && t <= '9') + if (char.IsAsciiLetter(t) || t is '-') { - continue; - } - else if (t == '-') - { - allDigits = false; - continue; + hasAsciiLetterOrHyphen = true; } - else + else if (!char.IsAsciiDigit(t)) { return false; } } - return !allDigits; - } - - /// - /// System Restore APIs are not supported on the ARM platform. Skip the system restore operation is necessary. - /// - /// - /// - internal static bool SkipSystemRestoreOperationForARMPlatform(PSCmdlet cmdlet) - { - bool retValue = false; - if (PsUtils.IsRunningOnProcessorArchitectureARM()) - { - var ex = new InvalidOperationException(ComputerResources.SystemRestoreNotSupported); - var er = new ErrorRecord(ex, "SystemRestoreNotSupported", ErrorCategory.InvalidOperation, null); - cmdlet.WriteError(er); - retValue = true; - } - return retValue; + return hasAsciiLetterOrHyphen; } /// @@ -6754,16 +2044,16 @@ internal static bool SkipSystemRestoreOperationForARMPlatform(PSCmdlet cmdlet) /// over a CIMSession. The flags parameter determines the type of shutdown operation /// such as shutdown, reboot, force etc. /// - /// Cmdlet host for reporting errors - /// True if local host computer - /// Target computer - /// Win32Shutdown flags - /// Optional credential - /// Optional authentication - /// Error message format string that takes two parameters - /// Fully qualified error Id - /// Cancel token - /// True on success + /// Cmdlet host for reporting errors. + /// True if local host computer. + /// Target computer. + /// Win32Shutdown flags. + /// Optional credential. + /// Optional authentication. + /// Error message format string that takes two parameters. + /// Fully qualified error Id. + /// Cancel token. + /// True on success. internal static bool InvokeWin32ShutdownUsingWsman( PSCmdlet cmdlet, bool isLocalhost, @@ -6786,7 +2076,7 @@ internal static bool InvokeWin32ShutdownUsingWsman( { Timeout = TimeSpan.FromMilliseconds(10000), CancellationToken = cancelToken, - //This prefix works against all versions of the WinRM server stack, both win8 and win7 + // This prefix works against all versions of the WinRM server stack, both win8 and win7 ResourceUriPrefix = new Uri(ComputerWMIHelper.CimUriPrefix) }; @@ -6798,14 +2088,15 @@ internal static bool InvokeWin32ShutdownUsingWsman( string message = StringUtil.Format(ComputerResources.PrivilegeNotEnabled, computerName, isLocalhost ? ComputerWMIHelper.SE_SHUTDOWN_NAME : ComputerWMIHelper.SE_REMOTE_SHUTDOWN_NAME); - ErrorRecord errorRecord = new ErrorRecord(new InvalidOperationException(message), "PrivilegeNotEnabled", ErrorCategory.InvalidOperation, null); + ErrorRecord errorRecord = new(new InvalidOperationException(message), "PrivilegeNotEnabled", ErrorCategory.InvalidOperation, null); cmdlet.WriteError(errorRecord); return false; } - using (CimSession cimSession = RemoteDiscoveryHelper.CreateCimSession(targetMachine, credInUse, authInUse, cancelToken, cmdlet)) + using (CimSession cimSession = RemoteDiscoveryHelper.CreateCimSession(targetMachine, credInUse, authInUse, isLocalhost, cmdlet, cancelToken)) { var methodParameters = new CimMethodParametersCollection(); + int retVal; methodParameters.Add(CimMethodParameter.Create( "Flags", flags[0], @@ -6818,19 +2109,44 @@ internal static bool InvokeWin32ShutdownUsingWsman( Microsoft.Management.Infrastructure.CimType.SInt32, CimFlags.None)); - CimMethodResult result = cimSession.InvokeMethod( - ComputerWMIHelper.CimOperatingSystemNamespace, - ComputerWMIHelper.WMI_Class_OperatingSystem, - ComputerWMIHelper.CimOperatingSystemShutdownMethod, - methodParameters, - operationOptions); + if (!InternalTestHooks.TestStopComputer) + { + CimMethodResult result = null; + + if (isLocalhost) + { + // Win32_ComputerSystem is a singleton hence FirstOrDefault() return the only instance returned by EnumerateInstances. + var computerSystem = cimSession.EnumerateInstances(ComputerWMIHelper.CimOperatingSystemNamespace, ComputerWMIHelper.WMI_Class_OperatingSystem).FirstOrDefault(); + + result = cimSession.InvokeMethod( + ComputerWMIHelper.CimOperatingSystemNamespace, + computerSystem, + ComputerWMIHelper.CimOperatingSystemShutdownMethod, + methodParameters, + operationOptions); + } + else + { + result = cimSession.InvokeMethod( + ComputerWMIHelper.CimOperatingSystemNamespace, + ComputerWMIHelper.WMI_Class_OperatingSystem, + ComputerWMIHelper.CimOperatingSystemShutdownMethod, + methodParameters, + operationOptions); + } + + retVal = Convert.ToInt32(result.ReturnValue.Value, CultureInfo.CurrentCulture); + } + else + { + retVal = InternalTestHooks.TestStopComputerResults; + } - int retVal = Convert.ToInt32(result.ReturnValue.Value, CultureInfo.CurrentCulture); if (retVal != 0) { var ex = new Win32Exception(retVal); string errMsg = StringUtil.Format(formatErrorMessage, computerName, ex.Message); - ErrorRecord error = new ErrorRecord( + ErrorRecord error = new( new InvalidOperationException(errMsg), ErrorFQEID, ErrorCategory.OperationStopped, computerName); cmdlet.WriteError(error); } @@ -6843,14 +2159,14 @@ internal static bool InvokeWin32ShutdownUsingWsman( catch (CimException ex) { string errMsg = StringUtil.Format(formatErrorMessage, computerName, ex.Message); - ErrorRecord error = new ErrorRecord(new InvalidOperationException(errMsg), ErrorFQEID, + ErrorRecord error = new(new InvalidOperationException(errMsg), ErrorFQEID, ErrorCategory.OperationStopped, computerName); cmdlet.WriteError(error); } catch (Exception ex) { string errMsg = StringUtil.Format(formatErrorMessage, computerName, ex.Message); - ErrorRecord error = new ErrorRecord(new InvalidOperationException(errMsg), ErrorFQEID, + ErrorRecord error = new(new InvalidOperationException(errMsg), ErrorFQEID, ErrorCategory.OperationStopped, computerName); cmdlet.WriteError(error); } @@ -6867,11 +2183,11 @@ internal static bool InvokeWin32ShutdownUsingWsman( /// /// Returns valid computer name or null on failure. /// - /// Computer name to validate + /// Computer name to validate. /// /// /// - /// Valid computer name + /// Valid computer name. internal static string ValidateComputerName( string nameToCheck, string shortLocalMachineName, @@ -6892,8 +2208,7 @@ internal static string ValidateComputerName( bool isIPAddress = false; try { - IPAddress unused; - isIPAddress = IPAddress.TryParse(nameToCheck, out unused); + isIPAddress = IPAddress.TryParse(nameToCheck, out _); } catch (Exception) { @@ -6926,6 +2241,7 @@ internal static string ValidateComputerName( return null; } + validatedComputerName = nameToCheck; } } @@ -6933,17 +2249,7 @@ internal static string ValidateComputerName( return validatedComputerName; } } -#endregion Helper - -#region Internal Enums - - internal enum TransportProtocol - { - DCOM = 1, - WSMan = 2 - } - -#endregion -}//End namespace + #endregion Helper +} #endif diff --git a/src/Microsoft.PowerShell.Commands.Management/commands/management/ComputerUnix.cs b/src/Microsoft.PowerShell.Commands.Management/commands/management/ComputerUnix.cs new file mode 100644 index 00000000000..22092116330 --- /dev/null +++ b/src/Microsoft.PowerShell.Commands.Management/commands/management/ComputerUnix.cs @@ -0,0 +1,207 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#if UNIX + +using System; +using System.Diagnostics; +using System.IO; +using System.Management.Automation; +using System.Management.Automation.Internal; +using System.Runtime.InteropServices; + +#nullable enable + +namespace Microsoft.PowerShell.Commands +{ +#region Restart-Computer + + /// + /// Cmdlet to restart computer. + /// + [Cmdlet(VerbsLifecycle.Restart, "Computer", SupportsShouldProcess = true, + HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2097060", RemotingCapability = RemotingCapability.SupportedByCommand)] + public sealed class RestartComputerCommand : CommandLineCmdletBase + { + // TODO: Support remote computers? + +#region "Overrides" + + /// + /// BeginProcessing. + /// + protected override void BeginProcessing() + { + if (InternalTestHooks.TestStopComputer) + { + var retVal = InternalTestHooks.TestStopComputerResults; + if (retVal != 0) + { + string errMsg = StringUtil.Format("Command returned 0x{0:X}", retVal); + ErrorRecord error = new ErrorRecord( + new InvalidOperationException(errMsg), "CommandFailed", ErrorCategory.OperationStopped, "localhost"); + WriteError(error); + } + return; + } + + RunShutdown("-r now"); + } +#endregion "Overrides" + } +#endregion Restart-Computer + +#region Stop-Computer + + /// + /// Cmdlet to stop computer. + /// + [Cmdlet(VerbsLifecycle.Stop, "Computer", SupportsShouldProcess = true, + HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2097151", RemotingCapability = RemotingCapability.SupportedByCommand)] + public sealed class StopComputerCommand : CommandLineCmdletBase + { + // TODO: Support remote computers? + +#region "Overrides" + + /// + /// BeginProcessing. + /// + protected override void BeginProcessing() + { + var args = "-P now"; + if (Platform.IsMacOS) + { + args = "now"; + } + if (InternalTestHooks.TestStopComputer) + { + var retVal = InternalTestHooks.TestStopComputerResults; + if (retVal != 0) + { + string errMsg = StringUtil.Format("Command returned 0x{0:X}", retVal); + ErrorRecord error = new ErrorRecord( + new InvalidOperationException(errMsg), "CommandFailed", ErrorCategory.OperationStopped, "localhost"); + WriteError(error); + } + return; + } + + RunShutdown(args); + } +#endregion "Overrides" + } + + /// + /// A base class for cmdlets that can run shell commands. + /// + public class CommandLineCmdletBase : PSCmdlet, IDisposable + { +#region Private Members + private Process? _process = null; +#endregion + +#region "IDisposable Members" + + /// + /// Releases all resources used by the . + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// Releases the unmanaged resources used by the + /// and optionally releases the managed resources. + /// + /// + /// to release both managed and unmanaged resources; + /// to release only unmanaged resources. + /// + protected virtual void Dispose(bool disposing) + { + if (disposing) + { + _process?.Dispose(); + } + } + +#endregion "IDisposable Members" + +#region "Overrides" + /// + /// To implement ^C. + /// + protected override void StopProcessing() + { + if (_process == null) { + return; + } + + try { + if (!_process.HasExited) { + _process.Kill(); + } + WriteObject(_process.ExitCode); + } + catch (InvalidOperationException) {} + catch (NotSupportedException) {} + } +#endregion "Overrides" + +#region "Internals" + + private static string? shutdownPath; + + /// + /// Run shutdown command. + /// + protected void RunShutdown(string args) + { + if (shutdownPath is null) + { + CommandInfo cmdinfo = CommandDiscovery.LookupCommandInfo( + "shutdown", CommandTypes.Application, + SearchResolutionOptions.None, CommandOrigin.Internal, this.Context); + + if (cmdinfo is not null) + { + shutdownPath = cmdinfo.Definition; + } + else + { + ErrorRecord error = new ErrorRecord( + new InvalidOperationException(ComputerResources.ShutdownCommandNotFound), "CommandNotFound", ErrorCategory.ObjectNotFound, targetObject: null); + ThrowTerminatingError(error); + } + } + + _process = new Process() + { + StartInfo = new ProcessStartInfo + { + FileName = shutdownPath, + Arguments = string.Empty, + RedirectStandardOutput = false, + RedirectStandardError = true, + UseShellExecute = false, + CreateNoWindow = true, + } + }; + _process.Start(); + _process.WaitForExit(); + if (_process.ExitCode != 0) + { + string stderr = _process.StandardError.ReadToEnd(); + ErrorRecord error = new ErrorRecord( + new InvalidOperationException(stderr), "CommandFailed", ErrorCategory.OperationStopped, null); + ThrowTerminatingError(error); + } + } +#endregion + } +#endregion +} +#endif diff --git a/src/Microsoft.PowerShell.Commands.Management/commands/management/ContentCommandBase.cs b/src/Microsoft.PowerShell.Commands.Management/commands/management/ContentCommandBase.cs index b53c46731f3..e7b3ada4ebb 100644 --- a/src/Microsoft.PowerShell.Commands.Management/commands/management/ContentCommandBase.cs +++ b/src/Microsoft.PowerShell.Commands.Management/commands/management/ContentCommandBase.cs @@ -1,6 +1,5 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. using System; using System.Collections.Generic; @@ -8,33 +7,38 @@ using System.Management.Automation; using System.Management.Automation.Internal; using System.Management.Automation.Provider; + using Dbg = System.Management.Automation; namespace Microsoft.PowerShell.Commands { /// - /// The base class for the */content commands + /// The base class for the */content commands. /// public class ContentCommandBase : CoreCommandWithCredentialsBase, IDisposable { #region Parameters /// - /// Gets or sets the path parameter to the command + /// Gets or sets the path parameter to the command. /// [Parameter(Position = 0, ParameterSetName = "Path", Mandatory = true, ValueFromPipelineByPropertyName = true)] public string[] Path { get; set; } /// - /// Gets or sets the literal path parameter to the command + /// Gets or sets the literal path parameter to the command. /// [Parameter(ParameterSetName = "LiteralPath", Mandatory = true, ValueFromPipeline = false, ValueFromPipelineByPropertyName = true)] - [Alias("PSPath")] + [Alias("PSPath", "LP")] public string[] LiteralPath { - get { return Path; } + get + { + return Path; + } + set { base.SuppressWildcardExpansion = true; @@ -43,39 +47,41 @@ public string[] LiteralPath } /// - /// Gets or sets the filter property + /// Gets or sets the filter property. /// [Parameter] public override string Filter { get { return base.Filter; } + set { base.Filter = value; } } /// - /// Gets or sets the include property + /// Gets or sets the include property. /// [Parameter] public override string[] Include { get { return base.Include; } + set { base.Include = value; } } /// - /// Gets or sets the exclude property + /// Gets or sets the exclude property. /// [Parameter] public override string[] Exclude { get { return base.Exclude; } + set { base.Exclude = value; } } /// - /// Gets or sets the force property + /// Gets or sets the force property. /// - /// /// /// Gives the provider guidance on how vigorous it should be about performing /// the operation. If true, the provider should do everything possible to perform @@ -85,11 +91,11 @@ public override string[] Exclude /// the destination is read-only, if force is true, the provider should copy over /// the existing read-only file. If force is false, the provider should write an error. /// - /// [Parameter] public override SwitchParameter Force { get { return base.Force; } + set { base.Force = value; } } @@ -105,29 +111,23 @@ public override SwitchParameter Force /// An array of content holder objects that contain the path information /// and content readers/writers for the item represented by the path information. /// - /// - internal List contentStreams = new List(); + internal List contentStreams = new(); /// - /// Wraps the content into a PSObject and adds context information as notes + /// Wraps the content into a PSObject and adds context information as notes. /// - /// /// /// The content being written out. /// - /// /// /// The number of blocks that have been read so far. /// - /// /// /// The context the content was retrieved from. /// - /// /// /// The context the command is being run under. /// - /// internal void WriteContentObject(object content, long readCount, PathInfo pathInfo, CmdletProviderContext context) { Dbg.Diagnostics.Assert( @@ -153,13 +153,10 @@ internal void WriteContentObject(object content, long readCount, PathInfo pathIn if (_currentContentItem != null && ((_currentContentItem.PathInfo == pathInfo) || - ( - String.Compare( + string.Equals( pathInfo.Path, _currentContentItem.PathInfo.Path, - StringComparison.OrdinalIgnoreCase) == 0) - ) - ) + StringComparison.OrdinalIgnoreCase))) { result = _currentContentItem.AttachNotes(result); } @@ -188,8 +185,9 @@ internal void WriteContentObject(object content, long readCount, PathInfo pathIn } else { - parentPath = SessionState.Path.ParseParent(pathInfo.Path, String.Empty, context); + parentPath = SessionState.Path.ParseParent(pathInfo.Path, string.Empty, context); } + note = new PSNoteProperty("PSParentPath", parentPath); result.Properties.Add(note, true); tracer.WriteLine("Attaching {0} = {1}", "PSParentPath", parentPath); @@ -233,7 +231,7 @@ internal void WriteContentObject(object content, long readCount, PathInfo pathIn result.Properties.Add(note, true); WriteObject(result); - } // WriteContentObject + } /// /// A cache of the notes that get added to the content items as they are written @@ -246,16 +244,14 @@ internal void WriteContentObject(object content, long readCount, PathInfo pathIn /// as they get written to the pipeline. An instance of this cache class is /// only valid for a single path. /// - internal class ContentPathsCache + internal sealed class ContentPathsCache { /// /// Constructs a content cache item. /// - /// /// /// The path information for which the cache will be bound. /// - /// public ContentPathsCache(PathInfo pathInfo) { PathInfo = pathInfo; @@ -264,56 +260,47 @@ public ContentPathsCache(PathInfo pathInfo) /// /// The path information for the cached item. /// - /// public PathInfo PathInfo { get; } /// /// The cached PSPath of the item. /// - /// - public String PSPath { get; set; } + public string PSPath { get; set; } /// /// The cached parent path of the item. /// - /// - public String ParentPath { get; set; } + public string ParentPath { get; set; } /// /// The cached drive for the item. /// - /// public PSDriveInfo Drive { get; set; } /// /// The cached provider of the item. /// - /// public ProviderInfo Provider { get; set; } /// /// The cached child name of the item. /// - /// - public String ChildName { get; set; } + public string ChildName { get; set; } /// /// Attaches the cached notes to the specified PSObject. /// - /// /// /// The PSObject to attached the cached notes to. /// - /// /// /// The PSObject that was passed in with the cached notes added. /// - /// public PSObject AttachNotes(PSObject content) { // Construct a provider qualified path as the Path note - PSNoteProperty note = new PSNoteProperty("PSPath", PSPath); + PSNoteProperty note = new("PSPath", PSPath); content.Properties.Add(note, true); tracer.WriteLine("Attaching {0} = {1}", "PSPath", PSPath); @@ -345,16 +332,14 @@ public PSObject AttachNotes(PSObject content) tracer.WriteLine("Attaching {0} = {1}", "PSProvider", Provider); return content; - } // AttachNotes - } // ContentPathsCache - + } + } /// /// A struct to hold the path information and the content readers/writers /// for an item. /// - /// - internal struct ContentHolder + internal readonly struct ContentHolder { internal ContentHolder( PathInfo pathInfo, @@ -363,39 +348,36 @@ internal ContentHolder( { if (pathInfo == null) { - throw PSTraceSource.NewArgumentNullException("pathInfo"); + throw PSTraceSource.NewArgumentNullException(nameof(pathInfo)); } PathInfo = pathInfo; Reader = reader; Writer = writer; - } // constructor + } internal PathInfo PathInfo { get; } internal IContentReader Reader { get; } internal IContentWriter Writer { get; } - } // struct ContentHolder + } /// - /// Closes the content readers and writers in the content holder array + /// Closes the content readers and writers in the content holder array. /// internal void CloseContent(List contentHolders, bool disposing) { if (contentHolders == null) { - throw PSTraceSource.NewArgumentNullException("contentHolders"); + throw PSTraceSource.NewArgumentNullException(nameof(contentHolders)); } foreach (ContentHolder holder in contentHolders) { try { - if (holder.Writer != null) - { - holder.Writer.Close(); - } + holder.Writer?.Close(); } catch (Exception e) // Catch-all OK. 3rd party callout { @@ -403,14 +385,13 @@ internal void CloseContent(List contentHolders, bool disposing) // and write out an error. ProviderInvocationException providerException = - new ProviderInvocationException( + new( "ProviderContentCloseError", SessionStateStrings.ProviderContentCloseError, holder.PathInfo.Provider, holder.PathInfo.Path, e); - // Log a provider health event MshLog.LogProviderHealthEvent( @@ -430,10 +411,7 @@ internal void CloseContent(List contentHolders, bool disposing) try { - if (holder.Reader != null) - { - holder.Reader.Close(); - } + holder.Reader?.Close(); } catch (Exception e) // Catch-all OK. 3rd party callout { @@ -441,14 +419,13 @@ internal void CloseContent(List contentHolders, bool disposing) // and write out an error. ProviderInvocationException providerException = - new ProviderInvocationException( + new( "ProviderContentCloseError", SessionStateStrings.ProviderContentCloseError, holder.PathInfo.Provider, holder.PathInfo.Path, e); - // Log a provider health event MshLog.LogProviderHealthEvent( @@ -466,22 +443,19 @@ internal void CloseContent(List contentHolders, bool disposing) } } } - } // CloseContent + } /// /// Overridden by derived classes to support ShouldProcess with /// the appropriate information. /// - /// /// /// The path to the item from which the content writer will be /// retrieved. /// - /// /// /// True if the action should continue or false otherwise. /// - /// internal virtual bool CallShouldProcess(string path) { return true; @@ -490,11 +464,9 @@ internal virtual bool CallShouldProcess(string path) /// /// Gets the IContentReaders for the current path(s) /// - /// /// /// An array of IContentReaders for the current path(s) /// - /// internal List GetContentReaders( string[] readerPaths, CmdletProviderContext currentCommandContext) @@ -505,7 +477,7 @@ internal List GetContentReaders( // Create the results array - List results = new List(); + List results = new(); foreach (PathInfo pathInfo in pathInfos) { @@ -563,48 +535,42 @@ internal List GetContentReaders( if (readers.Count == 1 && readers[0] != null) { ContentHolder holder = - new ContentHolder(pathInfo, readers[0], null); + new(pathInfo, readers[0], null); results.Add(holder); } } - } // foreach pathInfo in pathInfos + } return results; - } // GetContentReaders + } /// - /// Resolves the specified paths to PathInfo objects + /// Resolves the specified paths to PathInfo objects. /// - /// /// /// The paths to be resolved. Each path may contain glob characters. /// - /// /// /// If true, resolves the path even if it doesn't exist. /// - /// /// /// If true, allows a wildcard that returns no results. /// - /// /// /// The context under which the command is running. /// - /// /// /// An array of PathInfo objects that are the resolved paths for the /// parameter. /// - /// internal Collection ResolvePaths( string[] pathsToResolve, bool allowNonexistingPaths, bool allowEmptyResult, CmdletProviderContext currentCommandContext) { - Collection results = new Collection(); + Collection results = new(); foreach (string path in pathsToResolve) { @@ -684,7 +650,7 @@ internal Collection ResolvePaths( out drive); PathInfo pathInfo = - new PathInfo( + new( drive, provider, unresolvedPath, @@ -696,8 +662,8 @@ internal Collection ResolvePaths( if (pathNotFoundErrorRecord == null) { // Detect if the path resolution failed to resolve to a file. - String error = StringUtil.Format(NavigationResources.ItemNotFound, Path); - Exception e = new Exception(error); + string error = StringUtil.Format(NavigationResources.ItemNotFound, Path); + Exception e = new(error); pathNotFoundErrorRecord = new ErrorRecord( e, @@ -712,7 +678,7 @@ internal Collection ResolvePaths( } return results; - } // ResolvePaths + } #endregion protected members @@ -728,7 +694,7 @@ internal void Dispose(bool isDisposing) } /// - /// Dispose method in IDisposable + /// Dispose method in IDisposable. /// public void Dispose() { @@ -736,14 +702,6 @@ public void Dispose() GC.SuppressFinalize(this); } - /// - /// Finalizer - /// - ~ContentCommandBase() - { - Dispose(false); - } #endregion IDisposable - - } // ContentCommandBase -} // namespace Microsoft.PowerShell.Commands + } +} diff --git a/src/Microsoft.PowerShell.Commands.Management/commands/management/ControlPanelItemCommand.cs b/src/Microsoft.PowerShell.Commands.Management/commands/management/ControlPanelItemCommand.cs index 54c95b9c16e..3734eb82b0c 100644 --- a/src/Microsoft.PowerShell.Commands.Management/commands/management/ControlPanelItemCommand.cs +++ b/src/Microsoft.PowerShell.Commands.Management/commands/management/ControlPanelItemCommand.cs @@ -1,52 +1,53 @@ -// -// Copyright (C) Microsoft. All rights reserved. -// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +using System; +using System.Collections.Generic; using System.Collections.ObjectModel; using System.Diagnostics.CodeAnalysis; using System.Globalization; -using Microsoft.Win32; -using System; using System.Management.Automation; using System.Management.Automation.Internal; -using System.Collections.Generic; + +using Microsoft.Win32; + using Dbg = System.Management.Automation.Diagnostics; namespace Microsoft.PowerShell.Commands { /// - /// Represent a control panel item + /// Represent a control panel item. /// public sealed class ControlPanelItem { /// - /// Control panel applet name + /// Control panel applet name. /// public string Name { get; } /// - /// Control panel applet canonical name + /// Control panel applet canonical name. /// public string CanonicalName { get; } /// - /// Control panel applet category + /// Control panel applet category. /// [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] public string[] Category { get; } /// - /// Control panel applet description + /// Control panel applet description. /// public string Description { get; } /// - /// Control panel applet path + /// Control panel applet path. /// internal string Path { get; } /// - /// Internal constructor for ControlPanelItem + /// Internal constructor for ControlPanelItem. /// /// /// @@ -63,7 +64,7 @@ internal ControlPanelItem(string name, string canonicalName, string[] category, } /// - /// ToString method + /// ToString method. /// /// public override string ToString() @@ -73,7 +74,7 @@ public override string ToString() } /// - /// This class implements the base for ControlPanelItem commands + /// This class implements the base for ControlPanelItem commands. /// public abstract class ControlPanelItemBaseCommand : PSCmdlet { @@ -112,7 +113,7 @@ public abstract class ControlPanelItemBaseCommand : PSCmdlet internal ControlPanelItem[] ControlPanelItems = new ControlPanelItem[0]; /// - /// Get all executable control panel items + /// Get all executable control panel items. /// internal List AllControlPanelItems { @@ -141,6 +142,7 @@ internal List AllControlPanelItems break; } } + if (match) continue; } @@ -149,15 +151,17 @@ internal List AllControlPanelItems _allControlPanelItems.Add(item); } } + return _allControlPanelItems; } } + private List _allControlPanelItems; #region Cmdlet Overrides /// - /// Does the preprocessing for ControlPanelItem cmdlets + /// Does the preprocessing for ControlPanelItem cmdlets. /// protected override void BeginProcessing() { @@ -182,7 +186,7 @@ protected override void BeginProcessing() #endregion /// - /// Test if an item can be invoked + /// Test if an item can be invoked. /// /// /// @@ -192,7 +196,7 @@ private bool ContainVerbOpen(ShellFolderItem item) FolderItemVerbs verbs = item.Verbs(); foreach (FolderItemVerb verb in verbs) { - if (!String.IsNullOrEmpty(verb.Name) && + if (!string.IsNullOrEmpty(verb.Name) && (verb.Name.Equals(ControlPanelResources.VerbActionOpen, StringComparison.OrdinalIgnoreCase) || CompareVerbActionOpen(verb.Name))) { @@ -200,6 +204,7 @@ private bool ContainVerbOpen(ShellFolderItem item) break; } } + return result; } @@ -221,8 +226,8 @@ private static bool CompareVerbActionOpen(string verbActionName) foreach (ShellFolderItem item in allItems) { string canonicalName = (string)item.ExtendedProperty("System.ApplicationName"); - canonicalName = !String.IsNullOrEmpty(canonicalName) - ? canonicalName.Substring(0, canonicalName.IndexOf("\0", StringComparison.OrdinalIgnoreCase)) + canonicalName = !string.IsNullOrEmpty(canonicalName) + ? canonicalName.Substring(0, canonicalName.IndexOf('\0')) : null; if (canonicalName != null && canonicalName.Equals(RegionCanonicalName, StringComparison.OrdinalIgnoreCase)) @@ -254,7 +259,7 @@ private bool IsServerCoreOrHeadLessServer() { Dbg.Assert(installation != null, "the CurrentVersion subkey should exist"); - string installationType = (string)installation.GetValue("InstallationType", ""); + string installationType = (string)installation.GetValue("InstallationType", string.Empty); if (installationType.Equals("Server Core")) { @@ -265,7 +270,7 @@ private bool IsServerCoreOrHeadLessServer() using (System.Management.Automation.PowerShell ps = System.Management.Automation.PowerShell.Create()) { ps.AddScript(TestHeadlessServerScript); - Collection psObjectCollection = ps.Invoke(new object[0]); + Collection psObjectCollection = ps.Invoke(Array.Empty()); Dbg.Assert(psObjectCollection != null && psObjectCollection.Count == 1, "invoke should never return null, there should be only one return item"); if (LanguagePrimitives.IsTrue(PSObject.Base(psObjectCollection[0]))) { @@ -279,7 +284,7 @@ private bool IsServerCoreOrHeadLessServer() } /// - /// Get the category number and name map + /// Get the category number and name map. /// internal void GetCategoryMap() { @@ -295,14 +300,14 @@ internal void GetCategoryMap() foreach (ShellFolderItem category in catItems) { string path = category.Path; - string catNum = path.Substring(path.LastIndexOf("\\", StringComparison.OrdinalIgnoreCase) + 1); + string catNum = path.Substring(path.LastIndexOf('\\') + 1); CategoryMap.Add(catNum, category.Name); } } /// - /// Get control panel item by the category + /// Get control panel item by the category. /// /// /// @@ -354,7 +359,7 @@ internal List GetControlPanelItemByCategory(List - /// Get control panel item by the regular name + /// Get control panel item by the regular name. /// /// /// @@ -402,7 +407,7 @@ internal List GetControlPanelItemByName(List c } /// - /// Get control panel item by the canonical name + /// Get control panel item by the canonical name. /// /// /// @@ -430,10 +435,11 @@ internal List GetControlPanelItemByCanonicalName(List GetControlPanelItemByCanonicalName(List GetControlPanelItemByCanonicalName(List - /// Get control panel item by the ControlPanelItem instances + /// Get control panel item by the ControlPanelItem instances. /// /// /// @@ -540,7 +546,7 @@ internal List GetControlPanelItemsByInstance(List - /// Get all control panel items that is available in the "All Control Panel Items" category + /// Get all control panel items that is available in the "All Control Panel Items" category. /// [Cmdlet(VerbsCommon.Get, "ControlPanelItem", DefaultParameterSetName = RegularNameParameterSet, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=219982")] [OutputType(typeof(ControlPanelItem))] @@ -552,7 +558,7 @@ public sealed class GetControlPanelItemCommand : ControlPanelItemBaseCommand #region "Parameters" /// - /// Control panel item names + /// Control panel item names. /// [Parameter(Position = 0, ParameterSetName = RegularNameParameterSet, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true)] [ValidateNotNullOrEmpty] @@ -560,16 +566,18 @@ public sealed class GetControlPanelItemCommand : ControlPanelItemBaseCommand public string[] Name { get { return RegularNames; } + set { RegularNames = value; _nameSpecified = true; } } + private bool _nameSpecified = false; /// - /// Canonical names of control panel items + /// Canonical names of control panel items. /// [Parameter(Mandatory = true, ParameterSetName = CanonicalNameParameterSet)] [AllowNull] @@ -577,16 +585,18 @@ public string[] Name public string[] CanonicalName { get { return CanonicalNames; } + set { CanonicalNames = value; _canonicalNameSpecified = true; } } + private bool _canonicalNameSpecified = false; /// - /// Category of control panel items + /// Category of control panel items. /// [Parameter] [ValidateNotNullOrEmpty] @@ -594,18 +604,19 @@ public string[] CanonicalName public string[] Category { get { return CategoryNames; } + set { CategoryNames = value; _categorySpecified = true; } } + private bool _categorySpecified = false; #endregion "Parameters" /// - /// /// protected override void ProcessRecord() { @@ -629,7 +640,7 @@ protected override void ProcessRecord() string description = (string)item.ExtendedProperty("InfoTip"); string canonicalName = (string)item.ExtendedProperty("System.ApplicationName"); canonicalName = canonicalName != null - ? canonicalName.Substring(0, canonicalName.IndexOf("\0", StringComparison.OrdinalIgnoreCase)) + ? canonicalName.Substring(0, canonicalName.IndexOf('\0')) : null; int[] categories = (int[])item.ExtendedProperty("System.ControlPanel.Category"); string[] cateStrings = new string[categories.Length]; @@ -672,7 +683,7 @@ private static int CompareControlPanelItems(ControlPanelItem x, ControlPanelItem } /// - /// Show the specified control panel applet + /// Show the specified control panel applet. /// [Cmdlet(VerbsCommon.Show, "ControlPanelItem", DefaultParameterSetName = RegularNameParameterSet, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=219983")] public sealed class ShowControlPanelItemCommand : ControlPanelItemBaseCommand @@ -684,7 +695,7 @@ public sealed class ShowControlPanelItemCommand : ControlPanelItemBaseCommand #region "Parameters" /// - /// Control panel item names + /// Control panel item names. /// [Parameter(Position = 0, Mandatory = true, ParameterSetName = RegularNameParameterSet, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true)] [ValidateNotNullOrEmpty] @@ -692,11 +703,12 @@ public sealed class ShowControlPanelItemCommand : ControlPanelItemBaseCommand public string[] Name { get { return RegularNames; } + set { RegularNames = value; } } /// - /// Canonical names of control panel items + /// Canonical names of control panel items. /// [Parameter(Mandatory = true, ParameterSetName = CanonicalNameParameterSet)] [AllowNull] @@ -704,11 +716,12 @@ public string[] Name public string[] CanonicalName { get { return CanonicalNames; } + set { CanonicalNames = value; } } /// - /// Control panel items returned by Get-ControlPanelItem + /// Control panel items returned by Get-ControlPanelItem. /// [Parameter(Position = 0, ParameterSetName = ControlPanelItemParameterSet, ValueFromPipeline = true)] [ValidateNotNullOrEmpty] @@ -716,13 +729,13 @@ public string[] CanonicalName public ControlPanelItem[] InputObject { get { return ControlPanelItems; } + set { ControlPanelItems = value; } } #endregion "Parameters" /// - /// /// protected override void ProcessRecord() { diff --git a/src/Microsoft.PowerShell.Commands.Management/commands/management/ConvertPathCommand.cs b/src/Microsoft.PowerShell.Commands.Management/commands/management/ConvertPathCommand.cs index dd3d3d90502..33796b23378 100644 --- a/src/Microsoft.PowerShell.Commands.Management/commands/management/ConvertPathCommand.cs +++ b/src/Microsoft.PowerShell.Commands.Management/commands/management/ConvertPathCommand.cs @@ -1,10 +1,8 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. using System.Collections.ObjectModel; using System.Management.Automation; -using Dbg = System.Management.Automation; namespace Microsoft.PowerShell.Commands { @@ -13,14 +11,14 @@ namespace Microsoft.PowerShell.Commands /// a provider internal path. /// [Cmdlet(VerbsData.Convert, "Path", DefaultParameterSetName = "Path", SupportsTransactions = true, - HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113289", RemotingCapability = RemotingCapability.None)] + HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2096588", RemotingCapability = RemotingCapability.None)] [OutputType(typeof(string))] public class ConvertPathCommand : CoreCommandBase { #region Parameters /// - /// Gets or sets the path parameter to the command + /// Gets or sets the path parameter to the command. /// [Parameter(Position = 0, ParameterSetName = "Path", Mandatory = true, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true)] @@ -29,40 +27,50 @@ public string[] Path get { return _paths; - } // get + } set { _paths = value; - } // set - } // Path + } + } /// - /// Gets or sets the literal path parameter to the command + /// Gets or sets the literal path parameter to the command. /// [Parameter(ParameterSetName = "LiteralPath", Mandatory = true, ValueFromPipeline = false, ValueFromPipelineByPropertyName = true)] - [Alias("PSPath")] + [Alias("PSPath", "LP")] public string[] LiteralPath { get { return _paths; - } // get + } set { base.SuppressWildcardExpansion = true; _paths = value; - } // set - } // LiteralPath + } + } + + /// + /// Gets or sets the force property. + /// + [Parameter] + public override SwitchParameter Force + { + get => base.Force; + set => base.Force = value; + } #endregion Parameters #region parameter data /// - /// The path(s) to the item(s) to convert + /// The path(s) to the item(s) to convert. /// private string[] _paths; @@ -123,10 +131,8 @@ protected override void ProcessRecord() continue; } } - } // ProcessRecord + } #endregion Command code - - } // ConvertPathCommand -} // namespace Microsoft.PowerShell.Commands - + } +} diff --git a/src/Microsoft.PowerShell.Commands.Management/commands/management/CopyPropertyCommand.cs b/src/Microsoft.PowerShell.Commands.Management/commands/management/CopyPropertyCommand.cs index b2b90c63b19..ff13448bc09 100644 --- a/src/Microsoft.PowerShell.Commands.Management/commands/management/CopyPropertyCommand.cs +++ b/src/Microsoft.PowerShell.Commands.Management/commands/management/CopyPropertyCommand.cs @@ -1,9 +1,7 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. using System.Management.Automation; -using Dbg = System.Management.Automation; namespace Microsoft.PowerShell.Commands { @@ -11,31 +9,36 @@ namespace Microsoft.PowerShell.Commands /// A command to copy a property on an item. /// [Cmdlet(VerbsCommon.Copy, "ItemProperty", DefaultParameterSetName = "Path", SupportsShouldProcess = true, SupportsTransactions = true, - HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113293")] + HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2096589")] public class CopyItemPropertyCommand : PassThroughItemPropertyCommandBase { #region Parameters /// - /// Gets or sets the path parameter to the command + /// Gets or sets the path parameter to the command. /// [Parameter(Position = 0, ParameterSetName = "Path", Mandatory = true, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true)] public string[] Path { get { return paths; } + set { paths = value; } } /// - /// Gets or sets the literal path parameter to the command + /// Gets or sets the literal path parameter to the command. /// [Parameter(ParameterSetName = "LiteralPath", Mandatory = true, ValueFromPipeline = false, ValueFromPipelineByPropertyName = true)] - [Alias("PSPath")] + [Alias("PSPath", "LP")] public string[] LiteralPath { - get { return paths; } + get + { + return paths; + } + set { base.SuppressWildcardExpansion = true; @@ -44,9 +47,8 @@ public string[] LiteralPath } /// - /// The name of the property to create on the item + /// The name of the property to create on the item. /// - /// [Parameter(Position = 2, Mandatory = true, ValueFromPipelineByPropertyName = true)] [Alias("PSProperty")] public string Name { get; set; } @@ -54,7 +56,6 @@ public string[] LiteralPath /// /// The path to the destination item to copy the property to. /// - /// [Parameter(Mandatory = true, Position = 1, ValueFromPipelineByPropertyName = true)] public string Destination { get; set; } @@ -63,16 +64,13 @@ public string[] LiteralPath /// that require dynamic parameters should override this method and return the /// dynamic parameter object. /// - /// /// /// The context under which the command is running. /// - /// /// /// An object representing the dynamic parameters for the cmdlet or null if there /// are none. /// - /// internal override object GetDynamicParameters(CmdletProviderContext context) { if (Path != null && Path.Length > 0) @@ -84,13 +82,14 @@ internal override object GetDynamicParameters(CmdletProviderContext context) Name, context); } + return InvokeProvider.Property.CopyPropertyDynamicParameters( ".", Name, Destination, Name, context); - } // GetDynamicParameters + } #endregion Parameters @@ -101,7 +100,7 @@ internal override object GetDynamicParameters(CmdletProviderContext context) #region Command code /// - /// Copies the property from one item to another + /// Copies the property from one item to another. /// protected override void ProcessRecord() { @@ -149,9 +148,8 @@ protected override void ProcessRecord() continue; } } - } // ProcessRecord + } #endregion Command code - - } // CopyItemPropertyCommand -} // namespace Microsoft.PowerShell.Commands + } +} diff --git a/src/Microsoft.PowerShell.Commands.Management/commands/management/Eventlog.cs b/src/Microsoft.PowerShell.Commands.Management/commands/management/Eventlog.cs index 2d5ff805e75..c667116fdd0 100644 --- a/src/Microsoft.PowerShell.Commands.Management/commands/management/Eventlog.cs +++ b/src/Microsoft.PowerShell.Commands.Management/commands/management/Eventlog.cs @@ -1,6 +1,5 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. using System; using System.Collections; @@ -15,7 +14,7 @@ namespace Microsoft.PowerShell.Commands { #region GetEventLogCommand /// - /// This class implements the Get-EventLog command + /// This class implements the Get-EventLog command. /// /// /// The CLR EventLogEntryCollection class has problems with managing @@ -35,28 +34,28 @@ namespace Microsoft.PowerShell.Commands /// [Cmdlet(VerbsCommon.Get, "EventLog", DefaultParameterSetName = "LogName", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113314", RemotingCapability = RemotingCapability.SupportedByCommand)] - [OutputType(typeof(EventLog), typeof(EventLogEntry), typeof(String))] + [OutputType(typeof(EventLog), typeof(EventLogEntry), typeof(string))] public sealed class GetEventLogCommand : PSCmdlet { #region Parameters /// - /// Read eventlog entries from this log + /// Read eventlog entries from this log. /// [Parameter(Position = 0, Mandatory = true, ParameterSetName = "LogName")] [Alias("LN")] public string LogName { get; set; } /// - /// Read eventlog entries from this computer + /// Read eventlog entries from this computer. /// - [Parameter()] - [ValidateNotNullOrEmpty()] + [Parameter] + [ValidateNotNullOrEmpty] [Alias("Cn")] [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] - public string[] ComputerName { get; set; } = new string[0]; + public string[] ComputerName { get; set; } = Array.Empty(); /// - /// Read only this number of entries + /// Read only this number of entries. /// [Parameter(ParameterSetName = "LogName")] [ValidateRange(0, Int32.MaxValue)] @@ -70,6 +69,7 @@ public sealed class GetEventLogCommand : PSCmdlet public DateTime After { get { return _after; } + set { _after = value; @@ -77,6 +77,7 @@ public DateTime After _isFilterSpecified = true; } } + private DateTime _after; /// @@ -87,6 +88,7 @@ public DateTime After public DateTime Before { get { return _before; } + set { _before = value; @@ -94,6 +96,7 @@ public DateTime Before _isFilterSpecified = true; } } + private DateTime _before; /// @@ -102,101 +105,109 @@ public DateTime Before [Parameter(ParameterSetName = "LogName")] [ValidateNotNullOrEmpty] [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] - public String[] UserName + public string[] UserName { get { return _username; } + set { _username = value; _isFilterSpecified = true; } } - private String[] _username; + + private string[] _username; /// - /// match eventlog entries by the InstanceIds - /// gets or sets an array of instanceIds + /// Match eventlog entries by the InstanceIds + /// gets or sets an array of instanceIds. /// [Parameter(Position = 1, ParameterSetName = "LogName")] - [ValidateNotNullOrEmpty()] + [ValidateNotNullOrEmpty] [ValidateRangeAttribute((long)0, long.MaxValue)] [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] public long[] InstanceId { get { return _instanceIds; } + set { _instanceIds = value; _isFilterSpecified = true; } } - private long[] _instanceIds = null; + private long[] _instanceIds = null; /// - /// match eventlog entries by the Index - /// gets or sets an array of indexes + /// Match eventlog entries by the Index + /// gets or sets an array of indexes. /// [Parameter(ParameterSetName = "LogName")] - [ValidateNotNullOrEmpty()] + [ValidateNotNullOrEmpty] [ValidateRangeAttribute((int)1, int.MaxValue)] [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] public int[] Index { get { return _indexes; } + set { _indexes = value; _isFilterSpecified = true; } } - private int[] _indexes = null; + private int[] _indexes = null; /// - /// match eventlog entries by the EntryType - /// gets or sets an array of EntryTypes + /// Match eventlog entries by the EntryType + /// gets or sets an array of EntryTypes. /// [Parameter(ParameterSetName = "LogName")] - [ValidateNotNullOrEmpty()] + [ValidateNotNullOrEmpty] [ValidateSetAttribute(new string[] { "Error", "Information", "FailureAudit", "SuccessAudit", "Warning" })] [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] [Alias("ET")] public string[] EntryType { get { return _entryTypes; } + set { _entryTypes = value; _isFilterSpecified = true; } } + private string[] _entryTypes = null; /// - /// get or sets an array of Source + /// Get or sets an array of Source. /// [Parameter(ParameterSetName = "LogName")] - [ValidateNotNullOrEmpty()] + [ValidateNotNullOrEmpty] [Alias("ABO")] [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] public string[] Source { get { return _sources; } + set { _sources = value; _isFilterSpecified = true; } } + private string[] _sources; /// - /// Get or Set Message string to searched in EventLog + /// Get or Set Message string to searched in EventLog. /// [Parameter(ParameterSetName = "LogName")] - [ValidateNotNullOrEmpty()] + [ValidateNotNullOrEmpty] [Alias("MSG")] public string Message { @@ -204,29 +215,30 @@ public string Message { return _message; } + set { _message = value; _isFilterSpecified = true; } } + private string _message; /// - /// returns Log Entry as base object + /// Returns Log Entry as base object. /// [Parameter(ParameterSetName = "LogName")] public SwitchParameter AsBaseObject { get; set; } /// - /// Return the Eventlog objects rather than the log contents + /// Return the Eventlog objects rather than the log contents. /// [Parameter(ParameterSetName = "List")] public SwitchParameter List { get; set; } - /// - /// Return the log names rather than the EventLog objects + /// Return the log names rather than the EventLog objects. /// [Parameter(ParameterSetName = "List")] public SwitchParameter AsString @@ -235,25 +247,27 @@ public SwitchParameter AsString { return _asString; } + set { _asString = value; } } + private bool _asString /* = false */; #endregion Parameters #region Overrides /// - /// Sets true when Filter is Specified + /// Sets true when Filter is Specified. /// private bool _isFilterSpecified = false; private bool _isDateSpecified = false; private bool _isThrowError = true; /// - /// Process the specified logs + /// Process the specified logs. /// protected override void BeginProcessing() { @@ -311,7 +325,7 @@ protected override void BeginProcessing() } } } - } // ProcessRecord + } #endregion Overrides #region Private @@ -346,7 +360,11 @@ private void OutputEvents(string logName) } catch (InvalidOperationException e) { - if (processing) throw; + if (processing) + { + throw; + } + ThrowTerminatingError(new ErrorRecord( e, // default exception text is OK "EventLogNotFound", @@ -407,7 +425,8 @@ private void Process(EventLog log) + ": " + e.Message); throw; } - if ((null != entry) && + + if ((entry != null) && ((lastindex == Int32.MinValue || lastindex - entry.Index == 1))) { @@ -417,11 +436,12 @@ private void Process(EventLog log) if (!FiltersMatch(entry)) continue; } + if (!AsBaseObject) { - //wrapping in PSobject to insert into PStypesnames + // wrapping in PSobject to insert into PStypesnames PSObject logentry = new PSObject(entry); - //inserting at zero position in reverse order + // inserting at zero position in reverse order logentry.TypeNames.Insert(0, logentry.ImmediateBaseObject + "#" + log.Log + "/" + entry.Source); logentry.TypeNames.Insert(0, logentry.ImmediateBaseObject + "#" + log.Log + "/" + entry.Source + "/" + entry.InstanceId); WriteObject(logentry); @@ -432,18 +452,18 @@ private void Process(EventLog log) WriteObject(entry); matchesfound = true; } + processed++; } } + if (!matchesfound && _isThrowError) { - Exception Ex = new ArgumentException(StringUtil.Format(EventlogResources.NoEntriesFound, log.Log, "")); + Exception Ex = new ArgumentException(StringUtil.Format(EventlogResources.NoEntriesFound, log.Log, string.Empty)); WriteError(new ErrorRecord(Ex, "GetEventLogNoEntriesFound", ErrorCategory.ObjectNotFound, null)); } } - - private bool FiltersMatch(EventLogEntry entry) { if (_indexes != null) @@ -453,6 +473,7 @@ private bool FiltersMatch(EventLogEntry entry) return false; } } + if (_instanceIds != null) { if (!((IList)_instanceIds).Contains(entry.InstanceId)) @@ -460,19 +481,25 @@ private bool FiltersMatch(EventLogEntry entry) return false; } } + if (_entryTypes != null) { bool entrymatch = false; foreach (string type in _entryTypes) { - if (type.Equals(entry.EntryType.ToString(), StringComparison.CurrentCultureIgnoreCase)) + if (type.Equals(entry.EntryType.ToString(), StringComparison.OrdinalIgnoreCase)) { entrymatch = true; break; } } - if (!entrymatch) return entrymatch; + + if (!entrymatch) + { + return entrymatch; + } } + if (_sources != null) { bool sourcematch = false; @@ -482,6 +509,7 @@ private bool FiltersMatch(EventLogEntry entry) { _isThrowError = false; } + WildcardPattern wildcardpattern = WildcardPattern.Get(source, WildcardOptions.IgnoreCase); if (wildcardpattern.IsMatch(entry.Source)) { @@ -489,20 +517,27 @@ private bool FiltersMatch(EventLogEntry entry) break; } } - if (!sourcematch) return sourcematch; + + if (!sourcematch) + { + return sourcematch; + } } + if (_message != null) { if (WildcardPattern.ContainsWildcardCharacters(_message)) { _isThrowError = false; } + WildcardPattern wildcardpattern = WildcardPattern.Get(_message, WildcardOptions.IgnoreCase); if (!wildcardpattern.IsMatch(entry.Message)) { return false; } } + if (_username != null) { bool usernamematch = false; @@ -519,8 +554,13 @@ private bool FiltersMatch(EventLogEntry entry) } } } - if (!usernamematch) return usernamematch; + + if (!usernamematch) + { + return usernamematch; + } } + if (_isDateSpecified) { _isThrowError = false; @@ -554,10 +594,16 @@ private bool FiltersMatch(EventLogEntry entry) } } } - if (!datematch) return datematch; + + if (!datematch) + { + return datematch; + } } + return true; } + private List GetMatchingLogs(string pattern) { WildcardPattern wildcardPattern = WildcardPattern.Get(pattern, WildcardOptions.IgnoreCase); @@ -588,16 +634,16 @@ private List GetMatchingLogs(string pattern) return matchingLogs; } - //private string ErrorBase = "EventlogResources"; + // private string ErrorBase = "EventlogResources"; private DateTime _initial = new DateTime(); #endregion Private - }//GetEventLogCommand + } #endregion GetEventLogCommand #region ClearEventLogCommand /// - /// This class implements the Clear-EventLog command + /// This class implements the Clear-EventLog command. /// [Cmdlet(VerbsCommon.Clear, "EventLog", SupportsShouldProcess = true, @@ -614,7 +660,6 @@ public sealed class ClearEventLogCommand : PSCmdlet [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] public string[] LogName { get; set; } - /// /// Clear eventlog entries from these Computers. /// @@ -629,14 +674,14 @@ public sealed class ClearEventLogCommand : PSCmdlet #region Overrides /// - /// Does the processing + /// Does the processing. /// protected override void BeginProcessing() { string computer = string.Empty; foreach (string compName in ComputerName) { - if ((compName.Equals("localhost", StringComparison.CurrentCultureIgnoreCase)) || (compName.Equals(".", StringComparison.OrdinalIgnoreCase))) + if ((compName.Equals("localhost", StringComparison.OrdinalIgnoreCase)) || (compName.Equals(".", StringComparison.OrdinalIgnoreCase))) { computer = "localhost"; } @@ -644,6 +689,7 @@ protected override void BeginProcessing() { computer = compName; } + foreach (string eventString in LogName) { try @@ -654,10 +700,12 @@ protected override void BeginProcessing() WriteError(er); continue; } + if (!ShouldProcess(StringUtil.Format(EventlogResources.ClearEventLogWarning, eventString, computer))) { continue; } + EventLog Log = new EventLog(eventString, compName); Log.Clear(); } @@ -683,15 +731,15 @@ protected override void BeginProcessing() } } - //beginprocessing + // beginprocessing #endregion Overrides - }//ClearEventLogCommand + } #endregion ClearEventLogCommand #region WriteEventLogCommand /// - /// This class implements the Write-EventLog command + /// This class implements the Write-EventLog command. /// [Cmdlet(VerbsCommunications.Write, "EventLog", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=135281", RemotingCapability = RemotingCapability.SupportedByCommand)] @@ -699,14 +747,13 @@ public sealed class WriteEventLogCommand : PSCmdlet { #region Parameters /// - /// Write eventlog entries in this log + /// Write eventlog entries in this log. /// [Parameter(Position = 0, Mandatory = true)] [Alias("LN")] [ValidateNotNullOrEmpty] public string LogName { get; set; } - /// /// The source by which the application is registered on the specified computer. /// @@ -748,9 +795,8 @@ public sealed class WriteEventLogCommand : PSCmdlet [ValidateLength(0, 32766)] public string Message { get; set; } - /// - /// Write eventlog entries of this log + /// Write eventlog entries of this log. /// [Parameter] [Alias("RD")] @@ -759,7 +805,7 @@ public sealed class WriteEventLogCommand : PSCmdlet public byte[] RawData { get; set; } /// - /// Write eventlog entries of this log + /// Write eventlog entries of this log. /// [Parameter] [Alias("CN")] @@ -768,7 +814,7 @@ public sealed class WriteEventLogCommand : PSCmdlet public string ComputerName { get; set; } = "."; #endregion Parameters - # region private + #region private private void WriteNonTerminatingError(Exception exception, string errorId, string errorMessage, ErrorCategory category) @@ -781,12 +827,12 @@ private void WriteNonTerminatingError(Exception exception, string errorId, strin #region Overrides /// - /// Does the processing + /// Does the processing. /// protected override void BeginProcessing() { string _computerName = string.Empty; - if ((ComputerName.Equals("localhost", StringComparison.CurrentCultureIgnoreCase)) || (ComputerName.Equals(".", StringComparison.OrdinalIgnoreCase))) + if ((ComputerName.Equals("localhost", StringComparison.OrdinalIgnoreCase)) || (ComputerName.Equals(".", StringComparison.OrdinalIgnoreCase))) { _computerName = "localhost"; } @@ -794,6 +840,7 @@ protected override void BeginProcessing() { _computerName = ComputerName; } + try { if (!(EventLog.SourceExists(Source, ComputerName))) @@ -831,15 +878,15 @@ protected override void BeginProcessing() { WriteNonTerminatingError(ex, "PathDoesNotExist", StringUtil.Format(EventlogResources.PathDoesNotExist, null, ComputerName, null), ErrorCategory.InvalidOperation); } - }//beginprocessing + } #endregion Overrides - }//WriteEventLogCommand + } #endregion WriteEventLogCommand #region LimitEventLogCommand /// - /// This class implements the Limit-EventLog command + /// This class implements the Limit-EventLog command. /// [Cmdlet(VerbsData.Limit, "EventLog", SupportsShouldProcess = true, @@ -875,12 +922,14 @@ public sealed class LimitEventLogCommand : PSCmdlet public Int32 RetentionDays { get { return _retention; } + set { _retention = value; _retentionSpecified = true; } } + private Int32 _retention; private bool _retentionSpecified = false; /// @@ -894,12 +943,14 @@ public Int32 RetentionDays public System.Diagnostics.OverflowAction OverflowAction { get { return _overflowaction; } + set { _overflowaction = value; _overflowSpecified = true; } } + private System.Diagnostics.OverflowAction _overflowaction; private bool _overflowSpecified = false; /// @@ -910,17 +961,19 @@ public System.Diagnostics.OverflowAction OverflowAction public Int64 MaximumSize { get { return _maximumKilobytes; } + set { _maximumKilobytes = value; _maxkbSpecified = true; } } + private Int64 _maximumKilobytes; private bool _maxkbSpecified = false; #endregion Parameters - # region private + #region private private void WriteNonTerminatingError(Exception exception, string resourceId, string errorId, ErrorCategory category, string _logName, string _compName) { @@ -933,7 +986,7 @@ private void WriteNonTerminatingError(Exception exception, string resourceId, st #region Overrides /// - /// Does the processing + /// Does the processing. /// protected override void @@ -942,7 +995,7 @@ protected override string computer = string.Empty; foreach (string compname in ComputerName) { - if ((compname.Equals("localhost", StringComparison.CurrentCultureIgnoreCase)) || (compname.Equals(".", StringComparison.OrdinalIgnoreCase))) + if ((compname.Equals("localhost", StringComparison.OrdinalIgnoreCase)) || (compname.Equals(".", StringComparison.OrdinalIgnoreCase))) { computer = "localhost"; } @@ -950,6 +1003,7 @@ protected override { computer = compname; } + foreach (string logname in LogName) { try @@ -1001,6 +1055,7 @@ protected override { newLog.ModifyOverflowPolicy(_overflowaction, _minRetention); } + if (_maxkbSpecified) { int kiloByte = 1024; @@ -1030,19 +1085,20 @@ protected override { WriteNonTerminatingError(ex, EventlogResources.ValueOutofRange, "ValueOutofRange", ErrorCategory.InvalidData, null, null); } + continue; } } } } - # endregion override + #endregion override } #endregion LimitEventLogCommand #region ShowEventLogCommand /// - /// This class implements the Show-EventLog command + /// This class implements the Show-EventLog command. /// [Cmdlet(VerbsCommon.Show, "EventLog", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=135257", RemotingCapability = RemotingCapability.SupportedByCommand)] @@ -1051,7 +1107,7 @@ public sealed class ShowEventLogCommand : PSCmdlet #region Parameters /// - /// show eventviewer of this computer. + /// Show eventviewer of this computer. /// [Parameter(Position = 0)] [Alias("CN")] @@ -1064,7 +1120,7 @@ public sealed class ShowEventLogCommand : PSCmdlet #region Overrides /// - /// Does the processing + /// Does the processing. /// protected override void @@ -1097,11 +1153,11 @@ protected override WriteError(er); } } - # endregion override + #endregion override } #endregion ShowEventLogCommand - # region NewEventLogCommand + #region NewEventLogCommand /// /// This cmdlet creates the new event log .This cmdlet can also be used to /// configure a new source for writing entries to an event log on the local @@ -1125,75 +1181,74 @@ protected override [Cmdlet(VerbsCommon.New, "EventLog", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=135235", RemotingCapability = RemotingCapability.SupportedByCommand)] public class NewEventLogCommand : PSCmdlet { - # region Parameter + #region Parameter /// /// The following is the definition of the input parameter "CategoryResourceFile". /// Specifies the path of the resource file that contains category strings for /// the source - /// Resource File is expected to be present in Local/Remote Machines + /// Resource File is expected to be present in Local/Remote Machines. /// [Parameter] [ValidateNotNullOrEmpty] [Alias("CRF")] - public String CategoryResourceFile { get; set; } + public string CategoryResourceFile { get; set; } /// /// The following is the definition of the input parameter "ComputerName". - /// Specify the Computer Name. The default is local computer + /// Specify the Computer Name. The default is local computer. /// [Parameter(Position = 2)] [ValidateNotNullOrEmpty] [Alias("CN")] [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] - public String[] ComputerName { get; set; } = { "." }; + public string[] ComputerName { get; set; } = { "." }; /// /// The following is the definition of the input parameter "LogName". - /// Specifies the name of the log - /// + /// Specifies the name of the log. /// [Parameter(Mandatory = true, Position = 0)] [ValidateNotNullOrEmpty] [Alias("LN")] - public String LogName { get; set; } + public string LogName { get; set; } /// /// The following is the definition of the input parameter "MessageResourceFile". /// Specifies the path of the message resource file that contains message /// formatting strings for the source - /// Resource File is expected to be present in Local/Remote Machines + /// Resource File is expected to be present in Local/Remote Machines. /// [Parameter] [ValidateNotNullOrEmpty] [Alias("MRF")] - public String MessageResourceFile { get; set; } + public string MessageResourceFile { get; set; } /// /// The following is the definition of the input parameter "ParameterResourceFile". /// Specifies the path of the resource file that contains message parameter /// strings for the source - /// Resource File is expected to be present in Local/Remote Machines + /// Resource File is expected to be present in Local/Remote Machines. /// [Parameter] [ValidateNotNullOrEmpty] [Alias("PRF")] - public String ParameterResourceFile { get; set; } + public string ParameterResourceFile { get; set; } /// /// The following is the definition of the input parameter "Source". - /// Specifies the Source of the EventLog + /// Specifies the Source of the EventLog. /// [Parameter(Mandatory = true, Position = 1)] [ValidateNotNullOrEmpty] [Alias("SRC")] [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] - public String[] Source { get; set; } + public string[] Source { get; set; } - # endregion Parameter + #endregion Parameter - # region private + #region private private void WriteNonTerminatingError(Exception exception, string resourceId, string errorId, ErrorCategory category, string _logName, string _compName, string _source, string _resourceFile) { @@ -1201,9 +1256,9 @@ private void WriteNonTerminatingError(Exception exception, string resourceId, st WriteError(new ErrorRecord(ex, errorId, category, null)); } - # endregion private + #endregion private - # region override + #region override /// /// BeginProcessing method. /// @@ -1212,7 +1267,7 @@ protected override void BeginProcessing() string computer = string.Empty; foreach (string compname in ComputerName) { - if ((compname.Equals("localhost", StringComparison.CurrentCultureIgnoreCase)) || (compname.Equals(".", StringComparison.OrdinalIgnoreCase))) + if ((compname.Equals("localhost", StringComparison.OrdinalIgnoreCase)) || (compname.Equals(".", StringComparison.OrdinalIgnoreCase))) { computer = "localhost"; } @@ -1220,6 +1275,7 @@ protected override void BeginProcessing() { computer = compname; } + try { foreach (string _sourceName in Source) @@ -1228,11 +1284,11 @@ protected override void BeginProcessing() { EventSourceCreationData newEventSource = new EventSourceCreationData(_sourceName, LogName); newEventSource.MachineName = compname; - if (!String.IsNullOrEmpty(MessageResourceFile)) + if (!string.IsNullOrEmpty(MessageResourceFile)) newEventSource.MessageResourceFile = MessageResourceFile; - if (!String.IsNullOrEmpty(ParameterResourceFile)) + if (!string.IsNullOrEmpty(ParameterResourceFile)) newEventSource.ParameterResourceFile = ParameterResourceFile; - if (!String.IsNullOrEmpty(CategoryResourceFile)) + if (!string.IsNullOrEmpty(CategoryResourceFile)) newEventSource.CategoryResourceFile = CategoryResourceFile; EventLog.CreateEventSource(newEventSource); } @@ -1262,9 +1318,9 @@ protected override void BeginProcessing() } } } - //End BeginProcessing() + // End BeginProcessing() #endregion override - }//End Class + } #endregion NewEventLogCommand #region RemoveEventLogCommand @@ -1281,18 +1337,18 @@ public class RemoveEventLogCommand : PSCmdlet { /// /// The following is the definition of the input parameter "ComputerName". - /// Specifies the Computer Name + /// Specifies the Computer Name. /// [Parameter(Position = 1)] [ValidateNotNull] [ValidateNotNullOrEmpty] [Alias("CN")] [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] - public String[] ComputerName { get; set; } = { "." }; + public string[] ComputerName { get; set; } = { "." }; /// /// The following is the definition of the input parameter "LogName". - /// Specifies the Event Log Name + /// Specifies the Event Log Name. /// [Parameter(Mandatory = true, Position = 0, ParameterSetName = "Default")] @@ -1300,11 +1356,11 @@ public class RemoveEventLogCommand : PSCmdlet [ValidateNotNullOrEmpty] [Alias("LN")] [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] - public String[] LogName { get; set; } + public string[] LogName { get; set; } /// /// The following is the definition of the input parameter "RemoveSource". - /// Specifies either to remove the event log and and associated source or + /// Specifies either to remove the event log and associated source or /// source. alone. /// When this parameter is not specified, the cmdlet uses Delete Method which /// clears the eventlog and also the source associated with it. @@ -1316,8 +1372,7 @@ public class RemoveEventLogCommand : PSCmdlet [ValidateNotNullOrEmpty] [Alias("SRC")] [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] - public String[] Source { get; set; } - + public string[] Source { get; set; } /// /// BeginProcessing method. @@ -1329,7 +1384,7 @@ protected override void BeginProcessing() string computer = string.Empty; foreach (string compName in ComputerName) { - if ((compName.Equals("localhost", StringComparison.CurrentCultureIgnoreCase)) || (compName.Equals(".", StringComparison.OrdinalIgnoreCase))) + if ((compName.Equals("localhost", StringComparison.OrdinalIgnoreCase)) || (compName.Equals(".", StringComparison.OrdinalIgnoreCase))) { computer = "localhost"; } @@ -1337,6 +1392,7 @@ protected override void BeginProcessing() { computer = compName; } + if (ParameterSetName.Equals("Default")) { foreach (string log in LogName) @@ -1349,6 +1405,7 @@ protected override void BeginProcessing() { continue; } + EventLog.Delete(log, compName); } else @@ -1378,11 +1435,12 @@ protected override void BeginProcessing() { continue; } + EventLog.DeleteEventSource(src, compName); } else { - ErrorRecord er = new ErrorRecord(new InvalidOperationException(StringUtil.Format(EventlogResources.SourceDoesNotExist, "", computer, src)), null, ErrorCategory.InvalidOperation, null); + ErrorRecord er = new ErrorRecord(new InvalidOperationException(StringUtil.Format(EventlogResources.SourceDoesNotExist, string.Empty, computer, src)), null, ErrorCategory.InvalidOperation, null); WriteError(er); continue; } @@ -1402,9 +1460,8 @@ protected override void BeginProcessing() ErrorRecord er = new ErrorRecord(ex, "NewEventlogException", ErrorCategory.SecurityError, null); WriteError(er); } - }//End BeginProcessing() - }//End Class + } + } #endregion RemoveEventLogCommand -}//Microsoft.PowerShell.Commands - +} diff --git a/src/Microsoft.PowerShell.Commands.Management/commands/management/GetChildrenCommand.cs b/src/Microsoft.PowerShell.Commands.Management/commands/management/GetChildrenCommand.cs index 591d6e00370..c049370e4b4 100644 --- a/src/Microsoft.PowerShell.Commands.Management/commands/management/GetChildrenCommand.cs +++ b/src/Microsoft.PowerShell.Commands.Management/commands/management/GetChildrenCommand.cs @@ -1,22 +1,19 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. -using System; using System.Management.Automation; + using Dbg = System.Management.Automation; namespace Microsoft.PowerShell.Commands { /// /// The get-childitem command class. - /// This command lists the contents of a container + /// This command lists the contents of a container. /// - /// /// /// - /// - [Cmdlet(VerbsCommon.Get, "ChildItem", DefaultParameterSetName = "Items", SupportsTransactions = true, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113308")] + [Cmdlet(VerbsCommon.Get, "ChildItem", DefaultParameterSetName = "Items", SupportsTransactions = true, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2096492")] public class GetChildItemCommand : CoreCommandBase { /// @@ -30,25 +27,10 @@ public class GetChildItemCommand : CoreCommandBase private const string childrenSet = "Items"; private const string literalChildrenSet = "LiteralItems"; -#if RELATIONSHIP_SUPPORTED - // 2004/11/24-JeffJon - Relationships have been removed from the Exchange release - - /// - /// The string declaration for the -relationship parameter set. - /// - /// - /// - /// The "relationship" parameter set includes the following parameters: - /// -relationship - /// -property - /// - /// - private const string relationshipSet = "Relationship"; -#endif #region Command parameters /// - /// Gets or sets the path for the operation + /// Gets or sets the path for the operation. /// [Parameter(Position = 0, ParameterSetName = childrenSet, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true)] @@ -58,6 +40,7 @@ public string[] Path { return _paths; } + set { _paths = value; @@ -65,27 +48,27 @@ public string[] Path } /// - /// Gets or sets the literal path parameter to the command + /// Gets or sets the literal path parameter to the command. /// [Parameter(ParameterSetName = literalChildrenSet, Mandatory = true, ValueFromPipeline = false, ValueFromPipelineByPropertyName = true)] - [Alias("PSPath")] + [Alias("PSPath", "LP")] public string[] LiteralPath { get { return _paths; - } // get + } set { base.SuppressWildcardExpansion = true; _paths = value; - } // set - } // LiteralPath + } + } /// - /// Gets or sets the filter property + /// Gets or sets the filter property. /// [Parameter(Position = 1)] public override string Filter @@ -94,6 +77,7 @@ public override string Filter { return base.Filter; } + set { base.Filter = value; @@ -101,7 +85,7 @@ public override string Filter } /// - /// Gets or sets the include property + /// Gets or sets the include property. /// [Parameter] public override string[] Include @@ -109,16 +93,16 @@ public override string[] Include get { return base.Include; - } // get + } set { base.Include = value; - } // set - } // Include + } + } /// - /// Gets or sets the exclude property + /// Gets or sets the exclude property. /// [Parameter] public override string[] Exclude @@ -126,25 +110,26 @@ public override string[] Exclude get { return base.Exclude; - } // get + } set { base.Exclude = value; - } // set - } // Exclude + } + } /// - /// Gets or sets the recurse switch + /// Gets or sets the recurse switch. /// [Parameter] - [Alias("s")] + [Alias("s", "r")] public SwitchParameter Recurse { get { return _recurse; } + set { _recurse = value; @@ -164,6 +149,7 @@ public uint Depth { return _depth; } + set { _depth = value; @@ -172,9 +158,8 @@ public uint Depth } /// - /// Gets or sets the force property + /// Gets or sets the force property. /// - /// /// /// Gives the provider guidance on how vigorous it should be about performing /// the operation. If true, the provider should do everything possible to perform @@ -184,7 +169,6 @@ public uint Depth /// the destination is read-only, if force is true, the provider should copy over /// the existing read-only file. If force is false, the provider should write an error. /// - /// [Parameter] public override SwitchParameter Force { @@ -192,14 +176,15 @@ public override SwitchParameter Force { return base.Force; } + set { base.Force = value; } - } // Force + } /// - /// Gets or sets the names switch + /// Gets or sets the names switch. /// [Parameter] public SwitchParameter Name @@ -208,76 +193,29 @@ public SwitchParameter Name { return _childNames; } - set - { - _childNames = value; - } - } - -#if RELATIONSHIP_SUPPORTED - // 2004/11/24-JeffJon - Relationships have been removed from the Exchange release - /// - /// Gets and sets the value of the Relationship parameter which determines - /// which relationship the targets should be retrieved for. - /// - /// - [Parameter( - Mandatory = true, - ParameterSetName = relationshipSet, - ValueFromPipelineByPropertyName = true)] - public string[] Relationship - { - get - { - return relationships; - } set { - relationships = value; + _childNames = value; } } - private string[] relationships = new string[0]; - - /// - /// Gets or sets the property parameter which may provide guidance to the relationship - /// provider on which targets to return. - /// - /// - [Parameter(ParameterSetName = relationshipSet, ValueFromPipelineByPropertyName = true)] - public string Property - { - get - { - return property; - } - set - { - property = value; - } - } - private string property = String.Empty; -#endif /// /// A virtual method for retrieving the dynamic parameters for a cmdlet. Derived cmdlets /// that require dynamic parameters should override this method and return the /// dynamic parameter object. /// - /// /// /// The context under which the command is running. /// - /// /// /// An object representing the dynamic parameters for the cmdlet or null if there /// are none. /// - /// internal override object GetDynamicParameters(CmdletProviderContext context) { object result = null; - string path = String.Empty; + string path = string.Empty; if (_paths != null && _paths.Length > 0) { @@ -300,21 +238,16 @@ internal override object GetDynamicParameters(CmdletProviderContext context) { result = InvokeProvider.ChildItem.GetChildItemsDynamicParameters(path, Recurse, context); } - break; - -#if RELATIONSHIP_SUPPORTED - // 2004/11/24-JeffJon - Relationships have been removed from the Exchange release - case relationshipSet: - // No possible dynamic parameters for the relationship set. break; -#endif + default: result = InvokeProvider.ChildItem.GetChildItemsDynamicParameters(path, Recurse, context); break; } + return result; - } // GetDynamicParameters + } #endregion Command parameters @@ -339,13 +272,12 @@ internal override object GetDynamicParameters(CmdletProviderContext context) private uint _depth = uint.MaxValue; /// - /// The flag that specifies whether to retrieve the child names or the child items + /// The flag that specifies whether to retrieve the child names or the child items. /// private bool _childNames = false; #endregion command data - #region command code /// @@ -355,10 +287,9 @@ protected override void ProcessRecord() { CmdletProviderContext currentContext = CmdletProviderContext; - if (_paths == null || - (_paths != null && _paths.Length == 0)) + if (_paths == null || _paths.Length == 0) { - _paths = new string[] { String.Empty }; + _paths = new string[] { string.Empty }; } foreach (string path in _paths) @@ -421,63 +352,6 @@ protected override void ProcessRecord() break; -#if RELATIONSHIP_SUPPORTED - // 2004/11/24-JeffJon - Relationships have been removed from the Exchange release - - case relationshipSet: - foreach (string relationship in relationships) - { - Collection results = null; - - try - { - results = - InvokeProvider.Relationship.GetTargets( - relationship, - path, - property); - } - catch (PSArgumentException argException) - { - WriteError( - new ErrorRecord( - argException.ErrorRecord, - argException)); - continue; - } - catch (ProviderNotFoundException providerNotFound) - { - WriteError( - new ErrorRecord( - providerNotFound.ErrorRecord, - providerNotFound)); - continue; - } - - foreach (string target in results) - { - // Create an PSObject with the result. - // Attach the relationship name as a note, - // and set "System.Management.Automation.RelationshipTarget" - // as the TreatAs. - - PSObject result = PSObject.AsPSObject (target); - result.Properties.Add ( - new PSNoteProperty ( - "Relationship", - relationship)); - - Collection treatAs = new Collection (); - treatAs.Add (targetTreatAsType); - - result.TypeNames = treatAs; - - // Now write out the result - WriteObject (result); - } - } - break; -#endif default: Dbg.Diagnostics.Assert( false, @@ -485,14 +359,8 @@ protected override void ProcessRecord() break; } } - } // ProcessRecord - -#if RELATIONSHIP_SUPPORTED - // 2004/11/24-JeffJon - Relationships have been removed from the Exchange release + } - private const string targetTreatAsType = "System.Management.Automation.RelationshipTarget"; -#endif #endregion command code - } // class GetChildrenCommand -} // namespace Microsoft.PowerShell.Commands - + } +} diff --git a/src/Microsoft.PowerShell.Commands.Management/commands/management/GetClipboardCommand.cs b/src/Microsoft.PowerShell.Commands.Management/commands/management/GetClipboardCommand.cs index 176ee347211..a2b7f03ce70 100644 --- a/src/Microsoft.PowerShell.Commands.Management/commands/management/GetClipboardCommand.cs +++ b/src/Microsoft.PowerShell.Commands.Management/commands/management/GetClipboardCommand.cs @@ -1,202 +1,116 @@ -using System; -using System.Management.Automation; +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections; using System.Collections.Generic; -using System.Windows.Forms; -using System.Globalization; -using System.IO; -using System.Linq; -using System.Drawing; -using System.Media; -using System.Runtime.InteropServices; -using System.ComponentModel; -using System.Diagnostics.CodeAnalysis; -using System.Collections.Specialized; +using System.Management.Automation; +using System.Management.Automation.Language; +using Microsoft.PowerShell.Commands.Internal; namespace Microsoft.PowerShell.Commands { - /// - /// Defines the different type supported by the clipboard. - /// - public enum ClipboardFormat - { - /// Text format as default. - Text = 0, - - /// File format. - FileDropList = 1, - - /// Image format. - Image = 2, - - /// Audio format. - Audio = 3, - }; - /// /// Defines the implementation of the 'Get-Clipboard' cmdlet. /// This cmdlet get the content from system clipboard. /// - [Cmdlet(VerbsCommon.Get, "Clipboard", HelpUri = "https://go.microsoft.com/fwlink/?LinkId=526219")] + [Cmdlet(VerbsCommon.Get, "Clipboard", HelpUri = "https://go.microsoft.com/fwlink/?LinkId=2109905")] [Alias("gcb")] - [OutputType(typeof(String), typeof(FileInfo), typeof(Image), typeof(Stream))] + [OutputType(typeof(string))] public class GetClipboardCommand : PSCmdlet { /// - /// Property that sets clipboard type. This will return the required format from clipboard - /// - [Parameter] - public ClipboardFormat Format { get; set; } - - /// - /// Property that sets format type when the return type is text. + /// Property that sets raw parameter. This will allow clipboard return text or file list as one string. /// [Parameter] - [ValidateNotNullOrEmpty] - public TextDataFormat TextFormatType + public SwitchParameter Raw { - get { return _textFormat; } - set + get { - _isTextFormatTypeSet = true; - _textFormat = value; + return _raw; } - } - private TextDataFormat _textFormat = TextDataFormat.UnicodeText; - private bool _isTextFormatTypeSet = false; - /// - /// Property that sets raw parameter. This will allow clipboard return text or file list as one string. - /// - [Parameter] - public SwitchParameter Raw - { - get { return _raw; } set { - _isRawSet = true; _raw = value; } } + + /// + /// Gets or sets the delimiters to use when splitting the clipboard content. + /// + [Parameter] + [ArgumentCompleter(typeof(DelimiterCompleter))] + public string[] Delimiter { get; set; } = [Environment.NewLine]; + private bool _raw; - private bool _isRawSet = false; /// - /// This method implements the ProcessRecord method for Get-Clipboard command + /// This method implements the ProcessRecord method for Get-Clipboard command. /// protected override void BeginProcessing() { - // TextFormatType should only combine with Text. - if (Format != ClipboardFormat.Text && _isTextFormatTypeSet) - { - ThrowTerminatingError(new ErrorRecord(new InvalidOperationException( - String.Format(CultureInfo.InvariantCulture, ClipboardResources.InvalidTypeCombine)), - "FailedToGetClipboard", ErrorCategory.InvalidOperation, "Clipboard")); - } - - // Raw should only combine with Text or FileDropList. - if (Format != ClipboardFormat.Text && Format != ClipboardFormat.FileDropList && _isRawSet) - { - ThrowTerminatingError(new ErrorRecord(new InvalidOperationException( - String.Format(CultureInfo.InvariantCulture, ClipboardResources.InvalidRawCombine)), - "FailedToGetClipboard", ErrorCategory.InvalidOperation, "Clipboard")); - } - - if (Format == ClipboardFormat.Text) - { - this.WriteObject(GetClipboardContentAsText(_textFormat), true); - } - else if (Format == ClipboardFormat.Image) - { - this.WriteObject(Clipboard.GetImage()); - } - else if (Format == ClipboardFormat.FileDropList) - { - if (_raw) - { - this.WriteObject(Clipboard.GetFileDropList(), true); - } - else - { - this.WriteObject(GetClipboardContentAsFileList()); - } - } - else if (Format == ClipboardFormat.Audio) - { - this.WriteObject(Clipboard.GetAudioStream()); - } + this.WriteObject(GetClipboardContentAsText(), true); } /// /// Returns the clipboard content as text format. /// - /// - /// - private List GetClipboardContentAsText(TextDataFormat textFormat) + /// Array of strings representing content from clipboard. + private List GetClipboardContentAsText() { - if (!Clipboard.ContainsText(textFormat)) + var result = new List(); + string textContent = null; + + try + { + textContent = Clipboard.GetText(); + } + catch (PlatformNotSupportedException) { - return null; + ThrowTerminatingError(new ErrorRecord(new InvalidOperationException(ClipboardResources.UnsupportedPlatform), "FailedToGetClipboardUnsupportedPlatform", ErrorCategory.InvalidOperation, "Clipboard")); } - List result = new List(); - // TextFormat default value is Text, by default it is same as Clipboard.GetText() - string textContent = Clipboard.GetText(textFormat); if (_raw) { result.Add(textContent); } else { - string[] splitSymbol = { Environment.NewLine }; - result.AddRange(textContent.Split(splitSymbol, StringSplitOptions.None)); + result.AddRange(textContent.Split(Delimiter, StringSplitOptions.None)); } + return result; } + } + /// + /// Provides argument completion for the Delimiter parameter. + /// + public sealed class DelimiterCompleter : IArgumentCompleter + { /// - /// Returns the clipboard content as file info. + /// Provides argument completion for the Delimiter parameter. /// - /// - private List GetClipboardContentAsFileList() + /// The name of the command that is being completed. + /// The name of the parameter that is being completed. + /// The input text to filter the results by. + /// The ast of the command that triggered the completion. + /// The parameters bound to the command. + /// Completion results. + public IEnumerable CompleteArgument(string commandName, string parameterName, string wordToComplete, CommandAst commandAst, IDictionary fakeBoundParameters) { - if (!Clipboard.ContainsFileDropList()) - { - return null; - } - List result = new List(); - foreach (string filePath in Clipboard.GetFileDropList()) + wordToComplete ??= string.Empty; + var pattern = new WildcardPattern(wordToComplete + '*', WildcardOptions.IgnoreCase); + if (pattern.IsMatch("CRLF") || pattern.IsMatch("Windows")) { - FileInfo file = new FileInfo(filePath); - result.Add(WrapOutputInPSObject(file, filePath)); + yield return new CompletionResult("\"`r`n\"", "CRLF", CompletionResultType.ParameterValue, "Windows (CRLF)"); } - return result; - } - /// - /// Wraps the item in a PSObject and attaches some notes to the - /// object that deal with path information. - /// - /// - /// - /// - private PSObject WrapOutputInPSObject( - FileInfo item, - string path) - { - PSObject result = new PSObject(item); - - // Now get the parent path and child name - if (path != null) + if (pattern.IsMatch("LF") || pattern.IsMatch("Unix") || pattern.IsMatch("Linux")) { - // Get the parent path - string parentPath = Directory.GetParent(path).FullName; - result.AddOrSetProperty("PSParentPath", parentPath); - - // Get the child name - string childName = item.Name; - result.AddOrSetProperty("PSChildName", childName); + yield return new CompletionResult("\"`n\"", "LF", CompletionResultType.ParameterValue, "UNIX (LF)"); } - return result; } } } diff --git a/src/Microsoft.PowerShell.Commands.Management/commands/management/GetComputerInfoCommand.cs b/src/Microsoft.PowerShell.Commands.Management/commands/management/GetComputerInfoCommand.cs index bc1a97c924c..87f96c436ea 100644 --- a/src/Microsoft.PowerShell.Commands.Management/commands/management/GetComputerInfoCommand.cs +++ b/src/Microsoft.PowerShell.Commands.Management/commands/management/GetComputerInfoCommand.cs @@ -1,16 +1,16 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. #if !UNIX using System; using System.Collections.Generic; -using System.Runtime.InteropServices; -using System.Reflection; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; using System.Linq.Expressions; using System.Management.Automation; -using System.Diagnostics.CodeAnalysis; +using System.Reflection; +using System.Runtime.InteropServices; using Microsoft.Management.Infrastructure; using Microsoft.Win32; @@ -21,17 +21,17 @@ namespace Microsoft.PowerShell.Commands #region GetComputerInfoCommand cmdlet implementation /// - /// The Get=ComputerInfo cmdlet gathers and reports information + /// The Get-ComputerInfo cmdlet gathers and reports information /// about a computer. /// [Cmdlet(VerbsCommon.Get, "ComputerInfo", - HelpUri = "https://go.microsoft.com/fwlink/?LinkId=799466")] + HelpUri = "https://go.microsoft.com/fwlink/?LinkId=2096810")] [Alias("gin")] [OutputType(typeof(ComputerInfo), typeof(PSObject))] public class GetComputerInfoCommand : PSCmdlet { #region Inner Types - private class OSInfoGroup + private sealed class OSInfoGroup { public WmiOperatingSystem os; public HotFix[] hotFixes; @@ -41,7 +41,7 @@ private class OSInfoGroup public RegWinNtCurrentVersion regCurVer; } - private class SystemInfoGroup + private sealed class SystemInfoGroup { public WmiBaseBoard baseboard; public WmiBios bios; @@ -50,7 +50,7 @@ private class SystemInfoGroup public NetworkAdapter[] networkAdapters; } - private class HyperVInfo + private sealed class HyperVInfo { public bool? Present; public bool? VMMonitorModeExtensions; @@ -59,15 +59,15 @@ private class HyperVInfo public bool? DataExecutionPreventionAvailable; } - private class DeviceGuardInfo + private sealed class DeviceGuardInfo { public DeviceGuardSmartStatus status; public DeviceGuard deviceGuard; } - private class MiscInfoGroup + private sealed class MiscInfoGroup { - public UInt64? physicallyInstalledMemory; + public ulong? physicallyInstalledMemory; public string timeZone; public string logonServer; public FirmwareType? firmwareType; @@ -85,8 +85,7 @@ private class MiscInfoGroup #endregion Static Data and Constants #region Instance Data - private string _machineName = localMachineName; // we might need to have cmdlet work on another machine - private ProgressRecord _progress = null; + private readonly string _machineName = localMachineName; // we might need to have cmdlet work on another machine /// /// Collection of property names from the Property parameter, @@ -101,7 +100,7 @@ private class MiscInfoGroup /// The Property parameter contains the names of properties to be retrieved. /// If this parameter is given, the cmdlet returns a PSCustomObject /// containing only the requested properties. - /// Wild-card patterns may be provided + /// Wild-card patterns may be provided. /// /// /// @@ -124,7 +123,7 @@ private class MiscInfoGroup #region Cmdlet Overrides /// - /// Perform any first-stage processing + /// Perform any first-stage processing. /// protected override void BeginProcessing() { @@ -144,7 +143,7 @@ protected override void BeginProcessing() } /// - /// Performs the cmdlet's work + /// Performs the cmdlet's work. /// protected override void ProcessRecord() { @@ -199,13 +198,13 @@ protected override void ProcessRecord() systemInfo.networkAdapters = GetNetworkAdapters(session); UpdateProgress(null); // close the progress bar - } // end of using(CimSession...) + } var infoOutput = CreateFullOutputObject(systemInfo, osInfo, miscInfo); if (_namedProperties != null) { - //var output = CreateCustomOutputObject(namedProperties, systemInfo, osInfo, miscInfo); + // var output = CreateCustomOutputObject(namedProperties, systemInfo, osInfo, miscInfo); var output = CreateCustomOutputObject(infoOutput, _namedProperties); WriteObject(output); @@ -219,24 +218,17 @@ protected override void ProcessRecord() #region Private Methods /// - /// Display progress + /// Display progress. /// /// /// Text to be displayed in status bar /// private void UpdateProgress(string status) { - if (_progress != null) - { - _progress.RecordType = ProgressRecordType.Completed; - WriteProgress(_progress); - } + ProgressRecord progress = new(0, activity, status ?? ComputerResources.ProgressStatusCompleted); + progress.RecordType = status == null ? ProgressRecordType.Completed : ProgressRecordType.Processing; - if (status != null) - { - _progress = new ProgressRecord(0, activity, status); - WriteProgress(_progress); - } + WriteProgress(progress); } /// @@ -256,7 +248,7 @@ private static string GetHalVersion(CimSession session, string systemDirectory) try { var halPath = CIMHelper.EscapePath(System.IO.Path.Combine(systemDirectory, "hal.dll")); - var query = string.Format("SELECT * FROM CIM_DataFile Where Name='{0}'", halPath); + var query = string.Create(CultureInfo.InvariantCulture, $"SELECT * FROM CIM_DataFile Where Name='{halPath}'"); var instance = session.QueryFirstInstance(query); if (instance != null) @@ -295,7 +287,7 @@ private static NetworkAdapter[] GetNetworkAdapters(CimSession session) if (adapters != null && configs != null) { - var configDict = new Dictionary(); + var configDict = new Dictionary(); foreach (var config in configs) { @@ -412,11 +404,12 @@ private static bool CheckDeviceGuardLicense() // consider there to be no license. } } + return false; } /// - /// Retrieve information related to Device Guard + /// Retrieve information related to Device Guard. /// /// /// A object representing @@ -436,12 +429,14 @@ private static DeviceGuardInfo GetDeviceGuard(CimSession session) var wmiGuard = session.GetFirst(CIMHelper.DeviceGuardNamespace, CIMHelper.ClassNames.DeviceGuard); - if (wmiGuard != null) { + if (wmiGuard != null) + { var smartStatus = EnumConverter.Convert((int?)wmiGuard.VirtualizationBasedSecurityStatus ?? 0); if (smartStatus != null) { status = (DeviceGuardSmartStatus)smartStatus; } + guard = wmiGuard.AsOutputType; } } @@ -479,7 +474,7 @@ private static DeviceGuardInfo GetDeviceGuard(CimSession session) } /// - /// Retrieve information related to HyperVisor + /// Retrieve information related to HyperVisor. /// /// /// A object representing @@ -491,7 +486,7 @@ private static DeviceGuardInfo GetDeviceGuard(CimSession session) /// private static HyperVInfo GetHyperVisorInfo(CimSession session) { - HyperVInfo info = new HyperVInfo(); + HyperVInfo info = new(); bool ok = false; CimInstance instance = null; @@ -533,7 +528,7 @@ private static HyperVInfo GetHyperVisorInfo(CimSession session) } /// - /// Retrieve miscellaneous system information + /// Retrieve miscellaneous system information. /// /// /// A object representing @@ -550,7 +545,7 @@ private static MiscInfoGroup GetOtherInfo(CimSession session) // get platform role try { - //TODO: Local machine only. Check for that? + // TODO: Local machine only. Check for that? uint powerRole = Native.PowerDeterminePlatformRoleEx(Native.POWER_PLATFORM_ROLE_V2); if (powerRole >= (uint)PowerPlatformRole.MaximumEnumValue) rv.powerPlatformRole = PowerPlatformRole.Unspecified; @@ -564,14 +559,13 @@ private static MiscInfoGroup GetOtherInfo(CimSession session) } // get secure-boot info - //TODO: Local machine only? Check for that? + // TODO: Local machine only? Check for that? rv.firmwareType = GetFirmwareType(); // get amount of memory physically installed - //TODO: Local machine only. Check for that? + // TODO: Local machine only. Check for that? rv.physicallyInstalledMemory = GetPhysicallyInstalledSystemMemory(); - // get time zone // we'll use .Net's TimeZoneInfo for now. systeminfo uses Caption from Win32_TimeZone var tzi = TimeZoneInfo.Local; @@ -616,7 +610,7 @@ private static MiscInfoGroup GetOtherInfo(CimSession session) /// null if unsuccessful, otherwise FirmwareType enum specifying /// the firmware type. /// - private static Nullable GetFirmwareType() + private static FirmwareType? GetFirmwareType() { try { @@ -640,11 +634,11 @@ private static Nullable GetFirmwareType() /// /// null if unsuccessful, otherwise the amount of physically installed memory. /// - private static Nullable GetPhysicallyInstalledSystemMemory() + private static ulong? GetPhysicallyInstalledSystemMemory() { try { - UInt64 memory; + ulong memory; if (Native.GetPhysicallyInstalledSystemMemory(out memory)) return memory; } @@ -889,14 +883,13 @@ private static ComputerInfo CreateFullOutputObject(SystemInfoGroup systemInfo, O if (otherInfo.keyboards.Length > 0) { - //TODO: handle multiple keyboards? + // TODO: handle multiple keyboards? // there might be several keyboards found. For the moment // we display info for only one string layout = otherInfo.keyboards[0].Layout; - var culture = Conversion.MakeLocale(layout); - output.KeyboardLayout = culture == null ? layout : culture.Name; + output.KeyboardLayout = Conversion.GetLocaleName(layout); } if (otherInfo.hyperV != null) @@ -933,7 +926,7 @@ private static ComputerInfo CreateFullOutputObject(SystemInfoGroup systemInfo, O /// /// Create a new PSObject, containing only those properties named in the - /// namedProperties parameter + /// namedProperties parameter. /// /// /// A containing all the acquired system information @@ -988,7 +981,7 @@ private static List GetComputerInfoPropertyNames() } /// - /// Expand any wild-card patterns into known property names + /// Expand any wild-card patterns into known property names. /// /// /// List of known property names @@ -1049,9 +1042,9 @@ private static List CollectPropertyNames(string[] requestedProperties) // find a matching property name via case-insensitive string comparison Predicate pred = (s) => { - return string.Compare(s, + return string.Equals(s, name, - StringComparison.CurrentCultureIgnoreCase) == 0; + StringComparison.OrdinalIgnoreCase); }; var propertyName = availableProperties.Find(pred); @@ -1104,29 +1097,6 @@ internal static bool TryParseHex(string hexString, out uint value) } } - public static string LocaleIdToLocaleName(uint localeID) - { - // CoreCLR's System.Globalization.Culture does not appear to have a constructor - // that accepts an integer LocalID (LCID) value, so we'll PInvoke native code - // to get a locale name from an LCID value - - try - { - var sbName = new System.Text.StringBuilder(Native.LOCALE_NAME_MAX_LENGTH); - var len = Native.LCIDToLocaleName(localeID, sbName, sbName.Capacity, 0); - - if (len > 0 && sbName.Length > 0) - return sbName.ToString(); - } - catch (Exception) - { - // Probably failed to load the DLL or to file the function entry point. - // Fail silently - } - - return null; - } - /// /// Attempt to create a /// object from a locale string as retrieved from WMI. @@ -1143,38 +1113,33 @@ public static string LocaleIdToLocaleName(uint localeID) /// Failing that it attempts to retrieve the CultureInfo object /// using the locale string as passed. /// - internal static System.Globalization.CultureInfo MakeLocale(string locale) + internal static string GetLocaleName(string locale) { - System.Globalization.CultureInfo culture = null; + CultureInfo culture = null; if (locale != null) { try { - uint localeNum; - - if (TryParseHex(locale, out localeNum)) + // The "locale" must contain a hexadecimal value, with no + // base-indication prefix. For example, the string "0409" will be + // parsed into the base-10 integer value 1033, while the string "0x0409" + // will fail to parse due to the "0x" base-indication prefix. + if (uint.TryParse(locale, NumberStyles.HexNumber, CultureInfo.InvariantCulture, out uint localeNum)) { - string localeName = LocaleIdToLocaleName(localeNum); - - if (localeName != null) - culture = new System.Globalization.CultureInfo(localeName); + culture = CultureInfo.GetCultureInfo((int)localeNum); } - if (culture == null) - { - // either the TryParseHex failed, or the LocaleIdToLocaleName - // failed, so we'll try using the original string - culture = new System.Globalization.CultureInfo(locale); - } + // If TryParse failed we'll try using the original string as culture name + culture ??= CultureInfo.GetCultureInfo(locale); } - catch (Exception/* ex*/) + catch (Exception) { culture = null; } } - return culture; + return culture?.Name; } /// @@ -1218,14 +1183,14 @@ internal static class EnumConverter where T : struct, IConvertible private static readonly Func s_convert = MakeConverter(); /// - /// Convert an integer to a Nullable enum of type T + /// Convert an integer to a Nullable enum of type T. /// /// /// The integer value to be converted to the specified enum type. /// /// /// A Nullable enum object. If the value - /// is convertable to a valid enum value, the returned object's + /// is convertible to a valid enum value, the returned object's /// value will contain the converted value, otherwise the returned /// object will be null. /// @@ -1263,11 +1228,11 @@ internal static class EnumConverter where T : struct, IConvertible internal static class RegistryInfo { - public static Dictionary GetServerLevels() + public static Dictionary GetServerLevels() { const string keyPath = @"Software\Microsoft\Windows NT\CurrentVersion\Server\ServerLevels"; - var rv = new Dictionary(); + var rv = new Dictionary(); using (var key = Registry.LocalMachine.OpenSubKey(keyPath)) { @@ -1343,7 +1308,7 @@ internal abstract class WmiClassBase /// /// Get a language name from a language identifier. /// - /// + /// /// A nullable integer containing the language ID for the desired language. /// /// @@ -1351,17 +1316,26 @@ internal abstract class WmiClassBase /// the language parameter. If the language parameter is null or has a /// value that is not a valid language ID, the method returns null. /// - protected static string GetLanguageName(UInt32? language) + protected static string GetLanguageName(uint? lcid) { - if (language != null) - return Conversion.LocaleIdToLocaleName(language.Value); + if (lcid != null && lcid >= 0) + { + try + { + return CultureInfo.GetCultureInfo((int)lcid.Value).Name; + } + catch + { + } + } return null; } } + #pragma warning disable 649 // fields and properties in these class are assigned dynamically [SuppressMessage("Microsoft.Performance", "CA1812:AvoidUninstantiatedInternalClasses", Justification = "Class is instantiated directly from a CIM instance")] - internal class WmiBaseBoard + internal sealed class WmiBaseBoard { public string Caption; public string[] ConfigOptions; @@ -1394,9 +1368,9 @@ internal class WmiBaseBoard } [SuppressMessage("Microsoft.Performance", "CA1812:AvoidUninstantiatedInternalClasses", Justification = "Class is instantiated directly from a CIM instance")] - internal class WmiBios : WmiClassBase + internal sealed class WmiBios : WmiClassBase { - public UInt16[] BiosCharacteristics; + public ushort[] BiosCharacteristics; public string[] BIOSVersion; public string BuildNumber; public string Caption; @@ -1406,7 +1380,7 @@ internal class WmiBios : WmiClassBase public byte? EmbeddedControllerMajorVersion; public byte? EmbeddedControllerMinorVersion; public string IdentificationCode; - public UInt16? InstallableLanguages; + public ushort? InstallableLanguages; public DateTime? InstallDate; public string LanguageEdition; public string[] ListOfLanguages; @@ -1417,77 +1391,77 @@ internal class WmiBios : WmiClassBase public DateTime? ReleaseDate; public string SerialNumber; public string SMBIOSBIOSVersion; - public UInt16? SMBIOSMajorVersion; - public UInt16? SMBIOSMinorVersion; + public ushort? SMBIOSMajorVersion; + public ushort? SMBIOSMinorVersion; public bool? SMBIOSPresent; - public UInt16? SoftwareElementState; + public ushort? SoftwareElementState; public string Status; public byte? SystemBiosMajorVersion; public byte? SystemBiosMinorVersion; - public UInt16? TargetOperatingSystem; + public ushort? TargetOperatingSystem; public string Version; } [SuppressMessage("Microsoft.Performance", "CA1812:AvoidUninstantiatedInternalClasses", Justification = "Class is instantiated directly from a CIM instance")] - internal class WmiComputerSystem + internal sealed class WmiComputerSystem { - public UInt16? AdminPasswordStatus; + public ushort? AdminPasswordStatus; public bool? AutomaticManagedPagefile; public bool? AutomaticResetBootOption; public bool? AutomaticResetCapability; - public UInt16? BootOptionOnLimit; - public UInt16? BootOptionOnWatchDog; + public ushort? BootOptionOnLimit; + public ushort? BootOptionOnWatchDog; public bool? BootROMSupported; public string BootupState; - public UInt16[] BootStatus; + public ushort[] BootStatus; public string Caption; - public UInt16? ChassisBootupState; + public ushort? ChassisBootupState; public string ChassisSKUNumber; - public Int16? CurrentTimeZone; + public short? CurrentTimeZone; public bool? DaylightInEffect; public string Description; public string DNSHostName; public string Domain; - public UInt16? DomainRole; + public ushort? DomainRole; public bool? EnableDaylightSavingsTime; - public UInt16? FrontPanelResetStatus; + public ushort? FrontPanelResetStatus; public bool? HypervisorPresent; public bool? InfraredSupported; public string InitialLoadInfo; public DateTime? InstallDate; - public UInt16? KeyboardPasswordStatus; + public ushort? KeyboardPasswordStatus; public string LastLoadInfo; public string Manufacturer; public string Model; public string Name; public bool? NetworkServerModeEnabled; - public UInt32? NumberOfLogicalProcessors; - public UInt32? NumberOfProcessors; + public uint? NumberOfLogicalProcessors; + public uint? NumberOfProcessors; public string[] OEMStringArray; public bool? PartOfDomain; - public Int64? PauseAfterReset; - public UInt16? PCSystemType; - public UInt16? PCSystemTypeEx; - public UInt16[] PowerManagementCapabilities; + public long? PauseAfterReset; + public ushort? PCSystemType; + public ushort? PCSystemTypeEx; + public ushort[] PowerManagementCapabilities; public bool? PowerManagementSupported; - public UInt16? PowerOnPasswordStatus; - public UInt16? PowerState; - public UInt16? PowerSupplyState; + public ushort? PowerOnPasswordStatus; + public ushort? PowerState; + public ushort? PowerSupplyState; public string PrimaryOwnerContact; public string PrimaryOwnerName; - public UInt16? ResetCapability; - public Int16? ResetCount; - public Int16? ResetLimit; + public ushort? ResetCapability; + public short? ResetCount; + public short? ResetLimit; public string[] Roles; public string Status; public string[] SupportContactDescription; public string SystemFamily; public string SystemSKUNumber; public string SystemType; - public UInt16? ThermalState; - public UInt64? TotalPhysicalMemory; + public ushort? ThermalState; + public ulong? TotalPhysicalMemory; public string UserName; - public UInt16? WakeUpType; + public ushort? WakeUpType; public string Workgroup; public PowerManagementCapabilities[] GetPowerManagementCapabilities() @@ -1512,15 +1486,15 @@ public PowerManagementCapabilities[] GetPowerManagementCapabilities() } [SuppressMessage("Microsoft.Performance", "CA1812:AvoidUninstantiatedInternalClasses", Justification = "Class is instantiated directly from a CIM instance")] - internal class WmiDeviceGuard + internal sealed class WmiDeviceGuard { - public UInt32[] AvailableSecurityProperties; - public UInt32? CodeIntegrityPolicyEnforcementStatus; - public UInt32? UsermodeCodeIntegrityPolicyEnforcementStatus; - public UInt32[] RequiredSecurityProperties; - public UInt32[] SecurityServicesConfigured; - public UInt32[] SecurityServicesRunning; - public UInt32? VirtualizationBasedSecurityStatus; + public uint[] AvailableSecurityProperties; + public uint? CodeIntegrityPolicyEnforcementStatus; + public uint? UsermodeCodeIntegrityPolicyEnforcementStatus; + public uint[] RequiredSecurityProperties; + public uint[] SecurityServicesConfigured; + public uint[] SecurityServicesRunning; + public uint? VirtualizationBasedSecurityStatus; public DeviceGuard AsOutputType { @@ -1539,6 +1513,7 @@ public DeviceGuard AsOutputType if (temp != null) listHardware.Add(temp.Value); } + guard.RequiredSecurityProperties = listHardware.ToArray(); listHardware.Clear(); @@ -1549,6 +1524,7 @@ public DeviceGuard AsOutputType if (temp != null) listHardware.Add(temp.Value); } + guard.AvailableSecurityProperties = listHardware.ToArray(); var listSoftware = new List(); @@ -1559,6 +1535,7 @@ public DeviceGuard AsOutputType if (temp != null) listSoftware.Add(temp.Value); } + guard.SecurityServicesConfigured = listSoftware.ToArray(); listSoftware.Clear(); @@ -1569,6 +1546,7 @@ public DeviceGuard AsOutputType if (temp != null) listSoftware.Add(temp.Value); } + guard.SecurityServicesRunning = listSoftware.ToArray(); } @@ -1583,11 +1561,11 @@ public DeviceGuard AsOutputType } [SuppressMessage("Microsoft.Performance", "CA1812:AvoidUninstantiatedInternalClasses", Justification = "Class is instantiated directly from a CIM instance")] - internal class WmiKeyboard + internal sealed class WmiKeyboard { - public UInt16? Availability; + public ushort? Availability; public string Caption; - public UInt32? ConfigManagerErrorCode; + public uint? ConfigManagerErrorCode; public bool? ConfigManagerUserConfig; public string Description; public string DeviceID; @@ -1595,161 +1573,161 @@ internal class WmiKeyboard public string ErrorDescription; public DateTime? InstallDate; public bool? IsLocked; - public UInt32? LastErrorCode; + public uint? LastErrorCode; public string Layout; public string Name; - public UInt16? NumberOfFunctionKeys; - public UInt16? Password; + public ushort? NumberOfFunctionKeys; + public ushort? Password; public string PNPDeviceID; - public UInt16[] PowerManagementCapabilities; + public ushort[] PowerManagementCapabilities; public bool? PowerManagementSupported; public string Status; - public UInt16? StatusInfo; + public ushort? StatusInfo; public string SystemCreationClassName; public string SystemName; } [SuppressMessage("Microsoft.Performance", "CA1812:AvoidUninstantiatedInternalClasses", Justification = "Class is instantiated directly from a CIM instance")] - internal class WMiLogicalMemory + internal sealed class WMiLogicalMemory { - //TODO: fill this in!!! - public UInt32? TotalPhysicalMemory; + // TODO: fill this in!!! + public uint? TotalPhysicalMemory; } [SuppressMessage("Microsoft.Performance", "CA1812:AvoidUninstantiatedInternalClasses", Justification = "Class is instantiated directly from a CIM instance")] - internal class WmiMsftNetAdapter + internal sealed class WmiMsftNetAdapter { public string Caption; public string Description; public DateTime? InstallDate; public string Name; public string Status; - public UInt16? Availability; - public UInt32? ConfigManagerErrorCode; + public ushort? Availability; + public uint? ConfigManagerErrorCode; public bool? ConfigManagerUserConfig; public string DeviceID; public bool? ErrorCleared; public string ErrorDescription; - public UInt32? LastErrorCode; + public uint? LastErrorCode; public string PNPDeviceID; - public UInt16[] PowerManagementCapabilities; + public ushort[] PowerManagementCapabilities; public bool? PowerManagementSupported; - public UInt16? StatusInfo; + public ushort? StatusInfo; public string SystemCreationClassName; public string SystemName; - public UInt64? Speed; - public UInt64? MaxSpeed; - public UInt64? RequestedSpeed; - public UInt16? UsageRestriction; - public UInt16? PortType; + public ulong? Speed; + public ulong? MaxSpeed; + public ulong? RequestedSpeed; + public ushort? UsageRestriction; + public ushort? PortType; public string OtherPortType; public string OtherNetworkPortType; - public UInt16? PortNumber; - public UInt16? LinkTechnology; + public ushort? PortNumber; + public ushort? LinkTechnology; public string OtherLinkTechnology; public string PermanentAddress; public string[] NetworkAddresses; public bool? FullDuplex; public bool? AutoSense; - public UInt64? SupportedMaximumTransmissionUnit; - public UInt64? ActiveMaximumTransmissionUnit; + public ulong? SupportedMaximumTransmissionUnit; + public ulong? ActiveMaximumTransmissionUnit; public string InterfaceDescription; public string InterfaceName; - public UInt64? NetLuid; + public ulong? NetLuid; public string InterfaceGuid; - public UInt32? InterfaceIndex; + public uint? InterfaceIndex; public string DeviceName; - public UInt32? NetLuidIndex; + public uint? NetLuidIndex; public bool? Virtual; public bool? Hidden; public bool? NotUserRemovable; public bool? IMFilter; - public UInt32? InterfaceType; + public uint? InterfaceType; public bool? HardwareInterface; public bool? WdmInterface; public bool? EndPointInterface; public bool? iSCSIInterface; - public UInt32? State; - public UInt32? NdisMedium; - public UInt32? NdisPhysicalMedium; - public UInt32? InterfaceOperationalStatus; + public uint? State; + public uint? NdisMedium; + public uint? NdisPhysicalMedium; + public uint? InterfaceOperationalStatus; public bool? OperationalStatusDownDefaultPortNotAuthenticated; public bool? OperationalStatusDownMediaDisconnected; public bool? OperationalStatusDownInterfacePaused; public bool? OperationalStatusDownLowPowerState; - public UInt32? InterfaceAdminStatus; - public UInt32? MediaConnectState; - public UInt32? MtuSize; - public UInt16? VlanID; - public UInt64? TransmitLinkSpeed; - public UInt64? ReceiveLinkSpeed; + public uint? InterfaceAdminStatus; + public uint? MediaConnectState; + public uint? MtuSize; + public ushort? VlanID; + public ulong? TransmitLinkSpeed; + public ulong? ReceiveLinkSpeed; public bool? PromiscuousMode; public bool? DeviceWakeUpEnable; public bool? ConnectorPresent; - public UInt32? MediaDuplexState; + public uint? MediaDuplexState; public string DriverDate; - public UInt64? DriverDateData; + public ulong? DriverDateData; public string DriverVersionString; public string DriverName; public string DriverDescription; - public UInt16? MajorDriverVersion; - public UInt16? MinorDriverVersion; + public ushort? MajorDriverVersion; + public ushort? MinorDriverVersion; public byte? DriverMajorNdisVersion; public byte? DriverMinorNdisVersion; public string PnPDeviceID; public string DriverProvider; public string ComponentID; - public UInt32[] LowerLayerInterfaceIndices; - public UInt32[] HigherLayerInterfaceIndices; + public uint[] LowerLayerInterfaceIndices; + public uint[] HigherLayerInterfaceIndices; public bool? AdminLocked; } [SuppressMessage("Microsoft.Performance", "CA1812:AvoidUninstantiatedInternalClasses", Justification = "Class is instantiated directly from a CIM instance")] - internal class WmiNetworkAdapter + internal sealed class WmiNetworkAdapter { public string AdapterType; - public UInt16? AdapterTypeID; + public ushort? AdapterTypeID; public bool? AutoSense; - public UInt16? Availability; + public ushort? Availability; public string Caption; - public UInt32? ConfigManagerErrorCode; + public uint? ConfigManagerErrorCode; public bool? ConfigManagerUserConfig; public string Description; public string DeviceID; public bool? ErrorCleared; public string ErrorDescription; public string GUID; - public UInt32? Index; + public uint? Index; public DateTime? InstallDate; public bool? Installed; - public UInt32? InterfaceIndex; - public UInt32? LastErrorCode; + public uint? InterfaceIndex; + public uint? LastErrorCode; public string MACAddress; public string Manufacturer; - public UInt32? MaxNumberControlled; - public UInt64? MaxSpeed; + public uint? MaxNumberControlled; + public ulong? MaxSpeed; public string Name; public string NetConnectionID; - public UInt16? NetConnectionStatus; + public ushort? NetConnectionStatus; public bool? NetEnabled; public string[] NetworkAddresses; public string PermanentAddress; public bool? PhysicalAdapter; public string PNPDeviceID; - public UInt16[] PowerManagementCapabilities; + public ushort[] PowerManagementCapabilities; public bool? PowerManagementSupported; public string ProductName; public string ServiceName; - public UInt64? Speed; + public ulong? Speed; public string Status; - public UInt16? StatusInfo; + public ushort? StatusInfo; public string SystemCreationClassName; public string SystemName; public DateTime? TimeOfLastReset; } [SuppressMessage("Microsoft.Performance", "CA1812:AvoidUninstantiatedInternalClasses", Justification = "Class is instantiated directly from a CIM instance")] - internal class WmiNetworkAdapterConfiguration + internal sealed class WmiNetworkAdapterConfiguration { public bool? ArpAlwaysSourceRoute; public bool? ArpUseEtherSNAP; @@ -1770,14 +1748,14 @@ internal class WmiNetworkAdapterConfiguration public string DNSHostName; public string[] DNSServerSearchOrder; public bool? DomainDNSRegistrationEnabled; - public UInt32? ForwardBufferMemory; + public uint? ForwardBufferMemory; public bool? FullDNSRegistrationEnabled; - public UInt16[] GatewayCostMetric; + public ushort[] GatewayCostMetric; public byte? IGMPLevel; - public UInt32? Index; - public UInt32? InterfaceIndex; + public uint? Index; + public uint? InterfaceIndex; public string[] IPAddress; - public UInt32? IPConnectionMetric; + public uint? IPConnectionMetric; public bool? IPEnabled; public bool? IPFilterSecurityEnabled; public bool? IPPortSecurityEnabled; @@ -1788,25 +1766,25 @@ internal class WmiNetworkAdapterConfiguration public bool? IPUseZeroBroadcast; public string IPXAddress; public bool? IPXEnabled; - public UInt32[] IPXFrameType; - public UInt32? IPXMediaType; + public uint[] IPXFrameType; + public uint? IPXMediaType; public string[] IPXNetworkNumber; public string IPXVirtualNetNumber; - public UInt32? KeepAliveInterval; - public UInt32? KeepAliveTime; + public uint? KeepAliveInterval; + public uint? KeepAliveTime; public string MACAddress; - public UInt32? MTU; - public UInt32? NumForwardPackets; + public uint? MTU; + public uint? NumForwardPackets; public bool? PMTUBHDetectEnabled; public bool? PMTUDiscoveryEnabled; public string ServiceName; public string SettingID; - public UInt32? TcpipNetbiosOptions; - public UInt32? TcpMaxConnectRetransmissions; - public UInt32? TcpMaxDataRetransmissions; - public UInt32? TcpNumConnections; + public uint? TcpipNetbiosOptions; + public uint? TcpMaxConnectRetransmissions; + public uint? TcpMaxDataRetransmissions; + public uint? TcpNumConnections; public bool? TcpUseRFC1122UrgentPointer; - public UInt16? TcpWindowSize; + public ushort? TcpWindowSize; public bool? WINSEnableLMHostsLookup; public string WINSHostLookupFile; public string WINSPrimaryServer; @@ -1815,7 +1793,7 @@ internal class WmiNetworkAdapterConfiguration } [SuppressMessage("Microsoft.Performance", "CA1812:AvoidUninstantiatedInternalClasses", Justification = "Class is instantiated directly from a CIM instance")] - internal class WmiOperatingSystem : WmiClassBase + internal sealed class WmiOperatingSystem : WmiClassBase { #region Fields public string BootDevice; @@ -1826,7 +1804,7 @@ internal class WmiOperatingSystem : WmiClassBase public string CountryCode; public string CSDVersion; public string CSName; - public Int16? CurrentTimeZone; + public short? CurrentTimeZone; public bool? DataExecutionPrevention_Available; public bool? DataExecutionPrevention_32BitApplications; public bool? DataExecutionPrevention_Drivers; @@ -1834,47 +1812,47 @@ internal class WmiOperatingSystem : WmiClassBase public bool? Debug; public string Description; public bool? Distributed; - public UInt32? EncryptionLevel; + public uint? EncryptionLevel; public byte? ForegroundApplicationBoost; - public UInt64? FreePhysicalMemory; - public UInt64? FreeSpaceInPagingFiles; - public UInt64? FreeVirtualMemory; + public ulong? FreePhysicalMemory; + public ulong? FreeSpaceInPagingFiles; + public ulong? FreeVirtualMemory; public DateTime? InstallDate; public DateTime? LastBootUpTime; public DateTime? LocalDateTime; public string Locale; public string Manufacturer; - public UInt32? MaxNumberOfProcesses; - public UInt64? MaxProcessMemorySize; + public uint? MaxNumberOfProcesses; + public ulong? MaxProcessMemorySize; public string[] MUILanguages; public string Name; - public UInt32? NumberOfLicensedUsers; - public UInt32? NumberOfProcesses; - public UInt32? NumberOfUsers; - public UInt32? OperatingSystemSKU; + public uint? NumberOfLicensedUsers; + public uint? NumberOfProcesses; + public uint? NumberOfUsers; + public uint? OperatingSystemSKU; public string Organization; public string OSArchitecture; - public UInt32? OSLanguage; - public UInt32? OSProductSuite; - public UInt16? OSType; + public uint? OSLanguage; + public uint? OSProductSuite; + public ushort? OSType; public string OtherTypeDescription; public bool? PAEEnabled; public bool? PortableOperatingSystem; public bool? Primary; - public UInt32? ProductType; + public uint? ProductType; public string RegisteredUser; public string SerialNumber; - public UInt16? ServicePackMajorVersion; - public UInt16? ServicePackMinorVersion; - public UInt64? SizeStoredInPagingFiles; + public ushort? ServicePackMajorVersion; + public ushort? ServicePackMinorVersion; + public ulong? SizeStoredInPagingFiles; public string Status; - public UInt32? SuiteMask; + public uint? SuiteMask; public string SystemDevice; public string SystemDirectory; public string SystemDrive; - public UInt64? TotalSwapSpaceSize; - public UInt64? TotalVirtualMemorySize; - public UInt64? TotalVisibleMemorySize; + public ulong? TotalSwapSpaceSize; + public ulong? TotalVirtualMemorySize; + public ulong? TotalVisibleMemorySize; public string Version; public string WindowsDirectory; #endregion Fields @@ -1899,17 +1877,12 @@ public OSProductSuite[] Suites #region Public Methods public string GetLocale() { - System.Globalization.CultureInfo culture = null; - - if (Locale != null) - culture = Conversion.MakeLocale(Locale); - - return culture == null ? null : culture.Name; + return Conversion.GetLocaleName(Locale); } #endregion Public Methods #region Private Methods - private OSProductSuite[] MakeProductSuites(UInt32? suiteMask) + private static OSProductSuite[] MakeProductSuites(uint? suiteMask) { if (suiteMask == null) return null; @@ -1917,8 +1890,8 @@ private OSProductSuite[] MakeProductSuites(UInt32? suiteMask) var mask = suiteMask.Value; var list = new List(); - foreach (OSProductSuite suite in Enum.GetValues(typeof(OSProductSuite))) - if ((mask & (UInt32)suite) != 0) + foreach (OSProductSuite suite in Enum.GetValues()) + if ((mask & (uint)suite) != 0) list.Add(suite); return list.ToArray(); @@ -1927,84 +1900,84 @@ private OSProductSuite[] MakeProductSuites(UInt32? suiteMask) } [SuppressMessage("Microsoft.Performance", "CA1812:AvoidUninstantiatedInternalClasses", Justification = "Class is instantiated directly from a CIM instance")] - internal class WmiPageFileUsage + internal sealed class WmiPageFileUsage { - public UInt32? AllocatedBaseSize; + public uint? AllocatedBaseSize; public string Caption; - public UInt32? CurrentUsage; + public uint? CurrentUsage; public string Description; public DateTime? InstallDate; public string Name; - public UInt32? PeakUsage; + public uint? PeakUsage; public string Status; public bool? TempPageFile; } [SuppressMessage("Microsoft.Performance", "CA1812:AvoidUninstantiatedInternalClasses", Justification = "Class is instantiated directly from a CIM instance")] - internal class WmiProcessor + internal sealed class WmiProcessor { - public UInt16? AddressWidth; - public UInt16? Architecture; + public ushort? AddressWidth; + public ushort? Architecture; public string AssetTag; - public UInt16? Availability; + public ushort? Availability; public string Caption; - public UInt32? Characteristics; - public UInt32? ConfigManagerErrorCode; + public uint? Characteristics; + public uint? ConfigManagerErrorCode; public bool? ConfigManagerUserConfig; - public UInt16? CpuStatus; - public UInt32? CurrentClockSpeed; - public UInt16? CurrentVoltage; - public UInt16? DataWidth; + public ushort? CpuStatus; + public uint? CurrentClockSpeed; + public ushort? CurrentVoltage; + public ushort? DataWidth; public string Description; public string DeviceID; public bool? ErrorCleared; public string ErrorDescription; - public UInt32? ExtClock; - public UInt16? Family; + public uint? ExtClock; + public ushort? Family; public DateTime? InstallDate; - public UInt32? L2CacheSize; - public UInt32? L2CacheSpeed; - public UInt32? L3CacheSize; - public UInt32? L3CacheSpeed; - public UInt32? LastErrorCode; - public UInt16? Level; - public UInt16? LoadPercentage; + public uint? L2CacheSize; + public uint? L2CacheSpeed; + public uint? L3CacheSize; + public uint? L3CacheSpeed; + public uint? LastErrorCode; + public ushort? Level; + public ushort? LoadPercentage; public string Manufacturer; - public UInt32? MaxClockSpeed; + public uint? MaxClockSpeed; public string Name; - public UInt32? NumberOfCores; - public UInt32? NumberOfEnabledCore; - public UInt32? NumberOfLogicalProcessors; + public uint? NumberOfCores; + public uint? NumberOfEnabledCore; + public uint? NumberOfLogicalProcessors; public string OtherFamilyDescription; public string PartNumber; public string PNPDeviceID; - public UInt16[] PowerManagementCapabilities; + public ushort[] PowerManagementCapabilities; public bool? PowerManagementSupported; public string ProcessorId; - public UInt16? ProcessorType; - public UInt16? Revision; + public ushort? ProcessorType; + public ushort? Revision; public string Role; public bool? SecondLevelAddressTranslationExtensions; public string SerialNumber; public string SocketDesignation; public string Status; - public UInt16? StatusInfo; + public ushort? StatusInfo; public string Stepping; public string SystemName; - public UInt32? ThreadCount; + public uint? ThreadCount; public string UniqueId; - public UInt16? UpgradeMethod; + public ushort? UpgradeMethod; public string Version; public bool? VirtualizationFirmwareEnabled; public bool? VMMonitorModeExtensions; - public UInt32? VoltageCaps; + public uint? VoltageCaps; } #pragma warning restore 649 #endregion Intermediate WMI classes #region Other Intermediate classes - internal class RegWinNtCurrentVersion + internal sealed class RegWinNtCurrentVersion { public string BuildLabEx; public string CurrentVersion; @@ -2024,43 +1997,43 @@ internal class RegWinNtCurrentVersion #region Output components #region Classes comprising the output object /// - /// Provides information about Device Guard + /// Provides information about Device Guard. /// public class DeviceGuard { /// - /// Array of required security properties + /// Array of required security properties. /// [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] public DeviceGuardHardwareSecure[] RequiredSecurityProperties { get; internal set; } /// - /// Array of available security properties + /// Array of available security properties. /// [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] public DeviceGuardHardwareSecure[] AvailableSecurityProperties { get; internal set; } /// - /// Indicates which security services have been configured + /// Indicates which security services have been configured. /// [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] public DeviceGuardSoftwareSecure[] SecurityServicesConfigured { get; internal set; } /// - /// Indicates which security services are running + /// Indicates which security services are running. /// [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] public DeviceGuardSoftwareSecure[] SecurityServicesRunning { get; internal set; } /// - /// Indicates the status of the Device Guard Code Integrity policy + /// Indicates the status of the Device Guard Code Integrity policy. /// public DeviceGuardConfigCodeIntegrityStatus? CodeIntegrityPolicyEnforcementStatus { get; internal set; } /// - /// Indicates the status of the Device Guard user mode Code Integrity policy + /// Indicates the status of the Device Guard user mode Code Integrity policy. /// public DeviceGuardConfigCodeIntegrityStatus? UserModeCodeIntegrityPolicyEnforcementStatus { get; internal set; } } /// - /// Describes a Quick-Fix Engineering update + /// Describes a Quick-Fix Engineering update. /// public class HotFix { @@ -2075,7 +2048,7 @@ public string HotFixID } /// - /// Description of the update + /// Description of the update. /// public string Description { @@ -2085,7 +2058,7 @@ public string Description } /// - /// String containing the date that the update was installed + /// String containing the date that the update was installed. /// public string InstalledOn { @@ -2095,7 +2068,7 @@ public string InstalledOn } /// - /// Additional comments that relate to the update + /// Additional comments that relate to the update. /// public string FixComments { @@ -2106,30 +2079,30 @@ public string FixComments } /// - /// Provides information about a network adapter + /// Provides information about a network adapter. /// public class NetworkAdapter { /// - /// Description of the network adapter + /// Description of the network adapter. /// public string Description { get; internal set; } /// /// Name of the network connection as it appears in the Network - /// Connections Control Panel program + /// Connections Control Panel program. /// public string ConnectionID { get; internal set; } /// /// Indicates whether the DHCP server automatically assigns an IP address - /// to the computer system when establishing a network connection + /// to the computer system when establishing a network connection. /// public bool? DHCPEnabled { get; internal set; } /// - /// IP Address of the DHCP server + /// IP Address of the DHCP server. /// public string DHCPServer { get; internal set; } /// - /// State of the network adapter connection to the network + /// State of the network adapter connection to the network. /// public NetConnectionStatus ConnectionStatus { get; internal set; } /// @@ -2140,49 +2113,49 @@ public class NetworkAdapter } /// - /// Describes a processor on the computer + /// Describes a processor on the computer. /// public class Processor { /// - /// Name of the processor + /// Name of the processor. /// public string Name { get; internal set; } /// - /// Name of the processor manufacturer + /// Name of the processor manufacturer. /// public string Manufacturer { get; internal set; } /// - /// Description of the processor + /// Description of the processor. /// public string Description { get; internal set; } /// - /// Processor architecture used by the platform + /// Processor architecture used by the platform. /// public CpuArchitecture? Architecture { get; internal set; } /// - /// Address width of the processor + /// Address width of the processor. /// - public UInt16? AddressWidth { get; internal set; } + public ushort? AddressWidth { get; internal set; } /// - /// Data width of the processor + /// Data width of the processor. /// - public UInt16? DataWidth { get; internal set; } + public ushort? DataWidth { get; internal set; } /// - /// Maximum speed of the processor, in MHz + /// Maximum speed of the processor, in MHz. /// - public UInt32? MaxClockSpeed { get; internal set; } + public uint? MaxClockSpeed { get; internal set; } /// - /// Current speed of the processor, in MHz + /// Current speed of the processor, in MHz. /// - public UInt32? CurrentClockSpeed { get; internal set; } + public uint? CurrentClockSpeed { get; internal set; } /// /// Number of cores for the current instance of the processor. /// /// /// A core is a physical processor on the integrated circuit /// - public UInt32? NumberOfCores { get; internal set; } + public uint? NumberOfCores { get; internal set; } /// /// Number of logical processors for the current instance of the processor. /// @@ -2190,7 +2163,7 @@ public class Processor /// For processors capable of hyperthreading, this value includes only the /// processors which have hyperthreading enabled /// - public UInt32? NumberOfLogicalProcessors { get; internal set; } + public uint? NumberOfLogicalProcessors { get; internal set; } /// /// Processor information that describes the processor features. /// @@ -2206,35 +2179,35 @@ public class Processor /// public string ProcessorID { get; internal set; } /// - /// Type of chip socket used on the circuit + /// Type of chip socket used on the circuit. /// public string SocketDesignation { get; internal set; } /// - /// Primary function of the processor + /// Primary function of the processor. /// public ProcessorType? ProcessorType { get; internal set; } /// - /// Role of the processor + /// Role of the processor. /// public string Role { get; internal set; } /// - /// Current status of the processor + /// Current status of the processor. /// public string Status { get; internal set; } /// /// Current status of the processor. /// Status changes indicate processor usage, but not the physical - /// condition of the processor + /// condition of the processor. /// public CpuStatus? CpuStatus { get; internal set; } /// - /// Availability and status of the processor + /// Availability and status of the processor. /// public CpuAvailability? Availability { get; internal set; } } /// - /// The ComputerInfo class is output to the PowerShell pipeline. + /// The ComputerInfo class is output to the PowerShell pipeline. /// public class ComputerInfo { @@ -2303,21 +2276,21 @@ public class ComputerInfo #region BIOS /// /// Array of BIOS characteristics supported by the system as defined by - /// the System Management BIOS Reference Specification + /// the System Management BIOS Reference Specification. /// [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] - public UInt16[] BiosCharacteristics { get; internal set; } + public ushort[] BiosCharacteristics { get; internal set; } /// /// Array of the complete system BIOS information. In many computers /// there can be several version strings that are stored in the registry - /// and represent the system BIOS information + /// and represent the system BIOS information. /// [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] public string[] BiosBIOSVersion { get; internal set; } /// - /// Internal identifier for this compilation of the BIOS firmware + /// Internal identifier for this compilation of the BIOS firmware. /// public string BiosBuildNumber { get; internal set; } @@ -2327,29 +2300,29 @@ public class ComputerInfo public string BiosCaption { get; internal set; } /// - /// Code set used by the BIOS + /// Code set used by the BIOS. /// public string BiosCodeSet { get; internal set; } /// - /// Name of the current BIOS language + /// Name of the current BIOS language. /// public string BiosCurrentLanguage { get; internal set; } /// - /// Description of the BIOS + /// Description of the BIOS. /// public string BiosDescription { get; internal set; } /// - /// Major version of the embedded controller firmware + /// Major version of the embedded controller firmware. /// - public Int16? BiosEmbeddedControllerMajorVersion { get; internal set; } + public short? BiosEmbeddedControllerMajorVersion { get; internal set; } /// - /// Minor version of the embedded controller firmware + /// Minor version of the embedded controller firmware. /// - public Int16? BiosEmbeddedControllerMinorVersion { get; internal set; } + public short? BiosEmbeddedControllerMinorVersion { get; internal set; } /// /// Firmware type of the local computer. @@ -2361,43 +2334,43 @@ public class ComputerInfo /// /// Manufacturer's identifier for this software element. - /// Often this will be a stock keeping unit (SKU) or a part number + /// Often this will be a stock keeping unit (SKU) or a part number. /// public string BiosIdentificationCode { get; internal set; } /// /// Number of languages available for installation on this system. - /// Language may determine properties such as the need for Unicode and bidirectional text + /// Language may determine properties such as the need for Unicode and bidirectional text. /// - public UInt16? BiosInstallableLanguages { get; internal set; } + public ushort? BiosInstallableLanguages { get; internal set; } /// /// Date and time the object was installed. /// - //TODO: do we want this? On my system this is null + // TODO: do we want this? On my system this is null public DateTime? BiosInstallDate { get; internal set; } /// /// Language edition of the BIOS firmware. /// The language codes defined in ISO 639 should be used. /// Where the software element represents a multilingual or international - /// version of a product, the string "multilingual" should be used + /// version of a product, the string "multilingual" should be used. /// public string BiosLanguageEdition { get; internal set; } /// - /// Array of names of available BIOS-installable languages + /// Array of names of available BIOS-installable languages. /// [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] public string[] BiosListOfLanguages { get; internal set; } /// - /// Manufacturer of the BIOS + /// Manufacturer of the BIOS. /// public string BiosManufacturer { get; internal set; } /// - /// Name used to identify the BIOS + /// Name used to identify the BIOS. /// public string BiosName { get; internal set; } @@ -2406,139 +2379,141 @@ public class ComputerInfo /// the BiosTargetOperatingSystem property has a value of 1 (Other). /// When TargetOperatingSystem has a value of 1, BiosOtherTargetOS must /// have a nonnull value. For all other values of BiosTargetOperatingSystem, - /// BiosOtherTargetOS is NULL + /// BiosOtherTargetOS is NULL. /// public string BiosOtherTargetOS { get; internal set; } /// - /// If true, this is the primary BIOS of the computer system + /// If true, this is the primary BIOS of the computer system. /// public bool? BiosPrimaryBIOS { get; internal set; } /// - /// Release date of the Windows BIOS + /// Release date of the Windows BIOS. /// public DateTime? BiosReleaseDate { get; internal set; } /// - /// Assigned serial number of the BIOS + /// Assigned serial number of the BIOS. /// public string BiosSerialNumber { get; internal set; } /// - /// BIOS version as reported by SMBIOS + /// BIOS version as reported by SMBIOS. /// public string BiosSMBIOSBIOSVersion { get; internal set; } /// - /// SMBIOS major version number. This property is null if SMBIOS is not found + /// SMBIOS major version number. This property is null if SMBIOS is not found. /// - public UInt16? BiosSMBIOSMajorVersion { get; internal set; } + public ushort? BiosSMBIOSMajorVersion { get; internal set; } /// - /// SMBIOS minor version number. This property is null if SMBIOS is not found + /// SMBIOS minor version number. This property is null if SMBIOS is not found. /// - public UInt16? BiosSMBIOSMinorVersion { get; internal set; } + public ushort? BiosSMBIOSMinorVersion { get; internal set; } /// - /// If true, the SMBIOS is available on this computer system + /// If true, the SMBIOS is available on this computer system. /// public bool? BiosSMBIOSPresent { get; internal set; } /// - /// State of a BIOS software element + /// State of a BIOS software element. /// public SoftwareElementState? BiosSoftwareElementState { get; internal set; } /// - /// Status of the BIOS + /// Status of the BIOS. /// public string BiosStatus { get; internal set; } /// - /// Major elease of the System BIOS + /// Major elease of the System BIOS. /// - public UInt16? BiosSystemBiosMajorVersion { get; internal set; } + public ushort? BiosSystemBiosMajorVersion { get; internal set; } /// - /// Minor release of the System BIOS + /// Minor release of the System BIOS. /// - public UInt16? BiosSystemBiosMinorVersion { get; internal set; } + public ushort? BiosSystemBiosMinorVersion { get; internal set; } /// - /// Target operating system + /// Target operating system. /// - public UInt16? BiosTargetOperatingSystem { get; internal set; } + public ushort? BiosTargetOperatingSystem { get; internal set; } /// /// Version of the BIOS. - /// This string is created by the BIOS manufacturer + /// This string is created by the BIOS manufacturer. /// public string BiosVersion { get; internal set; } #endregion BIOS #region Computer System /// - /// System hardware security settings for administrator password status + /// System hardware security settings for administrator password status. /// - //public AdminPasswordStatus? CsAdminPasswordStatus { get; internal set; } + // public AdminPasswordStatus? CsAdminPasswordStatus { get; internal set; } + public HardwareSecurity? CsAdminPasswordStatus { get; internal set; } /// - /// If true, the system manages the page file + /// If true, the system manages the page file. /// public bool? CsAutomaticManagedPagefile { get; internal set; } /// - /// If True, the automatic reset boot option is enabled + /// If True, the automatic reset boot option is enabled. /// public bool? CsAutomaticResetBootOption { get; internal set; } /// - /// If True, the automatic reset is enabled + /// If True, the automatic reset is enabled. /// public bool? CsAutomaticResetCapability { get; internal set; } /// /// Boot option limit is ON. Identifies the system action when the - /// CsResetLimit value is reached + /// CsResetLimit value is reached. /// public BootOptionAction? CsBootOptionOnLimit { get; internal set; } /// - /// Type of reboot action after the time on the watchdog timer is elapsed + /// Type of reboot action after the time on the watchdog timer is elapsed. /// public BootOptionAction? CsBootOptionOnWatchDog { get; internal set; } /// - /// If true, indicates whether a boot ROM is supported + /// If true, indicates whether a boot ROM is supported. /// public bool? CsBootROMSupported { get; internal set; } /// - /// Status and Additional Data fields that identify the boot status + /// Status and Additional Data fields that identify the boot status. /// [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] - public UInt16[] CsBootStatus { get; internal set; } + public ushort[] CsBootStatus { get; internal set; } /// - /// System is started. Fail-safe boot bypasses the user startup files—also called SafeBoot + /// System is started. Fail-safe boot bypasses the user startup files—also called SafeBoot. /// public string CsBootupState { get; internal set; } /// - /// The name of this computer + /// The name of this computer. /// - public string CsCaption { get; internal set; } //TODO: remove this? Same as CsName??? + public string CsCaption { get; internal set; } // TODO: remove this? Same as CsName??? /// - /// Boot up state of the chassis + /// Boot up state of the chassis. /// - //public ChassisBootupState? CsChassisBootupState { get; internal set; } + // public ChassisBootupState? CsChassisBootupState { get; internal set; } + public SystemElementState? CsChassisBootupState { get; internal set; } /// - /// The chassis or enclosure SKU number as a string + /// The chassis or enclosure SKU number as a string. /// public string CsChassisSKUNumber { get; internal set; } @@ -2546,20 +2521,20 @@ public class ComputerInfo /// Amount of time the unitary computer system is offset from Coordinated /// Universal Time (UTC). /// - public Int16? CsCurrentTimeZone { get; internal set; } + public short? CsCurrentTimeZone { get; internal set; } /// - /// If True, the daylight savings mode is ON + /// If True, the daylight savings mode is ON. /// public bool? CsDaylightInEffect { get; internal set; } /// - /// Description of the computer system + /// Description of the computer system. /// public string CsDescription { get; internal set; } /// - /// Name of local computer according to the domain name server + /// Name of local computer according to the domain name server. /// public string CsDNSHostName { get; internal set; } @@ -2574,7 +2549,7 @@ public class ComputerInfo /// /// Role of a computer in an assigned domain workgroup. A domain workgroup /// is a collection of computers on the same network. For example, - /// a DomainRole property may show that a computer is a member workstation + /// a DomainRole property may show that a computer is a member workstation. /// public DomainRole? CsDomainRole { get; internal set; } @@ -2588,57 +2563,59 @@ public class ComputerInfo public bool? CsEnableDaylightSavingsTime { get; internal set; } /// - /// Hardware security setting for the reset button on a computer + /// Hardware security setting for the reset button on a computer. /// - //public FrontPanelResetStatus? CsFrontPanelResetStatus { get; internal set; } + // public FrontPanelResetStatus? CsFrontPanelResetStatus { get; internal set; } + public HardwareSecurity? CsFrontPanelResetStatus { get; internal set; } /// - /// If True, a hypervisor is present + /// If True, a hypervisor is present. /// public bool? CsHypervisorPresent { get; internal set; } /// - /// If True, an infrared port exists on a computer system + /// If True, an infrared port exists on a computer system. /// public bool? CsInfraredSupported { get; internal set; } /// - /// Data required to find the initial load device or boot service to request that the operating system start up + /// Data required to find the initial load device or boot service to request that the operating system start up. /// public string CsInitialLoadInfo { get; internal set; } /// - /// Object is installed. An object does not need a value to indicate that it is installed + /// Object is installed. An object does not need a value to indicate that it is installed. /// public DateTime? CsInstallDate { get; internal set; } /// - /// System hardware security setting for Keyboard Password Status + /// System hardware security setting for Keyboard Password Status. /// - //public KeyboardPasswordStatus? CsKeyboardPasswordStatus { get; internal set; } + // public KeyboardPasswordStatus? CsKeyboardPasswordStatus { get; internal set; } + public HardwareSecurity? CsKeyboardPasswordStatus { get; internal set; } /// /// Array entry of the CsInitialLoadInfo property that contains the data - /// to start the loaded operating system + /// to start the loaded operating system. /// public string CsLastLoadInfo { get; internal set; } /// - /// Name of the computer manufacturer + /// Name of the computer manufacturer. /// public string CsManufacturer { get; internal set; } /// - /// Product name that a manufacturer gives to a computer + /// Product name that a manufacturer gives to a computer. /// public string CsModel { get; internal set; } /// - /// Key of a CIM_System instance in an enterprise environment + /// Key of a CIM_System instance in an enterprise environment. /// - public string CsName { get; internal set; } //TODO: get rid of this? Is this about CIM rather than about the computer? + public string CsName { get; internal set; } /// /// An array of objects describing any @@ -2648,14 +2625,14 @@ public class ComputerInfo public NetworkAdapter[] CsNetworkAdapters { get; internal set; } /// - /// If True, the network Server Mode is enabled + /// If True, the network Server Mode is enabled. /// public bool? CsNetworkServerModeEnabled { get; internal set; } /// - /// Number of logical processors available on the computer + /// Number of logical processors available on the computer. /// - public UInt32? CsNumberOfLogicalProcessors { get; internal set; } + public uint? CsNumberOfLogicalProcessors { get; internal set; } /// /// Number of physical processors currently available on a system. @@ -2667,7 +2644,7 @@ public class ComputerInfo /// then the value of CsNumberOfProcessors is 2 and CsNumberOfLogicalProcessors /// is 4. The processors may be multicore or they may be hyperthreading processors /// - public UInt32? CsNumberOfProcessors { get; internal set; } + public uint? CsNumberOfProcessors { get; internal set; } /// /// Array of objects describing each processor on the system. @@ -2678,14 +2655,14 @@ public class ComputerInfo /// /// Array of free-form strings that an OEM defines. /// For example, an OEM defines the part numbers for system reference - /// documents, manufacturer contact information, and so on + /// documents, manufacturer contact information, and so on. /// [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] public string[] CsOEMStringArray { get; internal set; } /// /// If True, the computer is part of a domain. - /// If the value is NULL, the computer is not in a domain or the status is unknown + /// If the value is NULL, the computer is not in a domain or the status is unknown. /// public bool? CsPartOfDomain { get; internal set; } @@ -2693,12 +2670,12 @@ public class ComputerInfo /// Time delay before a reboot is initiated, in milliseconds. /// It is used after a system power cycle, local or remote system reset, /// and automatic system reset. A value of –1 (minus one) indicates that - /// the pause value is unknown + /// the pause value is unknown. /// - public Int64? CsPauseAfterReset { get; internal set; } + public long? CsPauseAfterReset { get; internal set; } /// - /// Type of the computer in use, such as laptop, desktop, or tablet + /// Type of the computer in use, such as laptop, desktop, or tablet. /// public PCSystemType? CsPCSystemType { get; internal set; } @@ -2708,7 +2685,7 @@ public class ComputerInfo public PCSystemTypeEx? CsPCSystemTypeEx { get; internal set; } /// - /// Array of the specific power-related capabilities of a logical device + /// Array of the specific power-related capabilities of a logical device. /// [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] public PowerManagementCapabilities[] CsPowerManagementCapabilities { get; internal set; } @@ -2725,9 +2702,10 @@ public class ComputerInfo public bool? CsPowerManagementSupported { get; internal set; } /// - /// System hardware security setting for Power-On Password Status + /// System hardware security setting for Power-On Password Status. /// - //public PowerOnPasswordStatus? CsPowerOnPasswordStatus { get; internal set; } + // public PowerOnPasswordStatus? CsPowerOnPasswordStatus { get; internal set; } + public HardwareSecurity? CsPowerOnPasswordStatus { get; internal set; } /// @@ -2736,19 +2714,20 @@ public class ComputerInfo public PowerState? CsPowerState { get; internal set; } /// - /// State of the power supply or supplies when last booted + /// State of the power supply or supplies when last booted. /// - //public PowerSupplyState? CsPowerSupplyState { get; internal set; } + // public PowerSupplyState? CsPowerSupplyState { get; internal set; } + public SystemElementState? CsPowerSupplyState { get; internal set; } /// /// Contact information for the primary system owner. - /// For example, phone number, email address, and so on + /// For example, phone number, email address, and so on. /// public string CsPrimaryOwnerContact { get; internal set; } /// - /// Name of the primary system owner + /// Name of the primary system owner. /// public string CsPrimaryOwnerName { get; internal set; } @@ -2759,30 +2738,30 @@ public class ComputerInfo /// /// Number of automatic resets since the last reset. - /// A value of –1 (minus one) indicates that the count is unknown + /// A value of –1 (minus one) indicates that the count is unknown. /// - public Int16? CsResetCount { get; internal set; } + public short? CsResetCount { get; internal set; } /// /// Number of consecutive times a system reset is attempted. - /// A value of –1 (minus one) indicates that the limit is unknown + /// A value of –1 (minus one) indicates that the limit is unknown. /// - public Int16? CsResetLimit { get; internal set; } + public short? CsResetLimit { get; internal set; } /// /// Array that specifies the roles of a system in the information - /// technology environment + /// technology environment. /// [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] public string[] CsRoles { get; internal set; } /// - /// Statis pf the computer system + /// Statis pf the computer system. /// public string CsStatus { get; internal set; } /// - /// Array of the support contact information for the Windows operating system + /// Array of the support contact information for the Windows operating system. /// [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] public string[] CsSupportContactDescription { get; internal set; } @@ -2790,25 +2769,26 @@ public class ComputerInfo /// /// The family to which a particular computer belongs. /// A family refers to a set of computers that are similar but not - /// identical from a hardware or software point of view + /// identical from a hardware or software point of view. /// public string CsSystemFamily { get; internal set; } /// /// Identifies a particular computer configuration for sale. - /// It is sometimes also called a product ID or purchase order number + /// It is sometimes also called a product ID or purchase order number. /// public string CsSystemSKUNumber { get; internal set; } /// - /// System running on the Windows-based computer + /// System running on the Windows-based computer. /// public string CsSystemType { get; internal set; } /// - /// Thermal state of the system when last booted + /// Thermal state of the system when last booted. /// - //public ThermalState? CsThermalState { get; internal set; } + // public ThermalState? CsThermalState { get; internal set; } + public SystemElementState? CsThermalState { get; internal set; } /// @@ -2819,13 +2799,13 @@ public class ComputerInfo /// return an accurate value for the physical memory. For example, /// it is not accurate if the BIOS is using some of the physical memory /// - public UInt64? CsTotalPhysicalMemory { get; internal set; } + public ulong? CsTotalPhysicalMemory { get; internal set; } /// /// Size of physically installed memory, as reported by the Windows API - /// function GetPhysicallyInstalledSystemMemory + /// function GetPhysicallyInstalledSystemMemory. /// - public UInt64? CsPhysicallyInstalledMemory { get; internal set; } + public ulong? CsPhysicallyInstalledMemory { get; internal set; } /// /// Name of a user that is logged on currently. @@ -2838,83 +2818,83 @@ public class ComputerInfo public string CsUserName { get; internal set; } /// - /// Event that causes the system to power up + /// Event that causes the system to power up. /// public WakeUpType? CsWakeUpType { get; internal set; } /// - /// Name of the workgroup for this computer + /// Name of the workgroup for this computer. /// public string CsWorkgroup { get; internal set; } #endregion Computer System #region Operating System /// - /// Name of the operating system + /// Name of the operating system. /// public string OsName { get; internal set; } /// - /// Type of operating system + /// Type of operating system. /// public OSType? OsType { get; internal set; } /// - /// SKU number for the operating system + /// SKU number for the operating system. /// public OperatingSystemSKU? OsOperatingSystemSKU { get; internal set; } /// - /// Version number of the operating system + /// Version number of the operating system. /// public string OsVersion { get; internal set; } /// /// String that indicates the latest service pack installed on a computer. - /// If no service pack is installed, the string is NULL + /// If no service pack is installed, the string is NULL. /// public string OsCSDVersion { get; internal set; } /// - /// Build number of the operating system + /// Build number of the operating system. /// public string OsBuildNumber { get; internal set; } /// /// Array of objects containing information about /// any Quick-Fix Engineering patches (Hot Fixes) applied to the operating - /// system + /// system. /// [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] public HotFix[] OsHotFixes { get; internal set; } /// - /// Name of the disk drive from which the Windows operating system starts + /// Name of the disk drive from which the Windows operating system starts. /// public string OsBootDevice { get; internal set; } /// - /// Physical disk partition on which the operating system is installed + /// Physical disk partition on which the operating system is installed. /// public string OsSystemDevice { get; internal set; } /// - /// System directory of the operating system + /// System directory of the operating system. /// public string OsSystemDirectory { get; internal set; } /// - /// Letter of the disk drive on which the operating system resides + /// Letter of the disk drive on which the operating system resides. /// public string OsSystemDrive { get; internal set; } /// - /// Windows directory of the operating system + /// Windows directory of the operating system. /// public string OsWindowsDirectory { get; internal set; } /// - /// Code for the country/region that an operating system uses + /// Code for the country/region that an operating system uses. /// /// /// Values are based on international phone dialing prefixes—also @@ -2924,9 +2904,9 @@ public class ComputerInfo /// /// Number, in minutes, an operating system is offset from Greenwich - /// mean time (GMT). The number is positive, negative, or zero + /// mean time (GMT). The number is positive, negative, or zero. /// - public Int16? OsCurrentTimeZone { get; internal set; } + public short? OsCurrentTimeZone { get; internal set; } /// /// Language identifier used by the operating system. @@ -2940,70 +2920,70 @@ public class ComputerInfo public string OsLocaleID { get; internal set; } // From Win32_OperatingSystem.Locale /// - /// The culture name, such as "en-US", derived from the property + /// The culture name, such as "en-US", derived from the property. /// public string OsLocale { get; internal set; } /// - /// Operating system version of the local date and time-of-day + /// Operating system version of the local date and time-of-day. /// public DateTime? OsLocalDateTime { get; internal set; } /// - /// Date and time the operating system was last restarted + /// Date and time the operating system was last restarted. /// public DateTime? OsLastBootUpTime { get; internal set; } /// /// The interval between the time the operating system was last - /// restarted and the current time + /// restarted and the current time. /// public TimeSpan? OsUptime { get; internal set; } /// - /// Type of build used for the operating system + /// Type of build used for the operating system. /// public string OsBuildType { get; internal set; } /// - /// Code page value the operating system uses + /// Code page value the operating system uses. /// public string OsCodeSet { get; internal set; } /// - /// If true, then the data execution prevention hardware feature is available + /// If true, then the data execution prevention hardware feature is available. /// public bool? OsDataExecutionPreventionAvailable { get; internal set; } /// /// When the data execution prevention hardware feature is available, /// this property indicates that the feature is set to work for 32-bit - /// applications if true + /// applications if true. /// public bool? OsDataExecutionPrevention32BitApplications { get; internal set; } /// /// When the data execution prevention hardware feature is available, /// this property indicates that the feature is set to work for drivers - /// if true + /// if true. /// public bool? OsDataExecutionPreventionDrivers { get; internal set; } /// /// Indicates which Data Execution Prevention (DEP) setting is applied. /// The DEP setting specifies the extent to which DEP applies to 32-bit - /// applications on the system. DEP is always applied to the Windows kernel + /// applications on the system. DEP is always applied to the Windows kernel. /// public DataExecutionPreventionSupportPolicy? OsDataExecutionPreventionSupportPolicy { get; internal set; } /// - /// If true, the operating system is a checked (debug) build + /// If true, the operating system is a checked (debug) build. /// public bool? OsDebug { get; internal set; } /// /// If True, the operating system is distributed across several computer - /// system nodes. If so, these nodes should be grouped as a cluster + /// system nodes. If so, these nodes should be grouped as a cluster. /// public bool? OsDistributed { get; internal set; } @@ -3013,7 +2993,7 @@ public class ComputerInfo public OSEncryptionLevel? OsEncryptionLevel { get; internal set; } /// - /// Increased priority given to the foreground application + /// Increased priority given to the foreground application. /// public ForegroundApplicationBoost? OsForegroundApplicationBoost { get; internal set; } @@ -3026,30 +3006,30 @@ public class ComputerInfo /// physical memory, but what is reported to the operating system /// as available to it. /// - public UInt64? OsTotalVisibleMemorySize { get; internal set; } + public ulong? OsTotalVisibleMemorySize { get; internal set; } /// - /// Number, in kilobytes, of physical memory currently unused and available + /// Number, in kilobytes, of physical memory currently unused and available. /// - public UInt64? OsFreePhysicalMemory { get; internal set; } + public ulong? OsFreePhysicalMemory { get; internal set; } /// - /// Number, in kilobytes, of virtual memory + /// Number, in kilobytes, of virtual memory. /// - public UInt64? OsTotalVirtualMemorySize { get; internal set; } + public ulong? OsTotalVirtualMemorySize { get; internal set; } /// - /// Number, in kilobytes, of virtual memory currently unused and available + /// Number, in kilobytes, of virtual memory currently unused and available. /// - public UInt64? OsFreeVirtualMemory { get; internal set; } + public ulong? OsFreeVirtualMemory { get; internal set; } /// - /// Number, in kilobytes, of virtual memory currently in use + /// Number, in kilobytes, of virtual memory currently in use. /// - public UInt64? OsInUseVirtualMemory { get; internal set; } + public ulong? OsInUseVirtualMemory { get; internal set; } /// - /// Total swap space in kilobytes + /// Total swap space in kilobytes. /// /// /// This value may be NULL (unspecified) if the swap space is not @@ -3058,24 +3038,24 @@ public class ComputerInfo /// can be swapped out when the free page list falls and remains below /// a specified amount /// - public UInt64? OsTotalSwapSpaceSize { get; internal set; } + public ulong? OsTotalSwapSpaceSize { get; internal set; } /// /// Total number of kilobytes that can be stored in the operating system /// paging files—0 (zero) indicates that there are no paging files. /// Be aware that this number does not represent the actual physical - /// size of the paging file on disk + /// size of the paging file on disk. /// - public UInt64? OsSizeStoredInPagingFiles { get; internal set; } + public ulong? OsSizeStoredInPagingFiles { get; internal set; } /// /// Number, in kilobytes, that can be mapped into the operating system - /// paging files without causing any other pages to be swapped out + /// paging files without causing any other pages to be swapped out. /// - public UInt64? OsFreeSpaceInPagingFiles { get; internal set; } + public ulong? OsFreeSpaceInPagingFiles { get; internal set; } /// - /// Array of fiel paths to the operating system's paging files + /// Array of file paths to the operating system's paging files. /// [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] public string[] OsPagingFiles { get; internal set; } @@ -3086,7 +3066,7 @@ public class ComputerInfo public string OsHardwareAbstractionLayer { get; internal set; } /// - /// Indicates the install date + /// Indicates the install date. /// public DateTime? OsInstallDate { get; internal set; } @@ -3097,18 +3077,18 @@ public class ComputerInfo public string OsManufacturer { get; internal set; } /// - /// Maximum number of process contexts the operating system can support + /// Maximum number of process contexts the operating system can support. /// - public UInt32? OsMaxNumberOfProcesses { get; internal set; } + public uint? OsMaxNumberOfProcesses { get; internal set; } /// - /// Maximum number, in kilobytes, of memory that can be allocated to a process + /// Maximum number, in kilobytes, of memory that can be allocated to a process. /// - public UInt64? OsMaxProcessMemorySize { get; internal set; } + public ulong? OsMaxProcessMemorySize { get; internal set; } /// /// Array of Multilingual User Interface Pack (MUI Pack) languages installed - /// on the computer + /// on the computer. /// [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] public string[] OsMuiLanguages { get; internal set; } @@ -3116,195 +3096,195 @@ public class ComputerInfo /// /// Number of user licenses for the operating system. /// - public UInt32? OsNumberOfLicensedUsers { get; internal set; } + public uint? OsNumberOfLicensedUsers { get; internal set; } /// - /// Number of process contexts currently loaded or running on the operating system + /// Number of process contexts currently loaded or running on the operating system. /// - public UInt32? OsNumberOfProcesses { get; internal set; } + public uint? OsNumberOfProcesses { get; internal set; } /// /// Number of user sessions for which the operating system is storing - /// state information currently + /// state information currently. /// - public UInt32? OsNumberOfUsers { get; internal set; } + public uint? OsNumberOfUsers { get; internal set; } /// - /// Company name for the registered user of the operating system + /// Company name for the registered user of the operating system. /// public string OsOrganization { get; internal set; } /// - /// Architecture of the operating system, as opposed to the processor + /// Architecture of the operating system, as opposed to the processor. /// public string OsArchitecture { get; internal set; } /// - /// Language version of the operating system installed + /// Language version of the operating system installed. /// public string OsLanguage { get; internal set; } /// /// Array of objects indicating installed - /// and licensed product additions to the operating system + /// and licensed product additions to the operating system. /// [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] public OSProductSuite[] OsProductSuites { get; internal set; } /// - /// Additional description for the current operating system version + /// Additional description for the current operating system version. /// public string OsOtherTypeDescription { get; internal set; } /// /// If True, the physical address extensions (PAE) are enabled by the - /// operating system running on Intel processors + /// operating system running on Intel processors. /// public bool? OsPAEEnabled { get; internal set; } /// /// Specifies whether the operating system booted from an external USB device. /// If true, the operating system has detected it is booting on a supported - /// locally connected storage device + /// locally connected storage device. /// public bool? OsPortableOperatingSystem { get; internal set; } /// - /// Specifies whether this is the primary operating system + /// Specifies whether this is the primary operating system. /// public bool? OsPrimary { get; internal set; } /// - /// Additional system information + /// Additional system information. /// public ProductType? OsProductType { get; internal set; } /// - /// Name of the registered user of the operating system + /// Name of the registered user of the operating system. /// public string OsRegisteredUser { get; internal set; } /// - /// Operating system product serial identification number + /// Operating system product serial identification number. /// public string OsSerialNumber { get; internal set; } /// - /// Major version of the service pack installed on the computer system + /// Major version of the service pack installed on the computer system. /// - public UInt16? OsServicePackMajorVersion { get; internal set; } + public ushort? OsServicePackMajorVersion { get; internal set; } /// - /// Minor version of the service pack installed on the computer system + /// Minor version of the service pack installed on the computer system. /// - public UInt16? OsServicePackMinorVersion { get; internal set; } + public ushort? OsServicePackMinorVersion { get; internal set; } /// - /// Current status + /// Current status. /// public string OsStatus { get; internal set; } /// - /// Product suites available on the operating system + /// Product suites available on the operating system. /// [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] public OSProductSuite[] OsSuites { get; internal set; } /// - /// Server level of the operating system, if the operating system is a server + /// Server level of the operating system, if the operating system is a server. /// public ServerLevel? OsServerLevel { get; internal set; } #endregion Operating System #region Misc Info /// - /// Layout of the (first) keyboard attached to the system + /// Layout of the (first) keyboard attached to the system. /// public string KeyboardLayout { get; internal set; } /// - /// Name of the system's current time zone + /// Name of the system's current time zone. /// public string TimeZone { get; internal set; } /// - /// Path to the system's logon server + /// Path to the system's logon server. /// public string LogonServer { get; internal set; } /// - /// Power platform role + /// Power platform role. /// public PowerPlatformRole? PowerPlatformRole { get; internal set; } /// - /// If true, a HyperVisor was detected + /// If true, a HyperVisor was detected. /// public bool? HyperVisorPresent { get; internal set; } /// /// If a HyperVisor is not present, indicates the state of the - /// requirement that the Data Execution Prevention feature is available + /// requirement that the Data Execution Prevention feature is available. /// public bool? HyperVRequirementDataExecutionPreventionAvailable { get; internal set; } /// /// If a HyperVisor is not present, indicates the state of the /// requirement that the processor supports address translation - /// extensions used for virtualization + /// extensions used for virtualization. /// public bool? HyperVRequirementSecondLevelAddressTranslation { get; internal set; } /// /// If a HyperVisor is not present, indicates the state of the /// requirement that the firmware has enabled virtualization - /// extensions + /// extensions. /// public bool? HyperVRequirementVirtualizationFirmwareEnabled { get; internal set; } /// /// If a HyperVisor is not present, indicates the state of the - /// requirement that the processor supports Intel or AMD Virtual - /// Machine Monitor extensions + /// requirement that the processor supports Intel or AMD Virtual + /// Machine Monitor extensions. /// public bool? HyperVRequirementVMMonitorModeExtensions { get; internal set; } /// - /// Indicates the status of the Device Guard features + /// Indicates the status of the Device Guard features. /// public DeviceGuardSmartStatus? DeviceGuardSmartStatus { get; internal set; } /// - /// Required Device Guard security properties + /// Required Device Guard security properties. /// [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] public DeviceGuardHardwareSecure[] DeviceGuardRequiredSecurityProperties { get; internal set; } /// - /// Available Device Guard security properties + /// Available Device Guard security properties. /// [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] public DeviceGuardHardwareSecure[] DeviceGuardAvailableSecurityProperties { get; internal set; } /// - /// Configured Device Guard security services + /// Configured Device Guard security services. /// [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] public DeviceGuardSoftwareSecure[] DeviceGuardSecurityServicesConfigured { get; internal set; } /// - /// Running Device Guard security services + /// Running Device Guard security services. /// [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] public DeviceGuardSoftwareSecure[] DeviceGuardSecurityServicesRunning { get; internal set; } /// - /// Status of the Device Guard Code Integrity policy enforcement + /// Status of the Device Guard Code Integrity policy enforcement. /// public DeviceGuardConfigCodeIntegrityStatus? DeviceGuardCodeIntegrityPolicyEnforcementStatus { get; internal set; } /// - /// Status of the Device Guard user mode Code Integrity policy enforcement + /// Status of the Device Guard user mode Code Integrity policy enforcement. /// public DeviceGuardConfigCodeIntegrityStatus? DeviceGuardUserModeCodeIntegrityPolicyEnforcementStatus { get; internal set; } #endregion Misc Info @@ -3313,311 +3293,311 @@ public class ComputerInfo #region Enums used in the output objects /// - /// System hardware security settings for administrator password status + /// System hardware security settings for administrator password status. /// public enum AdminPasswordStatus { /// - /// Feature is disabled + /// Feature is disabled. /// Disabled = 0, /// - /// Feature is Enabled + /// Feature is Enabled. /// Enabled = 1, /// - /// Feature is not implemented + /// Feature is not implemented. /// NotImplemented = 2, /// - /// Status is unknown + /// Status is unknown. /// Unknown = 3 } /// /// Actions related to the BootOptionOn* properties of the Win32_ComputerSystem - /// CIM class + /// CIM class. /// [SuppressMessage("Microsoft.Design", "CA1008:EnumsShouldHaveZeroValue", Justification = "The underlying MOF definition does not contain a zero value. The converter method will handle it appropriately.")] public enum BootOptionAction { - // - // This value is reserved - // - //Reserved = 0, + // + // This value is reserved + // + // Reserved = 0, /// - /// Boot into operating system + /// Boot into operating system. /// OperatingSystem = 1, /// - /// Boot into system utilities + /// Boot into system utilities. /// SystemUtilities = 2, /// - /// Do not reboot + /// Do not reboot. /// DoNotReboot = 3 } /// - /// Indicates the state of a system element + /// Indicates the state of a system element. /// [SuppressMessage("Microsoft.Design", "CA1008:EnumsShouldHaveZeroValue", Justification = "The underlying MOF definition does not contain a zero value. The converter method will handle it appropriately.")] public enum SystemElementState { /// - /// The element state is something other than those in this Enum + /// The element state is something other than those in this Enum. /// Other = 1, /// - /// The element state is unknown + /// The element state is unknown. /// Unknown = 2, /// - /// The element is in Safe state + /// The element is in Safe state. /// Safe = 3, /// - /// The element is in Warning state + /// The element is in Warning state. /// Warning = 4, /// - /// The element is in Critical state + /// The element is in Critical state. /// Critical = 5, /// - /// The element is in Non-Recoverable state + /// The element is in Non-Recoverable state. /// NonRecoverable = 6 } /// - /// Specifies the processor architecture + /// Specifies the processor architecture. /// public enum CpuArchitecture { /// - /// Architecture is Intel x86 + /// Architecture is Intel x86. /// x86 = 0, /// - /// Architecture is MIPS + /// Architecture is MIPS. /// MIPs = 1, /// - /// Architecture is DEC Alpha + /// Architecture is DEC Alpha. /// Alpha = 2, /// - /// Architecture is Motorola PowerPC + /// Architecture is Motorola PowerPC. /// PowerPC = 3, /// - /// Architecture is ARM + /// Architecture is ARM. /// ARM = 5, /// - /// Architecture is Itanium-based 64-bit + /// Architecture is Itanium-based 64-bit. /// ia64 = 6, /// - /// Architecture is Intel 64-bit + /// Architecture is Intel 64-bit. /// x64 = 9 } /// - /// Specifies a CPU's availability and status + /// Specifies a CPU's availability and status. /// [SuppressMessage("Microsoft.Design", "CA1008:EnumsShouldHaveZeroValue", Justification = "The underlying MOF definition does not contain a zero value. The converter method will handle it appropriately.")] public enum CpuAvailability { /// - /// A state other than those specified in CpuAvailability + /// A state other than those specified in CpuAvailability. /// Other = 1, /// - /// Availability status is unknown + /// Availability status is unknown. /// Unknown = 2, /// - /// The device is running or at full power + /// The device is running or at full power. /// RunningOrFullPower = 3, /// - /// Device is in a Warning state + /// Device is in a Warning state. /// Warning = 4, /// - /// Availability status is In Test + /// Availability status is In Test. /// InTest = 5, /// - /// Status is not applicable to this device + /// Status is not applicable to this device. /// NotApplicable = 6, /// - /// The device is powered off + /// The device is powered off. /// PowerOff = 7, /// - /// Availability status is Offline + /// Availability status is Offline. /// OffLine = 8, /// - /// Availability status is Off-Duty + /// Availability status is Off-Duty. /// OffDuty = 9, /// - /// Availability status is Degraded + /// Availability status is Degraded. /// Degraded = 10, /// - /// Availability status is Not Installed + /// Availability status is Not Installed. /// NotInstalled = 11, /// - /// Availability status is Install Error + /// Availability status is Install Error. /// InstallError = 12, /// - /// The device is known to be in a power save state, but its exact status is unknown + /// The device is known to be in a power save state, but its exact status is unknown. /// PowerSaveUnknown = 13, /// /// The device is in a power save state, but is still functioning, - /// and may exhibit decreased performance + /// and may exhibit decreased performance. /// PowerSaveLowPowerMode = 14, /// - /// The device is not functioning, but can be brought to full power quickly + /// The device is not functioning, but can be brought to full power quickly. /// PowerSaveStandby = 15, /// - /// The device is in a power-cycle state + /// The device is in a power-cycle state. /// PowerCycle = 16, /// - /// The device is in a warning state, though also in a power save state + /// The device is in a warning state, though also in a power save state. /// PowerSaveWarning = 17, /// - /// The device is paused + /// The device is paused. /// Paused = 18, /// - /// The device is not ready + /// The device is not ready. /// NotReady = 19, /// - /// The device is not configured + /// The device is not configured. /// NotConfigured = 20, /// - /// The device is quiet + /// The device is quiet. /// Quiesced = 21 } /// - /// Specifies that current status of the processor + /// Specifies that current status of the processor. /// [SuppressMessage("Microsoft.Design", "CA1027:MarkEnumsWithFlags", Justification = "The underlying MOF definition is not a bit field.")] public enum CpuStatus { /// - /// CPU status is Unknown + /// CPU status is Unknown. /// Unknown = 0, /// - /// CPU status is Enabled + /// CPU status is Enabled. /// Enabled = 1, /// - /// CPU status is Disabled by User via BIOS Setup + /// CPU status is Disabled by User via BIOS Setup. /// DisabledByUser = 2, /// - /// CPU status is Disabled by BIOS + /// CPU status is Disabled by BIOS. /// DisabledByBIOS = 3, /// - /// CPU is Idle + /// CPU is Idle. /// Idle = 4, // // This value is reserved // - //Reserved_5 = 5, + // Reserved_5 = 5, // // This value is reserved // - //Reserved_6 = 6, + // Reserved_6 = 6, /// - /// CPU is in another state + /// CPU is in another state. /// Other = 7 } /// - /// Data Execution Prevention (DEP) settings + /// Data Execution Prevention (DEP) settings. /// public enum DataExecutionPreventionSupportPolicy { - //Unknown = -1, + // Unknown = -1, /// - /// DEP is turned off for all 32-bit applications on the computer with no exceptions + /// DEP is turned off for all 32-bit applications on the computer with no exceptions. /// AlwaysOff = 0, /// - /// DEP is enabled for all 32-bit applications on the computer + /// DEP is enabled for all 32-bit applications on the computer. /// AlwaysOn = 1, @@ -3626,311 +3606,326 @@ public enum DataExecutionPreventionSupportPolicy /// Windows-based services. However, it is off by default for all 32-bit /// applications. A user or administrator must explicitly choose either /// the Always On or the Opt Out setting before DEP can be applied to - /// 32-bit applications + /// 32-bit applications. /// OptIn = 2, /// /// DEP is enabled by default for all 32-bit applications. A user or /// administrator can explicitly remove support for a 32-bit - /// application by adding the application to an exceptions list + /// application by adding the application to an exceptions list. /// OptOut = 3 } /// - /// Status of the Device Guard feature + /// Status of the Device Guard feature. /// public enum DeviceGuardSmartStatus { /// - /// Device Guard is off + /// Device Guard is off. /// Off = 0, /// - /// Device Guard is Configured + /// Device Guard is Configured. /// Configured = 1, /// - /// Device Guard is Running + /// Device Guard is Running. /// Running = 2 } /// - /// Configuration status of the Device Guard Code Integrity + /// Configuration status of the Device Guard Code Integrity. /// public enum DeviceGuardConfigCodeIntegrityStatus { /// - /// Code Integrity is off + /// Code Integrity is off. /// Off = 0, /// - /// Code Integrity uses Audit mode + /// Code Integrity uses Audit mode. /// AuditMode = 1, /// - /// Code Integrity uses Enforcement mode + /// Code Integrity uses Enforcement mode. /// EnforcementMode = 2 } /// - /// Device Guard hardware security properties + /// Device Guard hardware security properties. /// [SuppressMessage("Microsoft.Design", "CA1008:EnumsShouldHaveZeroValue", Justification = "The underlying MOF definition does not contain a zero value. The converter method will handle it appropriately.")] public enum DeviceGuardHardwareSecure { /// - /// Base Virtualization Support + /// Base Virtualization Support. /// BaseVirtualizationSupport = 1, /// - /// Secure Boot + /// Secure Boot. /// SecureBoot = 2, /// - /// DMA Protection + /// DMA Protection. /// DMAProtection = 3, /// - /// Secure Memory Overwrite + /// Secure Memory Overwrite. + /// + SecureMemoryOverwrite = 4, + + /// + /// UEFI Code Readonly. + /// + UEFICodeReadonly = 5, + + /// + /// SMM Security Mitigations 1.0. /// - SecureMemoryOverwrite = 4 + SMMSecurityMitigations = 6, + + /// + /// Mode Based Execution Control. + /// + ModeBasedExecutionControl = 7 } /// - /// Device Guard software security properties + /// Device Guard software security properties. /// [SuppressMessage("Microsoft.Design", "CA1008:EnumsShouldHaveZeroValue", Justification = "The underlying MOF definition does not contain a zero value. The converter method will handle it appropriately.")] public enum DeviceGuardSoftwareSecure { /// - /// Credential Guard + /// Credential Guard. /// CredentialGuard = 1, /// - /// Hypervisor enforced Code Integrity + /// Hypervisor enforced Code Integrity. /// HypervisorEnforcedCodeIntegrity = 2 } /// - /// Role of a computer in an assigned domain workgroup + /// Role of a computer in an assigned domain workgroup. /// public enum DomainRole { /// - /// Standalone Workstation + /// Standalone Workstation. /// StandaloneWorkstation = 0, /// - /// Member Workstation + /// Member Workstation. /// MemberWorkstation = 1, /// - /// Standalone Server + /// Standalone Server. /// StandaloneServer = 2, /// - /// Member Server + /// Member Server. /// MemberServer = 3, /// - /// Backup Domain Controller + /// Backup Domain Controller. /// BackupDomainController = 4, /// - /// Primary Domain Controller + /// Primary Domain Controller. /// PrimaryDomainController = 5 } /// - /// Specifies a firmware type + /// Specifies a firmware type. /// public enum FirmwareType { /// - /// The firmware type is unknown + /// The firmware type is unknown. /// Unknown = 0, /// - /// The computer booted in legacy BIOS mode + /// The computer booted in legacy BIOS mode. /// Bios = 1, /// - /// The computer booted in UEFI mode + /// The computer booted in UEFI mode. /// Uefi = 2, /// - /// Not implemented + /// Not implemented. /// Max = 3 } /// - /// Increase in priority given to the foreground application + /// Increase in priority given to the foreground application. /// public enum ForegroundApplicationBoost { /// - /// The system boosts the quantum length by 6 + /// The system boosts the quantum length by 6. /// None = 0, /// - /// The system boosts the quantum length by 12 + /// The system boosts the quantum length by 12. /// Minimum = 1, /// - /// The system boosts the quantum length by 18 + /// The system boosts the quantum length by 18. /// Maximum = 2 } /// - /// hardware security settings for the reset button on a computer + /// Hardware security settings for the reset button on a computer. /// public enum FrontPanelResetStatus { /// - /// Reset button is disabled + /// Reset button is disabled. /// Disabled = 0, /// - /// Reset button is enabled + /// Reset button is enabled. /// Enabled = 1, /// - /// Hardware security settings are not implement + /// Hardware security settings are not implement. /// NotImplemented = 2, /// - /// Unknown security setting + /// Unknown security setting. /// Unknown = 3 } /// - /// Indicates a hardware security setting + /// Indicates a hardware security setting. /// public enum HardwareSecurity { /// - /// Hardware security is disabled + /// Hardware security is disabled. /// Disabled = 0, /// - /// Hardware security is enabled + /// Hardware security is enabled. /// Enabled = 1, /// - /// Hardware security is not implemented + /// Hardware security is not implemented. /// NotImplemented = 2, /// - /// Hardware security setting is unknown + /// Hardware security setting is unknown. /// Unknown = 3 } /// - /// State of the network adapter connection to the network + /// State of the network adapter connection to the network. /// public enum NetConnectionStatus { /// - /// Adapter is disconnected + /// Adapter is disconnected. /// Disconnected = 0, /// - /// Adapter is connecting + /// Adapter is connecting. /// Connecting = 1, /// - /// Adapter is connected + /// Adapter is connected. /// Connected = 2, /// - /// Adapter is disconnecting + /// Adapter is disconnecting. /// Disconnecting = 3, /// - /// Adapter hardware is not present + /// Adapter hardware is not present. /// HardwareNotPresent = 4, /// - /// Adapter hardware is disabled + /// Adapter hardware is disabled. /// HardwareDisabled = 5, /// - /// Adapter has a hardware malfunction + /// Adapter has a hardware malfunction. /// HardwareMalfunction = 6, /// - /// Media is disconnected + /// Media is disconnected. /// MediaDisconnected = 7, /// - /// Adapter is authenticating + /// Adapter is authenticating. /// Authenticating = 8, /// - /// Authentication has succeeded + /// Authentication has succeeded. /// AuthenticationSucceeded = 9, /// - /// Authentication has failed + /// Authentication has failed. /// AuthenticationFailed = 10, /// - /// Address is invalid + /// Address is invalid. /// InvalidAddress = 11, /// - /// Credentials are required + /// Credentials are required. /// CredentialsRequired = 12, /// - /// Other unspecified state + /// Other unspecified state. /// Other = 13 } @@ -3941,23 +3936,23 @@ public enum NetConnectionStatus public enum OSEncryptionLevel { /// - /// 40-bit encryption + /// 40-bit encryption. /// Encrypt40Bits = 0, /// - /// 128-bit encryption + /// 128-bit encryption. /// Encrypt128Bits = 1, /// - /// n-bit encryption + /// N-bit encryption. /// EncryptNBits = 2 } /// - /// Indicates installed and licensed system product additions to the operating system + /// Indicates installed and licensed system product additions to the operating system. /// [SuppressMessage("Microsoft.Design", "CA1008:EnumsShouldHaveZeroValue", Justification = "The underlying MOF definition does not contain a zero value. The converter method will handle it appropriately.")] [FlagsAttribute] @@ -3965,68 +3960,68 @@ public enum OSProductSuite { /// /// Microsoft Small Business Server was once installed, but may have - /// been upgraded to another version of Windows + /// been upgraded to another version of Windows. /// SmallBusinessServer = 0x0001, /// - /// Windows Server 2008 Enterprise is installed + /// Windows Server 2008 Enterprise is installed. /// Server2008Enterprise = 0x0002, /// - /// Windows BackOffice components are installed + /// Windows BackOffice components are installed. /// BackOfficeComponents = 0x0004, /// - /// Communication Server is installed + /// Communication Server is installed. /// CommunicationsServer = 0x0008, /// - /// Terminal Services is installed + /// Terminal Services is installed. /// TerminalServices = 0x0010, /// /// Microsoft Small Business Server is installed with the restrictive - /// client license + /// client license. /// SmallBusinessServerRestricted = 0x0020, /// - /// Windows Embedded is installed + /// Windows Embedded is installed. /// WindowsEmbedded = 0x0040, /// - /// A Datacenter edition is installed + /// A Datacenter edition is installed. /// DatacenterEdition = 0x0080, /// - /// Terminal Services is installed, but only one interactive session is supported + /// Terminal Services is installed, but only one interactive session is supported. /// TerminalServicesSingleSession = 0x0100, /// - /// Windows Home Edition is installed + /// Windows Home Edition is installed. /// HomeEdition = 0x0200, /// - /// Web Server Edition is installed + /// Web Server Edition is installed. /// WebServerEdition = 0x0400, /// - /// Storage Server Edition is installed + /// Storage Server Edition is installed. /// StorageServerEdition = 0x2000, /// - /// Compute Cluster Edition is installed + /// Compute Cluster Edition is installed. /// ComputeClusterEdition = 0x4000 } @@ -4037,147 +4032,147 @@ public enum OSProductSuite public enum OperatingSystemSKU { /// - /// The SKU is undefined + /// The SKU is undefined. /// Undefined = 0, /// - /// SKU is Ultimate Edition + /// SKU is Ultimate Edition. /// UltimateEdition = 1, /// - /// SKU is Home Basic Edition + /// SKU is Home Basic Edition. /// HomeBasicEdition = 2, /// - /// SKU is Home Premium Edition + /// SKU is Home Premium Edition. /// HomePremiumEdition = 3, /// - /// SKU is Enterprise Edition + /// SKU is Enterprise Edition. /// EnterpriseEdition = 4, /// - /// SKU is Home Basic N Edition + /// SKU is Home Basic N Edition. /// HomeBasicNEdition = 5, /// - /// SKU is Business Edition + /// SKU is Business Edition. /// BusinessEdition = 6, /// - /// SKU is Standard Server Edition + /// SKU is Standard Server Edition. /// StandardServerEdition = 7, /// - /// SKU is Datacenter Server Edition + /// SKU is Datacenter Server Edition. /// DatacenterServerEdition = 8, /// - /// SKU is Small Business Server Edition + /// SKU is Small Business Server Edition. /// SmallBusinessServerEdition = 9, /// - /// SKU is Enterprise Server Edition + /// SKU is Enterprise Server Edition. /// EnterpriseServerEdition = 10, /// - /// SKU is Starter Edition + /// SKU is Starter Edition. /// StarterEdition = 11, /// - /// SKU is Datacenter Server Core Edition + /// SKU is Datacenter Server Core Edition. /// DatacenterServerCoreEdition = 12, /// - /// SKU is Standard Server Core Edition + /// SKU is Standard Server Core Edition. /// StandardServerCoreEdition = 13, /// - /// SKU is Enterprise Server Core Edition + /// SKU is Enterprise Server Core Edition. /// EnterpriseServerCoreEdition = 14, /// - /// SKU is Enterprise Server IA64 Edition + /// SKU is Enterprise Server IA64 Edition. /// EnterpriseServerIA64Edition = 15, /// - /// SKU is Business N Edition + /// SKU is Business N Edition. /// BusinessNEdition = 16, /// - /// SKU is Web Server Edition + /// SKU is Web Server Edition. /// WebServerEdition = 17, /// - /// SKU is Cluster Server Edition + /// SKU is Cluster Server Edition. /// ClusterServerEdition = 18, /// - /// SKU is Home Server Edition + /// SKU is Home Server Edition. /// HomeServerEdition = 19, /// - /// SKU is Storage Express Server Edition + /// SKU is Storage Express Server Edition. /// StorageExpressServerEdition = 20, /// - /// SKU is Storage Standard Server Edition + /// SKU is Storage Standard Server Edition. /// StorageStandardServerEdition = 21, /// - /// SKU is Storage Workgroup Server Edition + /// SKU is Storage Workgroup Server Edition. /// StorageWorkgroupServerEdition = 22, /// - /// SKU is Storage Enterprise Server Edition + /// SKU is Storage Enterprise Server Edition. /// StorageEnterpriseServerEdition = 23, /// - /// SKU is Server For Small Business Edition + /// SKU is Server For Small Business Edition. /// ServerForSmallBusinessEdition = 24, /// - /// SKU is Small Business Server Premium Edition + /// SKU is Small Business Server Premium Edition. /// SmallBusinessServerPremiumEdition = 25, /// - /// SKU is to be determined + /// SKU is to be determined. /// TBD = 26, /// - /// SKU is Windows Enterprise + /// SKU is Windows Enterprise. /// WindowsEnterprise = 27, /// - /// SKU is Windows Ultimate + /// SKU is Windows Ultimate. /// WindowsUltimate = 28, @@ -4187,17 +4182,17 @@ public enum OperatingSystemSKU WebServerCore = 29, /// - /// SKU is Server Foundation + /// SKU is Server Foundation. /// ServerFoundation = 33, /// - /// SKU is Windows Home Server + /// SKU is Windows Home Server. /// WindowsHomeServer = 34, /// - /// SKU is Windows Server Standard without Hyper-V + /// SKU is Windows Server Standard without Hyper-V. /// WindowsServerStandardNoHyperVFull = 36, @@ -4227,7 +4222,7 @@ public enum OperatingSystemSKU WindowsServerEnterpriseNoHyperVCore = 41, /// - /// SKU is Microsoft Hyper-V Server + /// SKU is Microsoft Hyper-V Server. /// MicrosoftHyperVServer = 42, @@ -4252,7 +4247,7 @@ public enum OperatingSystemSKU StorageServerEnterpriseCore = 46, /// - /// SKU is Windows Small Business Server 2011 Essentials + /// SKU is Windows Small Business Server 2011 Essentials. /// WindowsSmallBusinessServer2011Essentials = 50, @@ -4262,597 +4257,597 @@ public enum OperatingSystemSKU SmallBusinessServerPremiumCore = 63, /// - /// SKU is Windows Server Hyper Core V + /// SKU is Windows Server Hyper Core V. /// WindowsServerHyperCoreV = 64, /// - /// SKU is Windows Thin PC + /// SKU is Windows Thin PC. /// WindowsThinPC = 87, /// - /// SKU is Windows Embedded Industry + /// SKU is Windows Embedded Industry. /// WindowsEmbeddedIndustry = 89, /// - /// SKU is Windows RT + /// SKU is Windows RT. /// WindowsRT = 97, /// - /// SKU is Windows Home + /// SKU is Windows Home. /// WindowsHome = 101, /// - /// SKU is Windows Professional with Media Center + /// SKU is Windows Professional with Media Center. /// WindowsProfessionalWithMediaCenter = 103, /// - /// SKU is Windows Mobile + /// SKU is Windows Mobile. /// WindowsMobile = 104, /// - /// SKU is Windows Embedded Handheld + /// SKU is Windows Embedded Handheld. /// WindowsEmbeddedHandheld = 118, /// - /// SKU is Windows IoT (Internet of Things) Core + /// SKU is Windows IoT (Internet of Things) Core. /// WindowsIotCore = 123 } /// - /// Type of operating system + /// Type of operating system. /// public enum OSType { /// - /// OS is unknown + /// OS is unknown. /// Unknown = 0, /// - /// OS is one other than covered by this Enum + /// OS is one other than covered by this Enum. /// Other = 1, /// - /// OS is MacOS + /// OS is MacOS. /// MACROS = 2, /// - /// OS is AT&T UNIX + /// OS is AT&T UNIX. /// ATTUNIX = 3, /// - /// OS is DG/UX + /// OS is DG/UX. /// DGUX = 4, /// - /// OS is DECNT + /// OS is DECNT. /// DECNT = 5, /// - /// OS is Digital UNIX + /// OS is Digital UNIX. /// DigitalUNIX = 6, /// - /// OS is OpenVMS + /// OS is OpenVMS. /// OpenVMS = 7, /// - /// OS is HP-UX + /// OS is HP-UX. /// HPUX = 8, /// - /// OS is AIX + /// OS is AIX. /// AIX = 9, /// - /// OS is MVS + /// OS is MVS. /// MVS = 10, /// - /// OS is OS/400 + /// OS is OS/400. /// OS400 = 11, /// - /// OS is OS/2 + /// OS is OS/2. /// OS2 = 12, /// - /// OS is Java Virtual Machine + /// OS is Java Virtual Machine. /// JavaVM = 13, /// - /// OS is MS-DOS + /// OS is MS-DOS. /// MSDOS = 14, /// - /// OS is Windows 3x + /// OS is Windows 3x. /// WIN3x = 15, /// - /// OS is Windows 95 + /// OS is Windows 95. /// WIN95 = 16, /// - /// OS is Windows 98 + /// OS is Windows 98. /// WIN98 = 17, /// - /// OS is Windows NT + /// OS is Windows NT. /// WINNT = 18, /// - /// OS is Windows CE + /// OS is Windows CE. /// WINCE = 19, /// - /// OS is NCR System 3000 + /// OS is NCR System 3000. /// NCR3000 = 20, /// - /// OS is NetWare + /// OS is NetWare. /// NetWare = 21, /// - /// OS is OSF + /// OS is OSF. /// OSF = 22, /// - /// OS is DC/OS + /// OS is DC/OS. /// DC_OS = 23, /// - /// OS is Reliant UNIX + /// OS is Reliant UNIX. /// ReliantUNIX = 24, /// - /// OS is SCO UnixWare + /// OS is SCO UnixWare. /// SCOUnixWare = 25, /// - /// OS is SCO OpenServer + /// OS is SCO OpenServer. /// SCOOpenServer = 26, /// - /// OS is Sequent + /// OS is Sequent. /// Sequent = 27, /// - /// OS is IRIX + /// OS is IRIX. /// IRIX = 28, /// - /// OS is Solaris + /// OS is Solaris. /// Solaris = 29, /// - /// OS is SunOS + /// OS is SunOS. /// SunOS = 30, /// - /// OS is U6000 + /// OS is U6000. /// U6000 = 31, /// - /// OS is ASERIES + /// OS is ASERIES. /// ASERIES = 32, /// - /// OS is Tandem NSK + /// OS is Tandem NSK. /// TandemNSK = 33, /// - /// OS is Tandem NT + /// OS is Tandem NT. /// TandemNT = 34, /// - /// OS is BS2000 + /// OS is BS2000. /// BS2000 = 35, /// - /// OS is Linux + /// OS is Linux. /// LINUX = 36, /// - /// OS is Lynx + /// OS is Lynx. /// Lynx = 37, /// - /// OS is XENIX + /// OS is XENIX. /// XENIX = 38, /// - /// OS is VM/ESA + /// OS is VM/ESA. /// VM_ESA = 39, /// - /// OS is Interactive UNIX + /// OS is Interactive UNIX. /// InteractiveUNIX = 40, /// - /// OS is BSD UNIX + /// OS is BSD UNIX. /// BSDUNIX = 41, /// - /// OS is FreeBSD + /// OS is FreeBSD. /// FreeBSD = 42, /// - /// OS is NetBSD + /// OS is NetBSD. /// NetBSD = 43, /// - /// OS is GNU Hurd + /// OS is GNU Hurd. /// GNUHurd = 44, /// - /// OS is OS 9 + /// OS is OS 9. /// OS9 = 45, /// - /// OS is Mach Kernel + /// OS is Mach Kernel. /// MACHKernel = 46, /// - /// OS is Inferno + /// OS is Inferno. /// Inferno = 47, /// - /// OS is QNX + /// OS is QNX. /// QNX = 48, /// - /// OS is EPOC + /// OS is EPOC. /// EPOC = 49, /// - /// OS is IxWorks + /// OS is IxWorks. /// IxWorks = 50, /// - /// OS is VxWorks + /// OS is VxWorks. /// VxWorks = 51, /// - /// OS is MiNT + /// OS is MiNT. /// MiNT = 52, /// - /// OS is BeOS + /// OS is BeOS. /// BeOS = 53, /// - /// OS is HP MPE + /// OS is HP MPE. /// HP_MPE = 54, /// - /// OS is NextStep + /// OS is NextStep. /// NextStep = 55, /// - /// OS is PalmPilot + /// OS is PalmPilot. /// PalmPilot = 56, /// - /// OS is Rhapsody + /// OS is Rhapsody. /// Rhapsody = 57, /// - /// OS is Windows 2000 + /// OS is Windows 2000. /// Windows2000 = 58, /// - /// OS is Dedicated + /// OS is Dedicated. /// Dedicated = 59, /// - /// OS is OS/390 + /// OS is OS/390. /// OS_390 = 60, /// - /// OS is VSE + /// OS is VSE. /// VSE = 61, /// - /// OS is TPF + /// OS is TPF. /// TPF = 62 } /// - /// Specifies the type of the computer in use, such as laptop, desktop, or Tablet + /// Specifies the type of the computer in use, such as laptop, desktop, or Tablet. /// public enum PCSystemType { /// - /// System type is unspecified + /// System type is unspecified. /// Unspecified = 0, /// - /// System is a desktop + /// System is a desktop. /// Desktop = 1, /// - /// System is a mobile device + /// System is a mobile device. /// Mobile = 2, /// - /// System is a workstation + /// System is a workstation. /// Workstation = 3, /// - /// System is an Enterprise Server + /// System is an Enterprise Server. /// EnterpriseServer = 4, /// - /// System is a Small Office and Home Office (SOHO) Server + /// System is a Small Office and Home Office (SOHO) Server. /// SOHOServer = 5, /// - /// System is an appliance PC + /// System is an appliance PC. /// AppliancePC = 6, /// - /// System is a performance server + /// System is a performance server. /// PerformanceServer = 7, /// - /// Maximum enum value + /// Maximum enum value. /// Maximum = 8 } /// /// Specifies the type of the computer in use, such as laptop, desktop, or Tablet. - /// This is an extended version of PCSystemType + /// This is an extended version of PCSystemType. /// - //TODO: conflate these two enums??? + // TODO: conflate these two enums??? public enum PCSystemTypeEx { /// - /// System type is unspecified + /// System type is unspecified. /// Unspecified = 0, /// - /// System is a desktop + /// System is a desktop. /// Desktop = 1, /// - /// System is a mobile device + /// System is a mobile device. /// Mobile = 2, /// - /// System is a workstation + /// System is a workstation. /// Workstation = 3, /// - /// System is an Enterprise Server + /// System is an Enterprise Server. /// EnterpriseServer = 4, /// - /// System is a Small Office and Home Office (SOHO) Server + /// System is a Small Office and Home Office (SOHO) Server. /// SOHOServer = 5, /// - /// System is an appliance PC + /// System is an appliance PC. /// AppliancePC = 6, /// - /// System is a performance server + /// System is a performance server. /// PerformanceServer = 7, /// - /// System is a Slate + /// System is a Slate. /// Slate = 8, /// - /// Maximum enum value + /// Maximum enum value. /// Maximum = 9 } /// - /// Specifies power-related capabilities of a logical device + /// Specifies power-related capabilities of a logical device. /// public enum PowerManagementCapabilities { /// - /// Unknown capability + /// Unknown capability. /// Unknown = 0, /// - /// Power management not supported + /// Power management not supported. /// NotSupported = 1, /// - /// Power management features are currently disabled + /// Power management features are currently disabled. /// Disabled = 2, /// /// The power management features are currently enabled, - /// but the exact feature set is unknown or the information is unavailable + /// but the exact feature set is unknown or the information is unavailable. /// Enabled = 3, /// - /// The device can change its power state based on usage or other criteria + /// The device can change its power state based on usage or other criteria. /// PowerSavingModesEnteredAutomatically = 4, /// - /// The power state may be set through the Win32_LogicalDevice class + /// The power state may be set through the Win32_LogicalDevice class. /// PowerStateSettable = 5, /// - /// Power may be done through the Win32_LogicalDevice class + /// Power may be done through the Win32_LogicalDevice class. /// PowerCyclingSupported = 6, /// - /// Timed power-on is supported + /// Timed power-on is supported. /// TimedPowerOnSupported = 7 } /// - /// Specified power states + /// Specified power states. /// public enum PowerState { /// - /// Power state is unknown + /// Power state is unknown. /// Unknown = 0, /// - /// Full power + /// Full power. /// FullPower = 1, /// - /// Power Save - Low Power mode + /// Power Save - Low Power mode. /// PowerSaveLowPowerMode = 2, /// - /// Power Save - Standby + /// Power Save - Standby. /// PowerSaveStandby = 3, /// - /// Unknown Power Save mode + /// Unknown Power Save mode. /// PowerSaveUnknown = 4, /// - /// Power Cycle + /// Power Cycle. /// PowerCycle = 5, /// - /// Power Off + /// Power Off. /// PowerOff = 6, /// - /// Power Save - Warning + /// Power Save - Warning. /// PowerSaveWarning = 7, /// - /// Power Save - Hibernate + /// Power Save - Hibernate. /// PowerSaveHibernate = 8, /// - /// Power Save - Soft off + /// Power Save - Soft off. /// PowerSaveSoftOff = 9 } /// - /// Specifies the primary function of a processor + /// Specifies the primary function of a processor. /// [SuppressMessage("Microsoft.Design", "CA1008:EnumsShouldHaveZeroValue", Justification = "The underlying MOF definition does not contain a zero value. The converter method will handle it appropriately.")] public enum ProcessorType { /// - /// Processor ype is other than provided in these enumeration values + /// Processor ype is other than provided in these enumeration values. /// Other = 1, /// - /// Processor type is + /// Processor type is. /// Unknown = 2, @@ -4862,7 +4857,7 @@ public enum ProcessorType CentralProcessor = 3, /// - /// Processor is a Math processor + /// Processor is a Math processor. /// MathProcessor = 4, @@ -4872,45 +4867,45 @@ public enum ProcessorType DSPProcessor = 5, /// - /// Processor is a Video processor + /// Processor is a Video processor. /// VideoProcessor = 6 } /// - /// Specifies a computer's reset capability + /// Specifies a computer's reset capability. /// [SuppressMessage("Microsoft.Design", "CA1008:EnumsShouldHaveZeroValue", Justification = "The underlying MOF definition does not contain a zero value. The converter method will handle it appropriately.")] public enum ResetCapability { /// - /// Capability is a value other than provided in these enumerated values + /// Capability is a value other than provided in these enumerated values. /// Other = 1, /// - /// Reset capability is unknown + /// Reset capability is unknown. /// Unknown = 2, /// - /// Capability is disabled + /// Capability is disabled. /// Disabled = 3, /// - /// Capability is enabled + /// Capability is enabled. /// Enabled = 4, /// - /// Capability is not implemented + /// Capability is not implemented. /// NotImplemented = 5 } /// - /// Specifies the kind of event that causes a computer to power up + /// Specifies the kind of event that causes a computer to power up. /// [SuppressMessage("Microsoft.Design", "CA1008:EnumsShouldHaveZeroValue", Justification = "The underlying MOF definition does not contain a zero value. The converter method will handle it appropriately.")] public enum WakeUpType @@ -4918,61 +4913,61 @@ public enum WakeUpType // // This value is reserved // - //Reserved = 0, + // Reserved = 0, /// - /// An event other than specified in this enumeration + /// An event other than specified in this enumeration. /// Other = 1, /// - /// Event type is unknown + /// Event type is unknown. /// Unknown = 2, /// - /// Event is APM timer + /// Event is APM timer. /// APMTimer = 3, /// - /// Event is a Modem Ring + /// Event is a Modem Ring. /// ModemRing = 4, /// - /// Event is a LAN Remove + /// Event is a LAN Remove. /// LANRemote = 5, /// - /// Event is a power switch + /// Event is a power switch. /// PowerSwitch = 6, /// - /// Event is a PCI PME# signal + /// Event is a PCI PME# signal. /// PCIPME = 7, /// - /// AC power was restored + /// AC power was restored. /// ACPowerRestored = 8 } /// - /// Indicates the OEM's preferred power management profile + /// Indicates the OEM's preferred power management profile. /// public enum PowerPlatformRole { /// - /// The OEM did not specify a specific role + /// The OEM did not specify a specific role. /// Unspecified = 0, /// - /// The OEM specified a desktop role + /// The OEM specified a desktop role. /// Desktop = 1, @@ -4982,120 +4977,120 @@ public enum PowerPlatformRole Mobile = 2, /// - /// The OEM specified a workstation role + /// The OEM specified a workstation role. /// Workstation = 3, /// - /// The OEM specified an enterprise server role + /// The OEM specified an enterprise server role. /// EnterpriseServer = 4, /// - /// The OEM specified a single office/home office (SOHO) server role + /// The OEM specified a single office/home office (SOHO) server role. /// SOHOServer = 5, /// - /// The OEM specified an appliance PC role + /// The OEM specified an appliance PC role. /// AppliancePC = 6, /// - /// The OEM specified a performance server role + /// The OEM specified a performance server role. /// PerformanceServer = 7, // v1 last supported /// - /// The OEM specified a tablet form factor role + /// The OEM specified a tablet form factor role. /// Slate = 8, // v2 last supported /// - /// Max enum value + /// Max enum value. /// MaximumEnumValue } /// - /// Additional system information, from Win32_OperatingSystem + /// Additional system information, from Win32_OperatingSystem. /// public enum ProductType { /// - /// Product type is unknown + /// Product type is unknown. /// Unknown = 0, // this value is not specified in Win32_OperatingSystem, but may prove useful /// - /// System is a workstation + /// System is a workstation. /// WorkStation = 1, /// - /// System is a domain controller + /// System is a domain controller. /// DomainController = 2, /// - /// System is a server + /// System is a server. /// Server = 3 } /// - /// Specifies the system server level + /// Specifies the system server level. /// public enum ServerLevel { /// - /// An unknown or unrecognized level was detected + /// An unknown or unrecognized level was detected. /// Unknown = 0, /// - /// Nano server + /// Nano server. /// NanoServer, /// - /// Server core + /// Server core. /// ServerCore, /// - /// Server core with management tools + /// Server core with management tools. /// ServerCoreWithManagementTools, /// - /// Full server + /// Full server. /// FullServer } /// - /// State of a software element + /// State of a software element. /// public enum SoftwareElementState { /// - /// Software element is deployable + /// Software element is deployable. /// Deployable = 0, /// - /// Software element is installable + /// Software element is installable. /// Installable = 1, /// - /// Software element is executable + /// Software element is executable. /// Executable = 2, /// - /// Software element is running + /// Software element is running. /// Running = 3 } @@ -5103,12 +5098,11 @@ public enum SoftwareElementState #endregion Output components #region Native - internal static class Native + internal static partial class Native { private static class PInvokeDllNames { public const string GetPhysicallyInstalledSystemMemoryDllName = "api-ms-win-core-sysinfo-l1-2-1.dll"; - public const string LCIDToLocaleNameDllName = "kernelbase.dll"; public const string PowerDeterminePlatformRoleExDllName = "api-ms-win-power-base-l1-1-0.dll"; public const string GetFirmwareTypeDllName = "api-ms-win-core-kernel32-legacy-l1-1-1"; } @@ -5117,66 +5111,46 @@ private static class PInvokeDllNames public const uint POWER_PLATFORM_ROLE_V1 = 0x1; public const uint POWER_PLATFORM_ROLE_V2 = 0x2; - public const UInt32 S_OK = 0; - + public const uint S_OK = 0; /// - /// Import WINAPI function PowerDeterminePlatformRoleEx + /// Import WINAPI function PowerDeterminePlatformRoleEx. /// - /// The version of the POWER_PLATFORM_ROLE enumeration for the platform - /// POWER_PLATFORM_ROLE enumeration - [DllImport(PInvokeDllNames.PowerDeterminePlatformRoleExDllName, EntryPoint = "PowerDeterminePlatformRoleEx", CharSet = CharSet.Ansi)] - public static extern uint PowerDeterminePlatformRoleEx(uint version); + /// The version of the POWER_PLATFORM_ROLE enumeration for the platform. + /// POWER_PLATFORM_ROLE enumeration. + [LibraryImport(PInvokeDllNames.PowerDeterminePlatformRoleExDllName, EntryPoint = "PowerDeterminePlatformRoleEx")] + public static partial uint PowerDeterminePlatformRoleEx(uint version); /// - /// Retrieve the amount of RAM physically installed in the computer + /// Retrieve the amount of RAM physically installed in the computer. /// /// /// - [DllImport(PInvokeDllNames.GetPhysicallyInstalledSystemMemoryDllName, SetLastError = true)] + [LibraryImport(PInvokeDllNames.GetPhysicallyInstalledSystemMemoryDllName)] [return: MarshalAs(UnmanagedType.Bool)] - public static extern bool GetPhysicallyInstalledSystemMemory(out ulong MemoryInKilobytes); + public static partial bool GetPhysicallyInstalledSystemMemory(out ulong MemoryInKilobytes); /// - /// Retrieve the firmware type of the local computer + /// Retrieve the firmware type of the local computer. /// /// /// A reference to a enumeration to contain /// the resultant firmware type /// /// - [DllImport(PInvokeDllNames.GetFirmwareTypeDllName, SetLastError = true)] + [LibraryImport(PInvokeDllNames.GetFirmwareTypeDllName)] [return: MarshalAs(UnmanagedType.Bool)] - public static extern bool GetFirmwareType(out FirmwareType firmwareType); - - /// - /// Convert a Local Identifier to a Locale name - /// - /// The Locale ID (LCID) to be converted - /// Destination of the Locale name - /// Capacity of - /// - /// - [DllImport(PInvokeDllNames.LCIDToLocaleNameDllName, SetLastError = true, CharSet = CharSet.Unicode)] - public static extern int LCIDToLocaleName(uint localeID, System.Text.StringBuilder localeName, int localeNameSize, int flags); + public static partial bool GetFirmwareType(out FirmwareType firmwareType); /// /// Gets the data specified for the passed in property name from the - /// Software Licensing API + /// Software Licensing API. /// /// Name of the licensing property to get. /// Out parameter for the value. /// An hresult indicating success or failure. - [DllImport("slc.dll", CharSet = CharSet.Unicode)] - internal static extern int SLGetWindowsInformationDWORD(string licenseProperty, out int propertyValue); - /* - * SLGetWindowsInformationDWORD function returns - * S_OK (0x00000000): If the method succeeds - * SL_E_RIGHT_NOT_GRANTED (0xC004F013): The caller does not have the permissions necessary to call this function. - * SL_E_DATATYPE_MISMATCHED (0xC004F013): The value portion of the name-value pair is not a DWORD. - [DllImport("Slc.dll", EntryPoint = "SLGetWindowsInformationDWORD", CharSet = CharSet.Unicode)] - public static extern UInt32 SLGetWindowsInformationDWORD(string pwszValueName, ref int pdwValue); - */ + [LibraryImport("slc.dll", StringMarshalling = StringMarshalling.Utf16)] + internal static partial int SLGetWindowsInformationDWORD(string licenseProperty, out int propertyValue); } #endregion Native } diff --git a/src/Microsoft.PowerShell.Commands.Management/commands/management/GetContentCommand.cs b/src/Microsoft.PowerShell.Commands.Management/commands/management/GetContentCommand.cs index f4b5ce1b39a..a9d8772a5fc 100644 --- a/src/Microsoft.PowerShell.Commands.Management/commands/management/GetContentCommand.cs +++ b/src/Microsoft.PowerShell.Commands.Management/commands/management/GetContentCommand.cs @@ -1,6 +1,5 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. using System; using System.Collections; @@ -9,14 +8,15 @@ using System.Management.Automation; using System.Management.Automation.Internal; using System.Management.Automation.Provider; + using Dbg = System.Management.Automation; namespace Microsoft.PowerShell.Commands { /// - /// A command to get the content of an item at a specified path + /// A command to get the content of an item at a specified path. /// - [Cmdlet(VerbsCommon.Get, "Content", DefaultParameterSetName = "Path", SupportsTransactions = true, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113310")] + [Cmdlet(VerbsCommon.Get, "Content", DefaultParameterSetName = "Path", SupportsTransactions = true, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2096490")] public class GetContentCommand : ContentCommandBase { #region Parameters @@ -27,97 +27,62 @@ public class GetContentCommand : ContentCommandBase /// at a time. To read all blocks at once, set this value /// to a negative number. /// - /// [Parameter(ValueFromPipelineByPropertyName = true)] public long ReadCount { get; set; } = 1; /// - /// The number of content items to retrieve. By default this - /// value is -1 which means read all the content + /// The number of content items to retrieve. /// - /// [Parameter(ValueFromPipelineByPropertyName = true)] + [ValidateRange(0, long.MaxValue)] [Alias("First", "Head")] - public long TotalCount - { - get - { - return _totalCount; - } // get - - set - { - _totalCount = value; - _totalCountSpecified = true; - } - } // TotalCount - private bool _totalCountSpecified = false; + public long TotalCount { get; set; } = -1; /// /// The number of content items to retrieve from the back of the file. /// [Parameter(ValueFromPipelineByPropertyName = true)] + [ValidateRange(0, int.MaxValue)] [Alias("Last")] - public int Tail - { - set - { - _backCount = value; - _tailSpecified = true; - } - get { return _backCount; } - } - private int _backCount = -1; - private bool _tailSpecified = false; + public int Tail { get; set; } = -1; /// /// A virtual method for retrieving the dynamic parameters for a cmdlet. Derived cmdlets /// that require dynamic parameters should override this method and return the /// dynamic parameter object. /// - /// /// /// The context under which the command is running. /// - /// /// /// An object representing the dynamic parameters for the cmdlet or null if there /// are none. /// - /// internal override object GetDynamicParameters(CmdletProviderContext context) { if (Path != null && Path.Length > 0) { return InvokeProvider.Content.GetContentReaderDynamicParameters(Path[0], context); } + return InvokeProvider.Content.GetContentReaderDynamicParameters(".", context); - } // GetDynamicParameters + } #endregion Parameters - #region parameter data - - /// - /// The number of content items to retrieve. - /// - private long _totalCount = -1; - - #endregion parameter data - #region Command code /// - /// Gets the content of an item at the specified path + /// Gets the content of an item at the specified path. /// protected override void ProcessRecord() { // TotalCount and Tail should not be specified at the same time. // Throw out terminating error if this is the case. - if (_totalCountSpecified && _tailSpecified) + if (TotalCount != -1 && Tail != -1) { string errMsg = StringUtil.Format(SessionStateStrings.GetContent_TailAndHeadCannotCoexist, "TotalCount", "Tail"); - ErrorRecord error = new ErrorRecord(new InvalidOperationException(errMsg), "TailAndHeadCannotCoexist", ErrorCategory.InvalidOperation, null); + ErrorRecord error = new(new InvalidOperationException(errMsg), "TailAndHeadCannotCoexist", ErrorCategory.InvalidOperation, null); WriteError(error); return; } @@ -139,19 +104,17 @@ protected override void ProcessRecord() { long countRead = 0; - Dbg.Diagnostics.Assert( - holder.Reader != null, - "All holders should have a reader assigned"); + Dbg.Diagnostics.Assert(holder.Reader != null, "All holders should have a reader assigned"); - if (_tailSpecified && !(holder.Reader is FileSystemContentReaderWriter)) + if (Tail != -1 && holder.Reader is not FileSystemContentReaderWriter) { string errMsg = SessionStateStrings.GetContent_TailNotSupported; - ErrorRecord error = new ErrorRecord(new InvalidOperationException(errMsg), "TailNotSupported", ErrorCategory.InvalidOperation, Tail); + ErrorRecord error = new(new InvalidOperationException(errMsg), "TailNotSupported", ErrorCategory.InvalidOperation, Tail); WriteError(error); continue; } - // If Tail is negative, we are supposed to read all content out. This is same + // If Tail is -1, we are supposed to read all content out. This is same // as reading forwards. So we read forwards in this case. // If Tail is positive, we seek the right position. Or, if the seek failed // because of an unsupported encoding, we scan forward to get the tail content. @@ -166,7 +129,7 @@ protected override void ProcessRecord() catch (Exception e) { ProviderInvocationException providerException = - new ProviderInvocationException( + new( "ProviderContentReadError", SessionStateStrings.ProviderContentReadError, holder.PathInfo.Provider, @@ -195,94 +158,83 @@ protected override void ProcessRecord() } } - if (TotalCount != 0) + IList results = null; + + do { - IList results = null; + long countToRead = ReadCount; - do + // Make sure we only ask for the amount the user wanted + // I am using TotalCount - countToRead so that I don't + // have to worry about overflow + if (TotalCount > 0 && (countToRead == 0 || TotalCount - countToRead < countRead)) { - long countToRead = ReadCount; + countToRead = TotalCount - countRead; + } - // Make sure we only ask for the amount the user wanted - // I am using TotalCount - countToRead so that I don't - // have to worry about overflow + try + { + results = holder.Reader.Read(countToRead); + } + catch (Exception e) // Catch-all OK. 3rd party callout + { + ProviderInvocationException providerException = + new( + "ProviderContentReadError", + SessionStateStrings.ProviderContentReadError, + holder.PathInfo.Provider, + holder.PathInfo.Path, + e); - if ((TotalCount > 0) && (TotalCount - countToRead < countRead)) - { - countToRead = TotalCount - countRead; - } + // Log a provider health event + MshLog.LogProviderHealthEvent(this.Context, holder.PathInfo.Provider.Name, providerException, Severity.Warning); + WriteError(new ErrorRecord(providerException.ErrorRecord, providerException)); - try - { - results = holder.Reader.Read(countToRead); - } - catch (Exception e) // Catch-all OK. 3rd party callout + break; + } + + if (results != null && results.Count > 0) + { + countRead += results.Count; + if (ReadCount == 1) { - ProviderInvocationException providerException = - new ProviderInvocationException( - "ProviderContentReadError", - SessionStateStrings.ProviderContentReadError, - holder.PathInfo.Provider, - holder.PathInfo.Path, - e); - - // Log a provider health event - MshLog.LogProviderHealthEvent( - this.Context, - holder.PathInfo.Provider.Name, - providerException, - Severity.Warning); - - WriteError(new ErrorRecord( - providerException.ErrorRecord, - providerException)); - - break; + // Write out the content as a single object + WriteContentObject(results[0], countRead, holder.PathInfo, currentContext); } - - if (results != null && results.Count > 0) + else { - countRead += results.Count; - if (ReadCount == 1) - { - // Write out the content as a single object - WriteContentObject(results[0], countRead, holder.PathInfo, currentContext); - } - else - { - // Write out the content as an array of objects - WriteContentObject(results, countRead, holder.PathInfo, currentContext); - } + // Write out the content as an array of objects + WriteContentObject(results, countRead, holder.PathInfo, currentContext); } - } while (results != null && results.Count > 0 && ((TotalCount < 0) || countRead < TotalCount)); - } - } // foreach holder in contentStreams + } + } while (results != null && results.Count > 0 && (TotalCount == -1 || countRead < TotalCount)); + } } finally { - // close all the content readers + // Close all the content readers CloseContent(contentStreams, false); // Empty the content holder array contentStreams = new List(); } - } // ProcessRecord + } /// - /// Scan forwards to get the tail content + /// Scan forwards to get the tail content. /// /// /// /// - /// true if no error occured + /// true if no error occurred /// false if there was an error /// - private bool ScanForwardsForTail(ContentHolder holder, CmdletProviderContext currentContext) + private bool ScanForwardsForTail(in ContentHolder holder, CmdletProviderContext currentContext) { var fsReader = holder.Reader as FileSystemContentReaderWriter; Dbg.Diagnostics.Assert(fsReader != null, "Tail is only supported for FileSystemContentReaderWriter"); - var tailResultQueue = new Queue(); + Queue tailResultQueue = new(); IList results = null; ErrorRecord error = null; @@ -295,7 +247,7 @@ private bool ScanForwardsForTail(ContentHolder holder, CmdletProviderContext cur catch (Exception e) { ProviderInvocationException providerException = - new ProviderInvocationException( + new( "ProviderContentReadError", SessionStateStrings.ProviderContentReadError, holder.PathInfo.Provider, @@ -325,7 +277,10 @@ private bool ScanForwardsForTail(ContentHolder holder, CmdletProviderContext cur foreach (object entry in results) { if (tailResultQueue.Count == Tail) + { tailResultQueue.Dequeue(); + } + tailResultQueue.Enqueue(entry); } } @@ -339,39 +294,36 @@ private bool ScanForwardsForTail(ContentHolder holder, CmdletProviderContext cur if (ReadCount <= 0 || (ReadCount >= tailResultQueue.Count && ReadCount != 1)) { count = tailResultQueue.Count; - ArrayList outputList = new ArrayList(); - while (tailResultQueue.Count > 0) - { - outputList.Add(tailResultQueue.Dequeue()); - } + // Write out the content as an array of objects - WriteContentObject(outputList.ToArray(), count, holder.PathInfo, currentContext); + WriteContentObject(tailResultQueue.ToArray(), count, holder.PathInfo, currentContext); } else if (ReadCount == 1) { // Write out the content as single object while (tailResultQueue.Count > 0) + { WriteContentObject(tailResultQueue.Dequeue(), count++, holder.PathInfo, currentContext); + } } else // ReadCount < Queue.Count { while (tailResultQueue.Count >= ReadCount) { - ArrayList outputList = new ArrayList(); + List outputList = new((int)ReadCount); for (int idx = 0; idx < ReadCount; idx++, count++) + { outputList.Add(tailResultQueue.Dequeue()); + } + // Write out the content as an array of objects WriteContentObject(outputList.ToArray(), count, holder.PathInfo, currentContext); } - int remainder = tailResultQueue.Count; - if (remainder > 0) + if (tailResultQueue.Count > 0) { - ArrayList outputList = new ArrayList(); - for (; remainder > 0; remainder--, count++) - outputList.Add(tailResultQueue.Dequeue()); // Write out the content as an array of objects - WriteContentObject(outputList.ToArray(), count, holder.PathInfo, currentContext); + WriteContentObject(tailResultQueue.ToArray(), count, holder.PathInfo, currentContext); } } } @@ -386,7 +338,7 @@ private bool ScanForwardsForTail(ContentHolder holder, CmdletProviderContext cur } /// - /// Seek position to the right place + /// Seek position to the right place. /// /// /// reader should be able to be casted to FileSystemContentReader @@ -414,7 +366,7 @@ private bool SeekPositionForTail(IContentReader reader) } /// - /// Be sure to clean up + /// Be sure to clean up. /// protected override void EndProcessing() { @@ -422,6 +374,5 @@ protected override void EndProcessing() } #endregion Command code - } // GetContentCommand -} // namespace Microsoft.PowerShell.Commands - + } +} diff --git a/src/Microsoft.PowerShell.Commands.Management/commands/management/GetPropertyCommand.cs b/src/Microsoft.PowerShell.Commands.Management/commands/management/GetPropertyCommand.cs index ea812badce1..7523a497390 100644 --- a/src/Microsoft.PowerShell.Commands.Management/commands/management/GetPropertyCommand.cs +++ b/src/Microsoft.PowerShell.Commands.Management/commands/management/GetPropertyCommand.cs @@ -1,24 +1,22 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. using System.Collections.ObjectModel; using System.Diagnostics.CodeAnalysis; using System.Management.Automation; -using Dbg = System.Management.Automation; namespace Microsoft.PowerShell.Commands { /// - /// A command to get the property of an item at a specified path + /// A command to get the property of an item at a specified path. /// - [Cmdlet(VerbsCommon.Get, "ItemProperty", DefaultParameterSetName = "Path", SupportsTransactions = true, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113320")] + [Cmdlet(VerbsCommon.Get, "ItemProperty", DefaultParameterSetName = "Path", SupportsTransactions = true, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2096493")] public class GetItemPropertyCommand : ItemPropertyCommandBase { #region Parameters /// - /// Gets or sets the path parameter to the command + /// Gets or sets the path parameter to the command. /// [Parameter(Position = 0, ParameterSetName = "Path", Mandatory = true, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true)] @@ -27,38 +25,37 @@ public string[] Path get { return paths; - } // get + } set { paths = value; - } // set - } // Path + } + } /// - /// Gets or sets the literal path parameter to the command + /// Gets or sets the literal path parameter to the command. /// [Parameter(ParameterSetName = "LiteralPath", Mandatory = true, ValueFromPipeline = false, ValueFromPipelineByPropertyName = true)] - [Alias("PSPath")] + [Alias("PSPath", "LP")] public string[] LiteralPath { get { return paths; - } // get + } set { base.SuppressWildcardExpansion = true; paths = value; - } // set - } // LiteralPath + } + } /// - /// The properties to retrieve from the item + /// The properties to retrieve from the item. /// - /// [Parameter(Position = 1)] [Alias("PSProperty")] public string[] Name @@ -66,29 +63,26 @@ public string[] Name get { return _property; - } // get + } set { _property = value; } - } // Property + } /// /// A virtual method for retrieving the dynamic parameters for a cmdlet. Derived cmdlets /// that require dynamic parameters should override this method and return the /// dynamic parameter object. /// - /// /// /// The context under which the command is running. /// - /// /// /// An object representing the dynamic parameters for the cmdlet or null if there /// are none. /// - /// internal override object GetDynamicParameters(CmdletProviderContext context) { if (Path != null && Path.Length > 0) @@ -97,10 +91,11 @@ internal override object GetDynamicParameters(CmdletProviderContext context) Path[0], SessionStateUtilities.ConvertArrayToCollection(_property), context); } + return InvokeProvider.Property.GetPropertyDynamicParameters( ".", SessionStateUtilities.ConvertArrayToCollection(_property), context); - } // GetDynamicParameters + } #endregion Parameters @@ -116,7 +111,7 @@ internal override object GetDynamicParameters(CmdletProviderContext context) #region Command code /// - /// Gets the properties of an item at the specified path + /// Gets the properties of an item at the specified path. /// protected override void ProcessRecord() { @@ -162,21 +157,21 @@ protected override void ProcessRecord() continue; } } - } // ProcessRecord + } #endregion Command code - } // GetItemPropertyCommand + } /// /// A command to get the property value of an item at a specified path. /// - [Cmdlet(VerbsCommon.Get, "ItemPropertyValue", DefaultParameterSetName = "Path", SupportsTransactions = true, HelpUri = "https://go.microsoft.com/fwlink/?LinkId=389281")] + [Cmdlet(VerbsCommon.Get, "ItemPropertyValue", DefaultParameterSetName = "Path", SupportsTransactions = true, HelpUri = "https://go.microsoft.com/fwlink/?LinkId=2096906")] public sealed class GetItemPropertyValueCommand : ItemPropertyCommandBase { #region Parameters /// - /// Gets or sets the path parameter to the command + /// Gets or sets the path parameter to the command. /// [Parameter(Position = 0, ParameterSetName = "Path", Mandatory = false, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true)] [ValidateNotNullOrEmpty] @@ -186,38 +181,37 @@ public string[] Path get { return paths; - } // get + } set { paths = value; - } // set - } // Path + } + } /// - /// Gets or sets the literal path parameter to the command + /// Gets or sets the literal path parameter to the command. /// [Parameter(ParameterSetName = "LiteralPath", Mandatory = true, ValueFromPipelineByPropertyName = true)] - [Alias("PSPath")] + [Alias("PSPath", "LP")] [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] public string[] LiteralPath { get { return paths; - } // get + } set { base.SuppressWildcardExpansion = true; paths = value; - } // set - } // LiteralPath + } + } /// - /// The properties to retrieve from the item + /// The properties to retrieve from the item. /// - /// [Parameter(Position = 1, Mandatory = true)] [Alias("PSProperty")] [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] @@ -226,29 +220,26 @@ public string[] Name get { return _property; - } // get + } set { _property = value; } - } // Property + } /// /// A virtual method for retrieving the dynamic parameters for a cmdlet. Derived cmdlets /// that require dynamic parameters should override this method and return the /// dynamic parameter object. /// - /// /// /// The context under which the command is running. /// - /// /// /// An object representing the dynamic parameters for the cmdlet or null if there /// are none. /// - /// internal override object GetDynamicParameters(CmdletProviderContext context) { if (Path != null && Path.Length > 0) @@ -257,10 +248,11 @@ internal override object GetDynamicParameters(CmdletProviderContext context) Path[0], SessionStateUtilities.ConvertArrayToCollection(_property), context); } + return InvokeProvider.Property.GetPropertyDynamicParameters( ".", SessionStateUtilities.ConvertArrayToCollection(_property), context); - } // GetDynamicParameters + } #endregion Parameters @@ -284,6 +276,7 @@ protected override void ProcessRecord() { paths = new string[] { "." }; } + foreach (string path in Path) { try @@ -349,4 +342,4 @@ protected override void ProcessRecord() #endregion Command code } -} // namespace Microsoft.PowerShell.Commands +} diff --git a/src/Microsoft.PowerShell.Commands.Management/commands/management/GetTransactionCommand.cs b/src/Microsoft.PowerShell.Commands.Management/commands/management/GetTransactionCommand.cs index 81d9c1f9010..6b6551619f1 100644 --- a/src/Microsoft.PowerShell.Commands.Management/commands/management/GetTransactionCommand.cs +++ b/src/Microsoft.PowerShell.Commands.Management/commands/management/GetTransactionCommand.cs @@ -1,8 +1,8 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. using System.Management.Automation; + using Dbg = System.Management.Automation; namespace Microsoft.PowerShell.Commands @@ -21,6 +21,5 @@ protected override void EndProcessing() { WriteObject(this.Context.TransactionManager.GetCurrent()); } - } // GetTransactionCommand -} // namespace Microsoft.PowerShell.Commands - + } +} diff --git a/src/Microsoft.PowerShell.Commands.Management/commands/management/GetWMIObjectCommand.cs b/src/Microsoft.PowerShell.Commands.Management/commands/management/GetWMIObjectCommand.cs index 4d2ee3550be..27f812c7a80 100644 --- a/src/Microsoft.PowerShell.Commands.Management/commands/management/GetWMIObjectCommand.cs +++ b/src/Microsoft.PowerShell.Commands.Management/commands/management/GetWMIObjectCommand.cs @@ -1,19 +1,18 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. using System; +using System.Collections; using System.Globalization; -using System.Management.Automation; using System.Management; +using System.Management.Automation; using System.Text; -using System.Collections; using System.Threading; namespace Microsoft.PowerShell.Commands { /// - /// A command to get WMI Objects + /// A command to get WMI Objects. /// [Cmdlet(VerbsCommon.Get, "WmiObject", DefaultParameterSetName = "query", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113337", RemotingCapability = RemotingCapability.OwnedByCommand)] @@ -21,41 +20,41 @@ public class GetWmiObjectCommand : WmiBaseCmdlet { #region Parameters - /// - /// The WMI class to query + /// The WMI class to query. /// [Alias("ClassName")] [Parameter(Position = 0, Mandatory = true, ParameterSetName = "query")] [Parameter(Position = 1, ParameterSetName = "list")] - [ValidateNotNullOrEmpty()] + [ValidateNotNullOrEmpty] public string Class { get; set; } /// - /// To specify whether to get the results recursively + /// To specify whether to get the results recursively. /// [Parameter(ParameterSetName = "list")] public SwitchParameter Recurse { get; set; } = false; /// - /// The WMI properties to retrieve + /// The WMI properties to retrieve. /// [Parameter(Position = 1, ParameterSetName = "query")] - [ValidateNotNullOrEmpty()] + [ValidateNotNullOrEmpty] public string[] Property { get { return (string[])_property.Clone(); } + set { _property = value; } } /// - /// The filter to be used in the search + /// The filter to be used in the search. /// [Parameter(ParameterSetName = "query")] public string Filter { get; set; } /// - /// If Amended qualifier to use + /// If Amended qualifier to use. /// [Parameter] public SwitchParameter Amended { get; set; } @@ -68,13 +67,13 @@ public string[] Property public SwitchParameter DirectRead { get; set; } /// - /// The list of classes + /// The list of classes. /// [Parameter(ParameterSetName = "list")] public SwitchParameter List { get; set; } = false; /// - /// The query string to search for objects + /// The query string to search for objects. /// [Parameter(Mandatory = true, ParameterSetName = "WQLQuery")] public string Query { get; set; } @@ -90,19 +89,20 @@ public string[] Property #region Command code /// - /// Uses this.filter, this.wmiClass and this.property to retrieve the filter + /// Uses this.filter, this.wmiClass and this.property to retrieve the filter. /// internal string GetQueryString() { StringBuilder returnValue = new StringBuilder("select "); - returnValue.Append(String.Join(", ", _property)); + returnValue.Append(string.Join(", ", _property)); returnValue.Append(" from "); returnValue.Append(Class); - if (!String.IsNullOrEmpty(Filter)) + if (!string.IsNullOrEmpty(Filter)) { returnValue.Append(" where "); returnValue.Append(Filter); } + return returnValue.ToString(); } /// @@ -126,6 +126,7 @@ internal string GetFilterClassName() filterClass = filterClass.Replace('?', '_'); return filterClass; } + internal bool IsLocalizedNamespace(string sNamespace) { bool toReturn = false; @@ -133,8 +134,10 @@ internal bool IsLocalizedNamespace(string sNamespace) { toReturn = true; } + return toReturn; } + internal bool ValidateClassFormat() { string filterClass = this.Class; @@ -143,7 +146,7 @@ internal bool ValidateClassFormat() StringBuilder newClassName = new StringBuilder(); for (int i = 0; i < filterClass.Length; i++) { - if (Char.IsLetterOrDigit(filterClass[i]) || + if (char.IsLetterOrDigit(filterClass[i]) || filterClass[i].Equals('[') || filterClass[i].Equals(']') || filterClass[i].Equals('*') || filterClass[i].Equals('?') || filterClass[i].Equals('-')) @@ -158,14 +161,16 @@ internal bool ValidateClassFormat() newClassName.Append(']'); continue; } + return false; } + this.Class = newClassName.ToString(); return true; } /// - /// Gets the ManagementObjectSearcher object + /// Gets the ManagementObjectSearcher object. /// internal ManagementObjectSearcher GetObjectList(ManagementScope scope) { @@ -183,6 +188,7 @@ internal ManagementObjectSearcher GetObjectList(ManagementScope scope) queryStringBuilder.Append(filterClass); queryStringBuilder.Append("'"); } + ObjectQuery classQuery = new ObjectQuery(queryStringBuilder.ToString()); EnumerationOptions enumOptions = new EnumerationOptions(); @@ -192,7 +198,7 @@ internal ManagementObjectSearcher GetObjectList(ManagementScope scope) return searcher; } /// - /// Gets the properties of an item at the specified path + /// Gets the properties of an item at the specified path. /// protected override void BeginProcessing() { @@ -210,7 +216,7 @@ protected override void BeginProcessing() { ErrorRecord errorRecord = new ErrorRecord( new ArgumentException( - String.Format( + string.Format( Thread.CurrentThread.CurrentCulture, "Class", this.Class)), "INVALID_QUERY_IDENTIFIER", @@ -221,6 +227,7 @@ protected override void BeginProcessing() WriteError(errorRecord); return; } + foreach (string name in ComputerName) { if (this.Recurse.IsPresent) @@ -327,6 +334,7 @@ protected override void BeginProcessing() WriteError(errorRecord); continue; } + ManagementObjectSearcher searcher = this.GetObjectList(scope); if (searcher == null) continue; @@ -336,6 +344,7 @@ protected override void BeginProcessing() } } } + return; } @@ -371,19 +380,19 @@ protected override void BeginProcessing() if (e.ErrorCode.Equals(ManagementStatus.InvalidClass)) { string className = GetClassNameFromQuery(queryString); - string errorMsg = String.Format(CultureInfo.InvariantCulture, WmiResources.WmiQueryFailure, + string errorMsg = string.Format(CultureInfo.InvariantCulture, WmiResources.WmiQueryFailure, e.Message, className); errorRecord = new ErrorRecord(new ManagementException(errorMsg), "GetWMIManagementException", ErrorCategory.InvalidType, null); } else if (e.ErrorCode.Equals(ManagementStatus.InvalidQuery)) { - string errorMsg = String.Format(CultureInfo.InvariantCulture, WmiResources.WmiQueryFailure, + string errorMsg = string.Format(CultureInfo.InvariantCulture, WmiResources.WmiQueryFailure, e.Message, queryString); errorRecord = new ErrorRecord(new ManagementException(errorMsg), "GetWMIManagementException", ErrorCategory.InvalidArgument, null); } else if (e.ErrorCode.Equals(ManagementStatus.InvalidNamespace)) { - string errorMsg = String.Format(CultureInfo.InvariantCulture, WmiResources.WmiQueryFailure, + string errorMsg = string.Format(CultureInfo.InvariantCulture, WmiResources.WmiQueryFailure, e.Message, this.Namespace); errorRecord = new ErrorRecord(new ManagementException(errorMsg), "GetWMIManagementException", ErrorCategory.InvalidArgument, null); } @@ -401,12 +410,12 @@ protected override void BeginProcessing() WriteError(errorRecord); continue; } - } // foreach computerName + } } - } // BeginProcessing + } /// - /// Get the class name from a query string + /// Get the class name from a query string. /// /// /// @@ -427,6 +436,5 @@ private string GetClassNameFromQuery(string query) } #endregion Command code - } // GetWmiObjectCommand -} // namespace Microsoft.PowerShell.Commands - + } +} diff --git a/src/Microsoft.PowerShell.Commands.Management/commands/management/Hotfix.cs b/src/Microsoft.PowerShell.Commands.Management/commands/management/Hotfix.cs index 6bdbedbb966..1aa94509469 100644 --- a/src/Microsoft.PowerShell.Commands.Management/commands/management/Hotfix.cs +++ b/src/Microsoft.PowerShell.Commands.Management/commands/management/Hotfix.cs @@ -1,41 +1,25 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#if !UNIX using System; -using System.Text; -using System.Text.RegularExpressions; -using System.Collections; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Collections.Specialized; -using System.Diagnostics; // Process class -using System.ComponentModel; // Win32Exception -using System.Globalization; -using System.Runtime.Serialization; -using System.Security.Permissions; -using System.Threading; +using System.Diagnostics.CodeAnalysis; using System.Management; using System.Management.Automation; using System.Management.Automation.Internal; -using System.Diagnostics.CodeAnalysis; -using System.Net; -using System.IO; -using System.Security; using System.Security.Principal; -using System.Security.AccessControl; -using Dbg = System.Management.Automation; - +using System.Text; namespace Microsoft.PowerShell.Commands { #region Get-HotFix /// - /// Cmdlet for Get-Hotfix Proxy + /// Cmdlet for Get-Hotfix Proxy. /// [Cmdlet(VerbsCommon.Get, "HotFix", DefaultParameterSetName = "Default", - HelpUri = "https://go.microsoft.com/fwlink/?LinkID=135217", RemotingCapability = RemotingCapability.SupportedByCommand)] + HelpUri = "https://go.microsoft.com/fwlink/?linkid=2109716", RemotingCapability = RemotingCapability.SupportedByCommand)] [OutputType(@"System.Management.ManagementObject#root\cimv2\Win32_QuickFixEngineering")] public sealed class GetHotFixCommand : PSCmdlet, IDisposable { @@ -51,7 +35,7 @@ public sealed class GetHotFixCommand : PSCmdlet, IDisposable public string[] Id { get; set; } /// - /// To search on description of Hotfixes + /// To search on description of Hotfixes. /// [Parameter(ParameterSetName = "Description")] [ValidateNotNullOrEmpty] @@ -59,7 +43,7 @@ public sealed class GetHotFixCommand : PSCmdlet, IDisposable public string[] Description { get; set; } /// - /// Parameter to pass the Computer Name + /// Parameter to pass the Computer Name. /// [Parameter(ValueFromPipelineByPropertyName = true)] [ValidateNotNullOrEmpty] @@ -79,47 +63,64 @@ public sealed class GetHotFixCommand : PSCmdlet, IDisposable #region Overrides - private ManagementObjectSearcher _searchProcess; private bool _inputContainsWildcard = false; + private readonly ConnectionOptions _connectionOptions = new(); + /// - /// Get the List of HotFixes installed on the Local Machine. + /// Sets connection options. /// protected override void BeginProcessing() + { + _connectionOptions.Authentication = AuthenticationLevel.Packet; + _connectionOptions.Impersonation = ImpersonationLevel.Impersonate; + _connectionOptions.Username = Credential?.UserName; + _connectionOptions.SecurePassword = Credential?.Password; + } + + /// + /// Get the List of HotFixes installed on the Local Machine. + /// + protected override void ProcessRecord() { foreach (string computer in ComputerName) { bool foundRecord = false; - StringBuilder QueryString = new StringBuilder(); - ConnectionOptions conOptions = ComputerWMIHelper.GetConnectionOptions(AuthenticationLevel.Packet, ImpersonationLevel.Impersonate, this.Credential); - ManagementScope scope = new ManagementScope(ComputerWMIHelper.GetScopeString(computer, ComputerWMIHelper.WMI_Path_CIM), conOptions); + StringBuilder queryString = new(); + ManagementScope scope = new(ComputerWMIHelper.GetScopeString(computer, ComputerWMIHelper.WMI_Path_CIM), _connectionOptions); scope.Connect(); if (Id != null) { - QueryString.Append("Select * from Win32_QuickFixEngineering where ("); + queryString.Append("Select * from Win32_QuickFixEngineering where ("); for (int i = 0; i <= Id.Length - 1; i++) { - QueryString.Append("HotFixID= '"); - QueryString.Append(Id[i].ToString().Replace("'", "\\'")); - QueryString.Append("'"); + queryString.Append("HotFixID= '"); + queryString.Append(Id[i].Replace("'", "\\'")); + queryString.Append('\''); if (i < Id.Length - 1) - QueryString.Append(" Or "); + { + queryString.Append(" Or "); + } } - QueryString.Append(")"); + + queryString.Append(')'); } else { - QueryString.Append("Select * from Win32_QuickFixEngineering"); + queryString.Append("Select * from Win32_QuickFixEngineering"); foundRecord = true; } - _searchProcess = new ManagementObjectSearcher(scope, new ObjectQuery(QueryString.ToString())); + + _searchProcess = new ManagementObjectSearcher(scope, new ObjectQuery(queryString.ToString())); foreach (ManagementObject obj in _searchProcess.Get()) { if (Description != null) { if (!FilterMatch(obj)) + { continue; + } } else { @@ -129,54 +130,46 @@ protected override void BeginProcessing() // try to translate the SID to a more friendly username // just stick with the SID if anything goes wrong string installed = (string)obj["InstalledBy"]; - if (!String.IsNullOrEmpty(installed)) + if (!string.IsNullOrEmpty(installed)) { try { - SecurityIdentifier secObj = new SecurityIdentifier(installed); - obj["InstalledBy"] = secObj.Translate(typeof(NTAccount)); ; + SecurityIdentifier secObj = new(installed); + obj["InstalledBy"] = secObj.Translate(typeof(NTAccount)); } - catch (IdentityNotMappedException) // thrown by SecurityIdentifier.Translate + catch (IdentityNotMappedException) { + // thrown by SecurityIdentifier.Translate } - catch (SystemException) // thrown by SecurityIdentifier.constr + catch (SystemException) { + // thrown by SecurityIdentifier.constr } - //catch (ArgumentException) // thrown (indirectly) by SecurityIdentifier.constr (on XP only?) - //{ catch not needed - this is already caught as SystemException - //} - //catch (PlatformNotSupportedException) // thrown (indirectly) by SecurityIdentifier.Translate (on Win95 only?) - //{ catch not needed - this is already caught as SystemException - //} - //catch (UnauthorizedAccessException) // thrown (indirectly) by SecurityIdentifier.Translate - //{ catch not needed - this is already caught as SystemException - //} } WriteObject(obj); foundRecord = true; } + if (!foundRecord && !_inputContainsWildcard) { - Exception Ex = new ArgumentException(StringUtil.Format(HotFixResources.NoEntriesFound, computer)); - WriteError(new ErrorRecord(Ex, "GetHotFixNoEntriesFound", ErrorCategory.ObjectNotFound, null)); + Exception ex = new ArgumentException(StringUtil.Format(HotFixResources.NoEntriesFound, computer)); + WriteError(new ErrorRecord(ex, "GetHotFixNoEntriesFound", ErrorCategory.ObjectNotFound, null)); } + if (_searchProcess != null) { this.Dispose(); } } - }//end of BeginProcessing method + } /// - /// to implement ^C + /// To implement ^C. /// protected override void StopProcessing() { - if (_searchProcess != null) - { - _searchProcess.Dispose(); - } + _searchProcess?.Dispose(); } #endregion Overrides @@ -193,6 +186,7 @@ private bool FilterMatch(ManagementObject obj) { return true; } + if (WildcardPattern.ContainsWildcardCharacters(desc)) { _inputContainsWildcard = true; @@ -203,6 +197,7 @@ private bool FilterMatch(ManagementObject obj) { return false; } + return false; } @@ -211,7 +206,7 @@ private bool FilterMatch(ManagementObject obj) #region "IDisposable Members" /// - /// Dispose Method + /// Dispose Method. /// public void Dispose() { @@ -229,14 +224,13 @@ public void Dispose(bool disposing) { if (disposing) { - if (_searchProcess != null) - { - _searchProcess.Dispose(); - } + _searchProcess?.Dispose(); } } #endregion "IDisposable Members" - }//end class + } #endregion -}//Microsoft.Powershell.commands +} + +#endif diff --git a/src/Microsoft.PowerShell.Commands.Management/commands/management/InvokeWMIMethodCommand.cs b/src/Microsoft.PowerShell.Commands.Management/commands/management/InvokeWMIMethodCommand.cs index 2e37ca088e6..df0c5775ca1 100644 --- a/src/Microsoft.PowerShell.Commands.Management/commands/management/InvokeWMIMethodCommand.cs +++ b/src/Microsoft.PowerShell.Commands.Management/commands/management/InvokeWMIMethodCommand.cs @@ -1,23 +1,22 @@ -// -// Copyright (C) Microsoft. All rights reserved. -// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. using System; +using System.Collections; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.Management; using System.Management.Automation; using System.Management.Automation.Internal; -using System.Management; -using System.Text; using System.Management.Automation.Provider; -using System.ComponentModel; -using System.Collections; -using System.Collections.ObjectModel; -using System.Security.AccessControl; using System.Runtime.InteropServices; +using System.Security.AccessControl; +using System.Text; namespace Microsoft.PowerShell.Commands { /// - /// A command to Invoke WMI Method + /// A command to Invoke WMI Method. /// [Cmdlet(VerbsLifecycle.Invoke, "WmiMethod", DefaultParameterSetName = "class", SupportsShouldProcess = true, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113346", RemotingCapability = RemotingCapability.OwnedByCommand)] @@ -25,45 +24,48 @@ public sealed class InvokeWmiMethod : WmiBaseCmdlet { #region Parameters /// - /// The WMI Object to use + /// The WMI Object to use. /// - /// [Parameter(ValueFromPipeline = true, Mandatory = true, ParameterSetName = "object")] public ManagementObject InputObject { get { return _inputObject; } + set { _inputObject = value; } } /// - /// The WMI Path to use + /// The WMI Path to use. /// [Parameter(ParameterSetName = "path", Mandatory = true)] public string Path { get { return _path; } + set { _path = value; } } /// - /// The WMI class to use + /// The WMI class to use. /// [Parameter(Position = 0, Mandatory = true, ParameterSetName = "class")] public string Class { get { return _className; } + set { _className = value; } } /// - /// The WMI Method to execute + /// The WMI Method to execute. /// [Parameter(Position = 1, Mandatory = true)] public string Name { get { return _methodName; } + set { _methodName = value; } } /// - /// The parameters to the method specified by MethodName + /// The parameters to the method specified by MethodName. /// [Parameter(ParameterSetName = "path")] [Parameter(Position = 2, ParameterSetName = "class")] @@ -72,6 +74,7 @@ public string Name public object[] ArgumentList { get { return _argumentList; } + set { _argumentList = value; } } @@ -96,6 +99,7 @@ protected override void ProcessRecord() RunAsJob("Invoke-WMIMethod"); return; } + if (_inputObject != null) { object result = null; @@ -114,6 +118,7 @@ protected override void ProcessRecord() inParamCount--; } } + if (!ShouldProcess( StringUtil.Format(WmiResources.WmiMethodNameForConfirmation, _inputObject["__CLASS"].ToString(), @@ -122,6 +127,7 @@ protected override void ProcessRecord() { return; } + result = _inputObject.InvokeMethod(_methodName, inputParameters, null); } catch (ManagementException e) @@ -134,10 +140,12 @@ protected override void ProcessRecord() ErrorRecord errorRecord = new ErrorRecord(e, "InvokeWMICOMException", ErrorCategory.InvalidOperation, null); WriteError(errorRecord); } + if (result != null) { WriteObject(result); } + return; } else @@ -149,13 +157,13 @@ protected override void ProcessRecord() if (_path != null) { mPath = new ManagementPath(_path); - if (String.IsNullOrEmpty(mPath.NamespacePath)) + if (string.IsNullOrEmpty(mPath.NamespacePath)) { mPath.NamespacePath = this.Namespace; } else if (namespaceSpecified) { - //ThrowTerminatingError + // ThrowTerminatingError ThrowTerminatingError(new ErrorRecord( new InvalidOperationException(), "NamespaceSpecifiedWithPath", @@ -165,20 +173,21 @@ protected override void ProcessRecord() if (mPath.Server != "." && serverNameSpecified) { - //ThrowTerminatingError + // ThrowTerminatingError ThrowTerminatingError(new ErrorRecord( new InvalidOperationException(), "ComputerNameSpecifiedWithPath", ErrorCategory.InvalidOperation, ComputerName)); } - //If server name is specified loop through it. + // If server name is specified loop through it. if (!(mPath.Server == "." && serverNameSpecified)) { string[] serverName = new string[] { mPath.Server }; ComputerName = serverName; } } + foreach (string name in ComputerName) { result = null; @@ -197,6 +206,7 @@ protected override void ProcessRecord() ManagementObject mInstance = new ManagementObject(mPath); mObject = mInstance; } + ManagementScope mScope = new ManagementScope(mPath, options); mObject.Scope = mScope; } @@ -207,6 +217,7 @@ protected override void ProcessRecord() mObject = mClass; mObject.Scope = scope; } + ManagementBaseObject inputParameters = mObject.GetMethodParameters(_methodName); if (_argumentList != null) { @@ -224,9 +235,11 @@ protected override void ProcessRecord() { property.Value = argument; } + inParamCount--; } } + if (!ShouldProcess( StringUtil.Format(WmiResources.WmiMethodNameForConfirmation, mObject["__CLASS"].ToString(), @@ -235,6 +248,7 @@ protected override void ProcessRecord() { return; } + result = mObject.InvokeMethod(_methodName, inputParameters, null); } catch (ManagementException e) @@ -247,13 +261,14 @@ protected override void ProcessRecord() ErrorRecord errorRecord = new ErrorRecord(e, "InvokeWMICOMException", ErrorCategory.InvalidOperation, null); WriteError(errorRecord); } + if (result != null) { WriteObject(result); } } } - }//ProcessRecord + } /// /// Ensure that the argument is a collection containing no PSObjects. @@ -280,6 +295,7 @@ private static object MakeBaseObjectArray(object argument) break; } } + if (needCopy) { var copiedArgument = new object[listArgument.Count]; @@ -288,6 +304,7 @@ private static object MakeBaseObjectArray(object argument) { copiedArgument[index++] = argElement != null ? PSObject.Base(argElement) : null; } + return copiedArgument; } else @@ -297,5 +314,5 @@ private static object MakeBaseObjectArray(object argument) } #endregion Command code - }//InvokeWMIObject + } } diff --git a/src/Microsoft.PowerShell.Commands.Management/commands/management/JobProcessCollection.cs b/src/Microsoft.PowerShell.Commands.Management/commands/management/JobProcessCollection.cs new file mode 100644 index 00000000000..78560543939 --- /dev/null +++ b/src/Microsoft.PowerShell.Commands.Management/commands/management/JobProcessCollection.cs @@ -0,0 +1,125 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#nullable enable +#if !UNIX +using System; +using System.Diagnostics.CodeAnalysis; +using System.Threading; +using Microsoft.Win32.SafeHandles; + +namespace Microsoft.PowerShell.Commands; + +/// +/// JobProcessCollection is a helper class used by Start-Process -Wait cmdlet to monitor the +/// child processes created by the main process hosted by the Start-process cmdlet. +/// +internal sealed class JobProcessCollection : IDisposable +{ + /// + /// Stores the initialisation state of the job and completion port. + /// + private bool? _initStatus; + + /// + /// JobObjectHandle is a reference to the job object used to track + /// the child processes created by the main process hosted by the Start-Process cmdlet. + /// + private Interop.Windows.SafeJobHandle? _jobObject; + + /// + /// The completion port handle that is used to monitor job events. + /// + private Interop.Windows.SafeIoCompletionPort? _completionPort; + + /// + /// Initializes a new instance of the class. + /// + public JobProcessCollection() + { } + + /// + /// Initializes the job and IO completion port and adds the process to the + /// job object. + /// + /// The process to add to the job. + /// Whether the job creation and assignment worked or not. + public bool AssignProcessToJobObject(SafeProcessHandle process) + => InitializeJob() && Interop.Windows.AssignProcessToJobObject(_jobObject, process); + + /// + /// Blocks the current thread until all processes in the job have exited. + /// + /// A token to cancel the operation. + public void WaitForExit(CancellationToken cancellationToken) + { + if (_completionPort is null) + { + return; + } + + using var cancellationRegistration = cancellationToken.Register(() => + { + Interop.Windows.PostQueuedCompletionStatus( + _completionPort, + Interop.Windows.JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO); + }); + + int completionCode = 0; + do + { + Interop.Windows.GetQueuedCompletionStatus( + _completionPort, + Interop.Windows.INFINITE, + out completionCode); + } + while (completionCode != Interop.Windows.JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO); + cancellationToken.ThrowIfCancellationRequested(); + } + + [MemberNotNullWhen(true, [nameof(_jobObject), nameof(_completionPort)])] + private bool InitializeJob() + { + if (_initStatus.HasValue) + { + return _initStatus.Value; + } + + if (_jobObject is null) + { + _jobObject = Interop.Windows.CreateJobObject(); + if (_jobObject.IsInvalid) + { + _initStatus = false; + _jobObject.Dispose(); + _jobObject = null; + return false; + } + } + + if (_completionPort is null) + { + _completionPort = Interop.Windows.CreateIoCompletionPort(); + if (_completionPort.IsInvalid) + { + _initStatus = false; + _completionPort.Dispose(); + _completionPort = null; + return false; + } + } + + _initStatus = Interop.Windows.SetInformationJobObject( + _jobObject, + _completionPort); + + return _initStatus.Value; + } + + public void Dispose() + { + _jobObject?.Dispose(); + _completionPort?.Dispose(); + } +} +#endif diff --git a/src/Microsoft.PowerShell.Commands.Management/commands/management/MovePropertyCommand.cs b/src/Microsoft.PowerShell.Commands.Management/commands/management/MovePropertyCommand.cs index 9396471b74a..a40cae64b9f 100644 --- a/src/Microsoft.PowerShell.Commands.Management/commands/management/MovePropertyCommand.cs +++ b/src/Microsoft.PowerShell.Commands.Management/commands/management/MovePropertyCommand.cs @@ -1,42 +1,45 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. using System; using System.Management.Automation; -using Dbg = System.Management.Automation; namespace Microsoft.PowerShell.Commands { /// - /// A command to move a property on an item to another item + /// A command to move a property on an item to another item. /// [Cmdlet(VerbsCommon.Move, "ItemProperty", SupportsShouldProcess = true, DefaultParameterSetName = "Path", SupportsTransactions = true, - HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113351")] + HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2096817")] public class MoveItemPropertyCommand : PassThroughItemPropertyCommandBase { #region Parameters /// - /// Gets or sets the path parameter to the command + /// Gets or sets the path parameter to the command. /// [Parameter(Position = 0, ParameterSetName = "Path", Mandatory = true, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true)] public string[] Path { get { return paths; } + set { paths = value; } } /// - /// Gets or sets the literal path parameter to the command + /// Gets or sets the literal path parameter to the command. /// [Parameter(ParameterSetName = "LiteralPath", Mandatory = true, ValueFromPipeline = false, ValueFromPipelineByPropertyName = true)] - [Alias("PSPath")] + [Alias("PSPath", "LP")] public string[] LiteralPath { - get { return paths; } + get + { + return paths; + } + set { base.SuppressWildcardExpansion = true; @@ -45,20 +48,21 @@ public string[] LiteralPath } /// - /// The name of the property to create on the item + /// The name of the property to create on the item. /// - /// [Parameter(Position = 2, Mandatory = true, ValueFromPipelineByPropertyName = true)] [Alias("PSProperty")] public string[] Name { - get { return _property; } + get + { + return _property; + } + set { - if (value == null) - { - value = Utils.EmptyArray(); - } + value ??= Array.Empty(); + _property = value; } } @@ -66,7 +70,6 @@ public string[] Name /// /// The path to the destination item to copy the property to. /// - /// [Parameter(Mandatory = true, Position = 1, ValueFromPipelineByPropertyName = true)] public string Destination { get; set; } @@ -75,19 +78,16 @@ public string[] Name /// that require dynamic parameters should override this method and return the /// dynamic parameter object. /// - /// /// /// The context under which the command is running. /// - /// /// /// An object representing the dynamic parameters for the cmdlet or null if there /// are none. /// - /// internal override object GetDynamicParameters(CmdletProviderContext context) { - string propertyName = String.Empty; + string propertyName = string.Empty; if (Name != null && Name.Length > 0) { propertyName = Name[0]; @@ -97,13 +97,14 @@ internal override object GetDynamicParameters(CmdletProviderContext context) { return InvokeProvider.Property.MovePropertyDynamicParameters(Path[0], propertyName, Destination, propertyName, context); } + return InvokeProvider.Property.MovePropertyDynamicParameters( ".", propertyName, Destination, propertyName, context); - } // GetDynamicParameters + } #endregion Parameters @@ -112,14 +113,14 @@ internal override object GetDynamicParameters(CmdletProviderContext context) /// /// The property to be created. /// - private string[] _property = new string[0]; + private string[] _property = Array.Empty(); #endregion parameter data #region Command code /// - /// Creates the property on the item + /// Creates the property on the item. /// protected override void ProcessRecord() { @@ -165,9 +166,8 @@ protected override void ProcessRecord() } } } - } // ProcessRecord + } #endregion Command code - - } // MoveItemPropertyCommand -} // namespace Microsoft.PowerShell.Commands + } +} diff --git a/src/Microsoft.PowerShell.Commands.Management/commands/management/Navigation.cs b/src/Microsoft.PowerShell.Commands.Management/commands/management/Navigation.cs index f3aac32cce3..f4ba55202ed 100644 --- a/src/Microsoft.PowerShell.Commands.Management/commands/management/Navigation.cs +++ b/src/Microsoft.PowerShell.Commands.Management/commands/management/Navigation.cs @@ -1,6 +1,5 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. using System; using System.Collections.Generic; @@ -8,6 +7,7 @@ using System.Management.Automation; using System.Management.Automation.Internal; using System.Management.Automation.Provider; + using Dbg = System.Management.Automation; namespace Microsoft.PowerShell.Commands @@ -25,9 +25,8 @@ public abstract class CoreCommandBase : PSCmdlet, IDynamicParameters /// An instance of the PSTraceSource class used for trace output /// using "NavigationCommands" as the category. /// - /// - [Dbg.TraceSourceAttribute("NavigationCommands", "The namespace navigation tracer")] - internal static Dbg.PSTraceSource tracer = Dbg.PSTraceSource.GetTracer("NavigationCommands", "The namespace navigation tracer"); + [Dbg.TraceSource("NavigationCommands", "The namespace navigation tracer")] + internal static readonly Dbg.PSTraceSource tracer = Dbg.PSTraceSource.GetTracer("NavigationCommands", "The namespace navigation tracer"); #endregion Tracer @@ -40,7 +39,7 @@ internal virtual CmdletProviderContext CmdletProviderContext { get { - CmdletProviderContext coreCommandContext = new CmdletProviderContext(this); + CmdletProviderContext coreCommandContext = new(this); coreCommandContext.Force = Force; @@ -57,19 +56,14 @@ internal virtual CmdletProviderContext CmdletProviderContext return coreCommandContext; } - } // CmdletProviderContext + } internal virtual SwitchParameter SuppressWildcardExpansion { - get - { - return _suppressWildcardExpansion; - } - set - { - _suppressWildcardExpansion = value; - } + get => _suppressWildcardExpansion; + set => _suppressWildcardExpansion = value; } + private bool _suppressWildcardExpansion; /// @@ -77,45 +71,31 @@ internal virtual SwitchParameter SuppressWildcardExpansion /// that require dynamic parameters should override this method and return the /// dynamic parameter object. /// - /// /// /// The context under which the command is running. /// - /// /// /// An object representing the dynamic parameters for the cmdlet or null if there /// are none. /// - /// - internal virtual object GetDynamicParameters(CmdletProviderContext context) - { - return null; - } + internal virtual object GetDynamicParameters(CmdletProviderContext context) => null; /// /// Called by the base implementation that checks the SupportShouldProcess provider /// capability. This virtual method gives the /// derived cmdlet a chance query the CmdletProvider capabilities to determine - /// if the provider supports ShouldProcess + /// if the provider supports ShouldProcess. /// /// - protected virtual bool ProviderSupportsShouldProcess - { - get - { - return true; - } - } // ProviderSupportsShouldProcess + protected virtual bool ProviderSupportsShouldProcess => true; /// /// A helper for derived classes to call to determine if the paths specified - /// are for a provider that supports ShouldProcess + /// are for a provider that supports ShouldProcess. /// - /// /// /// The paths to check to see if the providers support ShouldProcess. /// - /// /// /// If the paths are to different providers, and any don't support /// ShouldProcess, then the return value is false. If they all @@ -127,7 +107,7 @@ protected bool DoesProviderSupportShouldProcess(string[] paths) // may be getting piped in. bool result = true; - if (paths != null && paths.Length >= 0) + if (paths != null) { foreach (string path in paths) { @@ -152,6 +132,7 @@ protected bool DoesProviderSupportShouldProcess(string[] paths) } } } + return result; } @@ -159,19 +140,11 @@ protected bool DoesProviderSupportShouldProcess(string[] paths) /// The dynamic parameters which have already been retrieved from the provider /// and bound by the command processor. /// - /// - protected internal object RetrievedDynamicParameters - { - get - { - return _dynamicParameters; - } // get - } // RetrievedDynamicParameters + protected internal object RetrievedDynamicParameters => _dynamicParameters; /// /// The dynamic parameters for the command. They are retrieved using the /// GetDynamicParameters virtual method. /// - /// private object _dynamicParameters; #endregion Protected members @@ -183,72 +156,58 @@ protected internal object RetrievedDynamicParameters /// CmdletProviderContext to tunnel the stop message to /// the provider instance. /// - /// protected override void StopProcessing() { foreach (CmdletProviderContext stopContext in stopContextCollection) { stopContext.StopProcessing(); } - } // StopProcessing + } + internal Collection stopContextCollection = - new Collection(); + new(); /// - /// Gets or sets the filter property + /// Gets or sets the filter property. /// - /// /// /// This is meant to be overridden by derived classes if /// they support the Filter parameter. This property is on /// the base class to simplify the creation of the CmdletProviderContext. /// - /// public virtual string Filter { get; set; } - /// - /// Gets or sets the include property + /// Gets or sets the include property. /// - /// /// /// This is meant to be overridden by derived classes if /// they support the Include parameter. This property is on /// the base class to simplify the creation of the CmdletProviderContext. /// - /// - public virtual string[] Include { get; -// get + public virtual string[] Include + { + get; set; -// set - } = new string[0]; - -// Include - + } = Array.Empty(); /// - /// Gets or sets the exclude property + /// Gets or sets the exclude property. /// - /// /// /// This is meant to be overridden by derived classes if /// they support the Exclude parameter. This property is on /// the base class to simplify the creation of the CmdletProviderContext. /// - /// - public virtual string[] Exclude { get; -// get + public virtual string[] Exclude + { + get; set; -// set - } = new string[0]; - -// Exclude - + } = Array.Empty(); /// - /// Gets or sets the force property + /// Gets or sets the force property. /// - /// /// /// Gives the provider guidance on how vigorous it should be about performing /// the operation. If true, the provider should do everything possible to perform @@ -262,26 +221,18 @@ protected override void StopProcessing() /// they support the Force parameter. This property is on /// the base class to simplify the creation of the CmdletProviderContext. /// - /// public virtual SwitchParameter Force { - get - { - return _force; - } - set - { - _force = value; - } - } // Force - private bool _force; + get => _force; + set => _force = value; + } + private bool _force; /// /// Retrieves the dynamic parameters for the command from /// the provider. /// - /// public object GetDynamicParameters() { // Don't stream errors or Write* to the pipeline. @@ -306,22 +257,15 @@ public object GetDynamicParameters() } return _dynamicParameters; - } // GetDynamicParameters + } /// - /// Determines if the cmdlet and CmdletProvider supports ShouldProcess + /// Determines if the cmdlet and CmdletProvider supports ShouldProcess. /// - /// - public bool SupportsShouldProcess - { - get - { - return ProviderSupportsShouldProcess; - } - } // SupportsShouldProcess + public bool SupportsShouldProcess => ProviderSupportsShouldProcess; #endregion Public members - } // class CoreCommandBase + } #endregion CoreCommandBase @@ -336,11 +280,10 @@ public class CoreCommandWithCredentialsBase : CoreCommandBase #region Parameters /// - /// Gets or sets the credential parameter + /// Gets or sets the credential parameter. /// - /// [Parameter(ValueFromPipelineByPropertyName = true)] - [Credential()] + [Credential] public PSCredential Credential { get; set; } #endregion Parameters @@ -358,7 +301,7 @@ internal override CmdletProviderContext CmdletProviderContext { get { - CmdletProviderContext coreCommandContext = new CmdletProviderContext(this, Credential); + CmdletProviderContext coreCommandContext = new(this, Credential); coreCommandContext.Force = Force; Collection includeFilter = @@ -374,10 +317,10 @@ internal override CmdletProviderContext CmdletProviderContext return coreCommandContext; } - } // CmdletProviderContext + } #endregion Protected members - } // CoreCommandWithCredentialsBase + } #endregion CoreCommandWithCredentialsBase @@ -388,54 +331,34 @@ internal override CmdletProviderContext CmdletProviderContext /// This command does things like list the contents of a container, get /// an item at a given path, get the current working directory, etc. /// - /// /// /// - /// - [Cmdlet(VerbsCommon.Get, "Location", DefaultParameterSetName = "Location", SupportsTransactions = true, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113321")] - [OutputType(typeof(PathInfo), ParameterSetName = new string[] { "locationSet" })] - [OutputType(typeof(PathInfoStack), ParameterSetName = new string[] { "Stack" })] + [Cmdlet(VerbsCommon.Get, "Location", DefaultParameterSetName = LocationParameterSet, SupportsTransactions = true, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2096495")] + [OutputType(typeof(PathInfo), ParameterSetName = new string[] { LocationParameterSet })] + [OutputType(typeof(PathInfoStack), ParameterSetName = new string[] { StackParameterSet })] public class GetLocationCommand : DriveMatchingCoreCommandBase { - /// - /// The string declaration for the Location parameter set in this command. - /// - /// - /// The "Location" parameter set includes the following parameters: - /// -location - /// - private const string locationSet = "Location"; - - /// - /// The string declaration for the Stack parameter set in this command. - /// - /// - /// The "Stack" parameter set includes the following parameters: - /// -stack - /// - private const string stackSet = "Stack"; + private const string LocationParameterSet = "Location"; + private const string StackParameterSet = "Stack"; #region Command parameters #region Location parameter set parameters - /// /// Gets or sets the provider from which to get the current location. /// - /// - [Parameter(ParameterSetName = locationSet, ValueFromPipelineByPropertyName = true)] + [Parameter(ParameterSetName = LocationParameterSet, ValueFromPipelineByPropertyName = true)] public string[] PSProvider { - get { return _provider; } - set { _provider = value ?? Utils.EmptyArray(); } + get => _provider; + set => _provider = value ?? Array.Empty(); } /// /// Gets or sets the drive from which to get the current location. /// - /// - [Parameter(ParameterSetName = locationSet, ValueFromPipelineByPropertyName = true)] + [Parameter(ParameterSetName = LocationParameterSet, ValueFromPipelineByPropertyName = true)] public string[] PSDrive { get; set; } #endregion Location parameter set parameters @@ -444,41 +367,29 @@ public string[] PSProvider /// /// Gets or sets the Stack switch parameter which is used - /// to disambiguate parameter sets + /// to disambiguate parameter sets. /// /// - [Parameter(ParameterSetName = stackSet)] + [Parameter(ParameterSetName = StackParameterSet)] public SwitchParameter Stack { - get - { - return _stackSwitch; - } - set - { - _stackSwitch = value; - } + get => _stackSwitch; + set => _stackSwitch = value; } + private bool _stackSwitch; /// /// Gets or sets the stack ID for the location stack that will /// be retrieved. /// - /// - [Parameter(ParameterSetName = stackSet, ValueFromPipelineByPropertyName = true)] + [Parameter(ParameterSetName = StackParameterSet, ValueFromPipelineByPropertyName = true)] public string[] StackName { - get - { - return _stackNames; - } // get + get => _stackNames; - set - { - _stackNames = value; - } // set - } // StackName + set => _stackNames = value; + } #endregion Stack parameter set parameters @@ -486,13 +397,12 @@ public string[] StackName #region command data - #region Location parameter set data /// /// The name of the provider from which to return the current location. /// - private string[] _provider = new string[0]; + private string[] _provider = Array.Empty(); #endregion Location parameter set data @@ -505,10 +415,8 @@ public string[] StackName #endregion Stack parameter set data - #endregion command data - #region command code /// @@ -516,7 +424,7 @@ public string[] StackName /// the parameter set that is specified, the command can do many things. /// -locationSet gets the current working directory as a Monad path /// -stackSet gets the directory stack of directories that have been - /// pushed by the push-location command + /// pushed by the push-location command. /// protected override void ProcessRecord() { @@ -524,7 +432,7 @@ protected override void ProcessRecord() // want a case sensitive comparison in the current culture. switch (ParameterSetName) { - case locationSet: + case LocationParameterSet: PathInfo result = null; if (PSDrive != null && PSDrive.Length > 0) @@ -539,7 +447,7 @@ protected override void ProcessRecord() catch (DriveNotFoundException e) { ErrorRecord errorRecord = - new ErrorRecord( + new( e, "GetLocationNoMatchingDrive", ErrorCategory.ObjectNotFound, @@ -550,7 +458,7 @@ protected override void ProcessRecord() catch (ProviderNotFoundException e) { ErrorRecord errorRecord = - new ErrorRecord( + new( e, "GetLocationNoMatchingProvider", ErrorCategory.ObjectNotFound, @@ -561,7 +469,7 @@ protected override void ProcessRecord() catch (ArgumentException argException) { ErrorRecord errorRecord = - new ErrorRecord( + new( argException, "GetLocationNoMatchingDrive", ErrorCategory.ObjectNotFound, @@ -615,7 +523,7 @@ protected override void ProcessRecord() catch (ProviderNotFoundException e) { ErrorRecord errorRecord = - new ErrorRecord( + new( e, "GetLocationNoMatchingProvider", ErrorCategory.ObjectNotFound, @@ -669,9 +577,10 @@ protected override void ProcessRecord() // Get the current working directory using the core command API. WriteObject(SessionState.Path.CurrentLocation); } + break; - case stackSet: + case StackParameterSet: if (_stackNames != null) { foreach (string stackName in _stackNames) @@ -705,94 +614,60 @@ protected override void ProcessRecord() argException)); } } + break; default: - Dbg.Diagnostics.Assert(false, String.Format(System.Globalization.CultureInfo.InvariantCulture, "One of the predefined parameter sets should have been specified, instead we got: {0}", ParameterSetName)); + Dbg.Diagnostics.Assert(false, string.Create(System.Globalization.CultureInfo.InvariantCulture, $"One of the predefined parameter sets should have been specified, instead we got: {ParameterSetName}")); break; - } // case (ParameterSetName) - } // ProcessRecord + } + } #endregion command code - } // class GetLocationCommand + } #endregion GetLocationCommand - #region SetLocationCommand /// /// The core command for setting/changing location. /// This is the equivalent of cd command. /// - [Cmdlet(VerbsCommon.Set, "Location", DefaultParameterSetName = "Path", SupportsTransactions = true, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113397")] + [Cmdlet(VerbsCommon.Set, "Location", DefaultParameterSetName = PathParameterSet, SupportsTransactions = true, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2097049")] [OutputType(typeof(PathInfo), typeof(PathInfoStack))] public class SetLocationCommand : CoreCommandBase { #region Command parameters + private const string PathParameterSet = "Path"; + private const string LiteralPathParameterSet = "LiteralPath"; + private const string StackParameterSet = "Stack"; /// - /// The string declaration for the Location parameter set in this command. - /// - private const string pathSet = "Path"; - - /// - /// The string declaration for the literal location parameter set in this command. - /// - private const string literalPathSet = "LiteralPath"; - - /// - /// The string declaration for the Stack parameter set in this command. - /// - private const string stackSet = "Stack"; - -#if RELATIONSHIP_SUPPORTED - // 2004/11/24-JeffJon - Relationships have been removed from the Exchange release - - /// - /// The string declaration for the Relationship parameter set in this command. - /// - private const string relationshipSet = "Relationship"; -#endif - /// - /// Gets or sets the path property + /// Gets or sets the path property. /// -#if RELATIONSHIP_SUPPORTED - // 2004/11/24-JeffJon - Relationships have been removed from the Exchange release - - [Parameter(Position = 0, ParameterSetName = relationshipSet, ValueFromPipelineByPropertyName = true)] -#endif - [Parameter(Position = 0, ParameterSetName = pathSet, + [Parameter(Position = 0, ParameterSetName = PathParameterSet, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true)] public string Path { - get - { - return _path; - } - set - { - _path = value; - } + get => _path; + set => _path = value; } /// - /// Gets or sets the path path property, when bound from the pipeline. + /// Gets or sets the path property, when bound from the pipeline. /// - [Parameter(ParameterSetName = literalPathSet, + [Parameter(ParameterSetName = LiteralPathParameterSet, Mandatory = true, ValueFromPipeline = false, ValueFromPipelineByPropertyName = true)] - [Alias("PSPath")] + [Alias("PSPath", "LP")] public string LiteralPath { - get - { - return _path; - } + get => _path; set { _path = value; base.SuppressWildcardExpansion = true; } - } // PSPath + } /// /// Gets or sets the parameter -passThru which states output from @@ -801,8 +676,8 @@ public string LiteralPath [Parameter] public SwitchParameter PassThru { - get { return _passThrough; } - set { _passThrough = value; } + get => _passThrough; + set => _passThrough = value; } /// @@ -810,79 +685,17 @@ public SwitchParameter PassThru /// to use for the push. If the parameter is missing or empty the default /// location stack is used. /// - [Parameter(ParameterSetName = stackSet, ValueFromPipelineByPropertyName = true)] + [Parameter(ParameterSetName = StackParameterSet, ValueFromPipelineByPropertyName = true)] public string StackName { get; set; } -#if RELATIONSHIP_SUPPORTED - // 2004/11/24-JeffJon - Relationships have been removed from the Exchange release - - /// - /// Gets or sets the relationship Parameter which determines which relationship - /// to resolve to a path to set-location to. - /// - /// - [Parameter(Mandatory = true, ParameterSetName = relationshipSet, ValueFromPipelineByPropertyName = true)] - public string Relationship - { - get - { - return relationship; - } - - set - { - relationship = value; - } - } - private string relationship = String.Empty; - - - /// - /// Gets or sets the Property parameter value - /// - /// - [Parameter(ParameterSetName = relationshipSet, ValueFromPipelineByPropertyName = true)] - public string Property - { - get - { - return property; - } - - set - { - property = value; - } - } - private string property = String.Empty; - - /// - /// Gets or sets the Target parameter value - /// - /// - [Parameter (ParameterSetName = relationshipSet, ValueFromPipelineByPropertyName = true)] - public string Target - { - get - { - return target; - } - - set - { - target = value; - } - } - private string target = String.Empty; -#endif #endregion Command parameters #region Command data /// - /// The filter used when doing a dir + /// The filter used when doing a dir. /// - private string _path = String.Empty; + private string _path = string.Empty; /// /// Determines if output should be passed through for @@ -904,8 +717,8 @@ protected override void ProcessRecord() switch (ParameterSetName) { - case pathSet: - case literalPathSet: + case PathParameterSet: + case LiteralPathParameterSet: try { // Change the current working directory @@ -915,7 +728,7 @@ protected override void ProcessRecord() Path = SessionState.Internal.GetSingleProvider(Commands.FileSystemProvider.ProviderName).Home; } - result = SessionState.Path.SetLocation(Path, CmdletProviderContext); + result = SessionState.Path.SetLocation(Path, CmdletProviderContext, ParameterSetName == LiteralPathParameterSet); } catch (PSNotSupportedException notSupported) { @@ -952,9 +765,10 @@ protected override void ProcessRecord() argException.ErrorRecord, argException)); } + break; - case stackSet: + case StackParameterSet: try { @@ -971,76 +785,6 @@ protected override void ProcessRecord() break; -#if RELATIONSHIP_SUPPORTED - // 2004/11/24-JeffJon - Relationships have been removed from the Exchange release - - case relationshipSet: - string relationshipPath = null; - try - { - relationshipPath = - InvokeProvider.Relationship.Resolve( - Relationship, - Path, - Property, - Target); - } - catch (PSArgumentException argException) - { - WriteError( - new ErrorRecord( - argException.ErrorRecord, - argException)); - return; - } - - try - { - result = SessionState.Path.SetLocation (relationshipPath, CmdletProviderContext); - } - catch (PSNotSupportedException notSupported) - { - WriteError( - new ErrorRecord( - notSupported.ErrorRecord, - notSupported)); - return; - } - catch (DriveNotFoundException driveNotFound) - { - WriteError( - new ErrorRecord( - driveNotFound.ErrorRecord, - driveNotFound)); - return; - } - catch (ProviderNotFoundException providerNotFound) - { - WriteError( - new ErrorRecord( - providerNotFound.ErrorRecord, - providerNotFound)); - return; - } - catch (PSArgumentException argException) - { - WriteError( - new ErrorRecord( - argException.ErrorRecord, - argException)); - return; - } - catch (ItemNotFoundException pathNotFound) - { - WriteError( - new ErrorRecord( - pathNotFound.ErrorRecord, - pathNotFound)); - return; - } - - break; -#endif default: Dbg.Diagnostics.Assert( false, @@ -1052,10 +796,10 @@ protected override void ProcessRecord() { WriteObject(result); } - } // ProcessRecord + } #endregion Command code - } // SetLocationCommand + } #endregion SetLocationCommand @@ -1065,57 +809,39 @@ protected override void ProcessRecord() /// The core command for setting/changing location and pushing it onto a location stack. /// This is the equivalent of the pushd command. /// - [Cmdlet(VerbsCommon.Push, "Location", DefaultParameterSetName = "Path", SupportsTransactions = true, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113370")] + [Cmdlet(VerbsCommon.Push, "Location", DefaultParameterSetName = PathParameterSet, SupportsTransactions = true, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2097105")] public class PushLocationCommand : CoreCommandBase { #region Command parameters - -#if RELATIONSHIP_SUPPORTED - // 2004/11/24-JeffJon - Relationships have been removed from the Exchange release - private const string relationshipSet = "Relationship"; -#endif + private const string PathParameterSet = "Path"; + private const string LiteralPathParameterSet = "LiteralPath"; /// - /// Gets or sets the path property + /// Gets or sets the path property. /// -#if RELATIONSHIP_SUPPORTED - // 2004/11/24-JeffJon - Relationships have been removed from the Exchange release - [Parameter (Position = 0, ParameterSetName = relationshipSet, ValueFromPipelineByPropertyName = true)] -#endif - [Parameter(Position = 0, ParameterSetName = "Path", + [Parameter(Position = 0, ParameterSetName = PathParameterSet, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true)] public string Path { - get - { - return _path; - } - set - { - _path = value; - } + get => _path; + set => _path = value; } /// - /// Gets or sets the literal path parameter to the command + /// Gets or sets the literal path parameter to the command. /// - [Parameter(ParameterSetName = "LiteralPath", + [Parameter(ParameterSetName = LiteralPathParameterSet, ValueFromPipeline = false, ValueFromPipelineByPropertyName = true)] - [Alias("PSPath")] + [Alias("PSPath", "LP")] public string LiteralPath { - get - { - return _path; - } // get - + get => _path; set { base.SuppressWildcardExpansion = true; _path = value; - } // set - } // LiteralPath - + } + } /// /// Gets or sets the parameter -passThru which states output from @@ -1124,107 +850,30 @@ public string LiteralPath [Parameter] public SwitchParameter PassThru { - get - { - return _passThrough; - } // get - set - { - _passThrough = value; - } //set - } // PassThru + get => _passThrough; + set => _passThrough = value; + } /// /// Gets or sets the StackName parameter which determines which location stack /// to use for the push. If the parameter is missing or empty the default /// location stack is used. /// -#if RELATIONSHIP_SUPPORTED - // 2004/11/24-JeffJon - Relationships have been removed from the Exchange release - [Parameter (ParameterSetName = relationshipSet)] -#endif [Parameter(ValueFromPipelineByPropertyName = true)] public string StackName { - get - { - return _stackName; - } // get - set - { - _stackName = value; - } //set - } // StackName - -#if RELATIONSHIP_SUPPORTED - // 2004/11/24-JeffJon - Relationships have been removed from the Exchange release - - /// - /// Gets or sets the relationship Parameter which determines which relationship - /// to resolve to a path to set-location to. - /// - /// - [Parameter (Mandatory = true, ParameterSetName = relationshipSet, ValueFromPipelineByPropertyName = true)] - public string Relationship - { - get - { - return relationship; - } - - set - { - relationship = value; - } - } - private string relationship = String.Empty; - - /// - /// Gets or sets the Property parameter value - /// - /// - [Parameter (ParameterSetName = relationshipSet, ValueFromPipelineByPropertyName = true)] - public string Property - { - get - { - return property; - } - - set - { - property = value; - } + get => _stackName; + set => _stackName = value; } - private string property = String.Empty; - - /// - /// Gets or sets the Target parameter value - /// - /// - [Parameter (ParameterSetName = relationshipSet, ValueFromPipelineByPropertyName = true)] - public string Target - { - get - { - return target; - } - set - { - target = value; - } - } - private string target = String.Empty; -#endif #endregion Command parameters #region Command data /// - /// The filter used when doing a dir + /// The filter used when doing a dir. /// - private string _path = String.Empty; + private string _path = string.Empty; /// /// Determines if output should be passed through for @@ -1251,42 +900,6 @@ protected override void ProcessRecord() // working directory stack SessionState.Path.PushCurrentLocation(_stackName); -#if RELATIONSHIP_SUPPORTED - // 2004/11/24-JeffJon - Relationships have been removed from the Exchange release - - if (String.Equals( - relationshipSet, - ParameterSetName, - StringComparison.OrdinalIgnoreCase)) - { - try - { - Path = - InvokeProvider.Relationship.Resolve( - Relationship, - Path, - Property, - Target); - } - catch (ProviderNotFoundException providerNotFound) - { - WriteError( - new ErrorRecord( - providerNotFound.ErrorRecord, - providerNotFound)); - - return; - } - catch (PSArgumentException argException) - { - WriteError( - new ErrorRecord( - argException.ErrorRecord, - argException)); - return; - } - } -#endif if (Path != null) { try @@ -1340,11 +953,11 @@ protected override void ProcessRecord() argException)); return; } - } // Path != null - } // ProcessRecord + } + } #endregion Command code - } // PushLocationCommand + } #endregion PushLocationCommand @@ -1354,7 +967,7 @@ protected override void ProcessRecord() /// The core command for pop-location. This is the equivalent of the popd command. /// It pops a container from the stack and sets the current location to that container. /// - [Cmdlet(VerbsCommon.Pop, "Location", SupportsTransactions = true, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113369")] + [Cmdlet(VerbsCommon.Pop, "Location", SupportsTransactions = true, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2096907")] public class PopLocationCommand : CoreCommandBase { #region Command parameters @@ -1366,15 +979,9 @@ public class PopLocationCommand : CoreCommandBase [Parameter] public SwitchParameter PassThru { - get - { - return _passThrough; - } // get - set - { - _passThrough = value; - } //set - } // PassThru + get => _passThrough; + set => _passThrough = value; + } /// /// Gets or sets the StackName parameter which determines which location stack @@ -1384,15 +991,9 @@ public SwitchParameter PassThru [Parameter(ValueFromPipelineByPropertyName = true)] public string StackName { - get - { - return _stackName; - } // get - set - { - _stackName = value; - } //set - } // StackName + get => _stackName; + set => _stackName = value; + } #endregion Command parameters @@ -1411,10 +1012,8 @@ public string StackName #endregion Command data - #region Command code - /// /// Gets the top container from the location stack and sets the /// location to it. @@ -1464,10 +1063,10 @@ protected override void ProcessRecord() itemNotFound)); return; } - } // ProcessRecord + } #endregion Command code - } // PopLocationCommand + } #endregion PopLocationCommand @@ -1476,96 +1075,64 @@ protected override void ProcessRecord() #region NewPSDriveCommand /// - /// Mounts a drive in the Monad namespace. + /// Mounts a drive in PowerShell runspace. /// - [Cmdlet(VerbsCommon.New, "PSDrive", SupportsShouldProcess = true, SupportsTransactions = true, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113357")] + [Cmdlet(VerbsCommon.New, "PSDrive", SupportsShouldProcess = true, ConfirmImpact = ConfirmImpact.Low, + SupportsTransactions = true, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2096815")] public class NewPSDriveCommand : CoreCommandWithCredentialsBase { #region Command parameters /// - /// Gets or sets the name of the drive + /// Gets or sets the name of the drive. /// - /// [Parameter(Position = 0, Mandatory = true, ValueFromPipelineByPropertyName = true)] public string Name { - get { return _name; } - set - { - if (value == null) - { - throw PSTraceSource.NewArgumentNullException("value"); - } - - _name = value; - } + get => _name; + set => _name = value ?? throw PSTraceSource.NewArgumentNullException(nameof(value)); } /// - /// Gets or sets the provider ID + /// Gets or sets the provider ID. /// - /// [Parameter(Position = 1, Mandatory = true, ValueFromPipelineByPropertyName = true)] public string PSProvider { - get { return _provider; } - set - { - if (value == null) - { - throw PSTraceSource.NewArgumentNullException("value"); - } - - _provider = value; - } + get => _provider; + set => _provider = value ?? throw PSTraceSource.NewArgumentNullException(nameof(value)); } /// /// Gets or sets the root of the drive. This path should be /// a namespace specific path. /// - /// [Parameter(Position = 2, Mandatory = true, ValueFromPipelineByPropertyName = true)] [AllowEmptyString] public string Root { - get { return _root; } - set - { - if (value == null) - { - throw PSTraceSource.NewArgumentNullException("value"); - } - - _root = value; - } + get => _root; + set => _root = value ?? throw PSTraceSource.NewArgumentNullException(nameof(value)); } /// - /// Gets or sets the description of the drive + /// Gets or sets the description of the drive. /// [Parameter(ValueFromPipelineByPropertyName = true)] public string Description { - get { return _description; } - set - { - if (value == null) - { - throw PSTraceSource.NewArgumentNullException("value"); - } - - _description = value; - } + get => _description; + set => _description = value ?? throw PSTraceSource.NewArgumentNullException(nameof(value)); } /// /// Gets or sets the scope identifier for the drive being created. /// [Parameter(ValueFromPipelineByPropertyName = true)] + [ArgumentCompleter(typeof(ScopeArgumentCompleter))] public string Scope { get; set; } +#if !UNIX /// /// Gets or sets the Persist Switch parameter. /// If this switch parameter is set then the created PSDrive @@ -1574,58 +1141,54 @@ public string Description [Parameter(ValueFromPipelineByPropertyName = true)] public SwitchParameter Persist { - get { return _persist; } - set { _persist = value; } + get => _persist; + set => _persist = value; } - private bool _persist = false; + private bool _persist = false; +#endif /// /// Gets the dynamic parameters for the new-psdrive cmdlet. /// - /// /// /// The context under which the command is running. /// - /// /// /// An object representing the dynamic parameters for the cmdlet or null if there /// are none. /// - /// internal override object GetDynamicParameters(CmdletProviderContext context) { return SessionState.Drive.NewDriveDynamicParameters(PSProvider, context); } /// - /// new-psdrive always supports ShouldProcess + /// New-psdrive always supports ShouldProcess. /// /// - protected override bool ProviderSupportsShouldProcess - { - get { return true; } - } + protected override bool ProviderSupportsShouldProcess => true; + #endregion Command parameters #region Command data /// - /// The name of the drive + /// The name of the drive. /// private string _name; /// - /// The provider ID for the drive + /// The provider ID for the drive. /// private string _provider; /// - /// The namespace specific path of the root of the drive + /// The namespace specific path of the root of the drive. /// private string _root; /// - /// A description for the drive + /// A description for the drive. /// private string _description; @@ -1634,7 +1197,7 @@ protected override bool ProviderSupportsShouldProcess #region Command code /// - /// Adds a new drive to the Monad namespace + /// Adds a new drive to the Monad namespace. /// protected override void ProcessRecord() { @@ -1662,7 +1225,7 @@ protected override void ProcessRecord() string resourceTemplate = NavigationResources.NewDriveConfirmResourceTemplate; string resource = - String.Format( + string.Format( System.Globalization.CultureInfo.CurrentCulture, resourceTemplate, Name, @@ -1671,23 +1234,40 @@ protected override void ProcessRecord() if (ShouldProcess(resource, action)) { +#if !UNIX // -Persist switch parameter is supported only for FileSystem provider. if (Persist && !provider.Name.Equals(FileSystemProvider.ProviderName, StringComparison.OrdinalIgnoreCase)) { - ErrorRecord er = new ErrorRecord(new NotSupportedException(FileSystemProviderStrings.PersistNotSupported), "DriveRootNotNetworkPath", ErrorCategory.InvalidArgument, this); + ErrorRecord er = new(new NotSupportedException(FileSystemProviderStrings.PersistNotSupported), "DriveRootNotNetworkPath", ErrorCategory.InvalidArgument, this); ThrowTerminatingError(er); } + // Trimming forward and backward slash for FileSystem provider when -Persist is used. + if (Persist && provider.Name.Equals(FileSystemProvider.ProviderName, StringComparison.OrdinalIgnoreCase)) + { + Root = Root.TrimEnd('/', '\\'); + } + // Create the new drive PSDriveInfo newDrive = - new PSDriveInfo( + new( Name, provider, Root, Description, Credential, Persist); - +#else + // Create the new drive + PSDriveInfo newDrive = + new PSDriveInfo( + Name, + provider, + Root, + Description, + Credential, + persist: false); +#endif try { SessionState.Drive.New(newDrive, Scope, CmdletProviderContext); @@ -1742,7 +1322,7 @@ protected override void ProcessRecord() } } } - } // ProcessRecord + } #endregion Command code } @@ -1761,43 +1341,35 @@ public class DriveMatchingCoreCommandBase : CoreCommandBase /// Globs on both the drive name and the provider name to get a list of Drives /// that match the glob filters. /// - /// /// /// The name of the drive(s) to returned. The name can contain glob characters. /// - /// /// /// The name of the provider(s) to return. The name can contain glob characters. /// - /// /// /// The scope to get the drives from. If this parameter is null or empty all drives /// will be retrieved. /// - /// /// /// A collection of the drives that match the filters. /// - /// /// /// - /// /// /// If is less than zero, or not /// a number and not "script", "global", "local", or "private" /// - /// /// /// If is less than zero or greater than the number of currently /// active scopes. /// - /// internal List GetMatchingDrives( string driveName, string[] providerNames, string scope) { - List results = new List(); + List results = new(); if (providerNames == null || providerNames.Length == 0) { @@ -1808,11 +1380,11 @@ internal List GetMatchingDrives( { tracer.WriteLine("ProviderName: {0}", providerName); - bool providerNameEmpty = String.IsNullOrEmpty(providerName); + bool providerNameEmpty = string.IsNullOrEmpty(providerName); bool providerNameContainsWildcardCharacters = WildcardPattern.ContainsWildcardCharacters(providerName); - bool driveNameEmpty = String.IsNullOrEmpty(driveName); + bool driveNameEmpty = string.IsNullOrEmpty(driveName); bool driveNameContainsWildcardCharacters = WildcardPattern.ContainsWildcardCharacters(driveName); @@ -1831,7 +1403,7 @@ internal List GetMatchingDrives( // exist. if (!driveNameEmpty && !driveNameContainsWildcardCharacters) { - if (String.IsNullOrEmpty(scope)) + if (string.IsNullOrEmpty(scope)) { SessionState.Drive.Get(driveName); } @@ -1841,7 +1413,6 @@ internal List GetMatchingDrives( } } - WildcardPattern providerMatcher = null; PSSnapinQualifiedName pssnapinQualifiedProviderName = null; @@ -1861,7 +1432,6 @@ internal List GetMatchingDrives( WildcardOptions.IgnoreCase); } - WildcardPattern nameMatcher = null; if (!driveNameEmpty) @@ -1878,12 +1448,12 @@ internal List GetMatchingDrives( if (base.SuppressWildcardExpansion) { - if (String.Equals(drive.Name, driveName, StringComparison.OrdinalIgnoreCase)) + if (string.Equals(drive.Name, driveName, StringComparison.OrdinalIgnoreCase)) addDrive = true; } else { - if (nameMatcher.IsMatch(drive.Name)) + if (nameMatcher != null && nameMatcher.IsMatch(drive.Name)) addDrive = true; } @@ -1894,64 +1464,58 @@ internal List GetMatchingDrives( { results.Add(drive); } - } // nameMatcher.IsMatch() - } // foreach Drive + } + } } + results.Sort(); return results; } - } // DriveMatchingCoreCommandBase + } #endregion DriveMatchingCoreCommandBase #region RemovePSDriveCommand /// - /// Removes a drive that is mounted in the Monad namespace. + /// Removes a drive that is mounted in the PowerShell runspace. /// - [Cmdlet(VerbsCommon.Remove, "PSDrive", DefaultParameterSetName = "Name", SupportsShouldProcess = true, SupportsTransactions = true, - HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113376")] + [Cmdlet(VerbsCommon.Remove, "PSDrive", DefaultParameterSetName = NameParameterSet, SupportsShouldProcess = true, SupportsTransactions = true, + HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2097050")] public class RemovePSDriveCommand : DriveMatchingCoreCommandBase { #region Command parameters + private const string NameParameterSet = "Name"; + private const string LiteralNameParameterSet = "LiteralName"; + /// /// Gets or sets the name of the drive to remove. /// - [Parameter(Position = 0, ParameterSetName = "Name", + [Parameter(Position = 0, ParameterSetName = NameParameterSet, Mandatory = true, ValueFromPipelineByPropertyName = true)] [AllowNull] [AllowEmptyCollection] public string[] Name { - get - { - return _names; - } - set - { - _names = value; - } - } // Name + get => _names; + set => _names = value; + } /// - /// Gets or sets the literal name parameter to the command + /// Gets or sets the literal name parameter to the command. /// - [Parameter(Position = 0, ParameterSetName = "LiteralName", + [Parameter(Position = 0, ParameterSetName = LiteralNameParameterSet, Mandatory = true, ValueFromPipeline = false, ValueFromPipelineByPropertyName = true)] public string[] LiteralName { - get - { - return _names; - } // get - + get => _names; set { base.SuppressWildcardExpansion = true; _names = value; - } // set - } // LiteralName + } + } /// /// Gets or sets the name provider(s) for which the drives should be removed. @@ -1959,15 +1523,8 @@ public string[] LiteralName [Parameter(ValueFromPipelineByPropertyName = true)] public string[] PSProvider { - get { return _provider; } - set - { - if (value == null) - { - value = Utils.EmptyArray(); - } - _provider = value; - } + get => _provider; + set => _provider = value ?? Array.Empty(); } /// @@ -1977,28 +1534,25 @@ public string[] PSProvider /// global scope until a drive of the given name is found to remove. /// [Parameter(ValueFromPipelineByPropertyName = true)] + [ArgumentCompleter(typeof(ScopeArgumentCompleter))] public string Scope { get; set; } /// /// Gets or sets the force property which determines if the drive /// should be removed even if there were errors. /// - /// [Parameter] public override SwitchParameter Force { - get { return base.Force; } - set { base.Force = value; } + get => base.Force; + set => base.Force = value; } /// - /// Determines if the provider for the specified path supports ShouldProcess + /// Determines if the provider for the specified path supports ShouldProcess. /// /// - protected override bool ProviderSupportsShouldProcess - { - get { return true; } - } + protected override bool ProviderSupportsShouldProcess => true; #endregion Command parameters @@ -2012,7 +1566,7 @@ protected override bool ProviderSupportsShouldProcess /// /// The name of the provider(s) for which to remove all drives. /// - private string[] _provider = new string[0]; + private string[] _provider = Array.Empty(); #endregion Command data @@ -2044,7 +1598,7 @@ protected override void ProcessRecord() foreach (PSDriveInfo drive in GetMatchingDrives(driveName, PSProvider, Scope)) { string resource = - String.Format( + string.Format( System.Globalization.CultureInfo.CurrentCulture, resourceTemplate, drive.Name, @@ -2057,8 +1611,7 @@ protected override void ProcessRecord() if (!Force && drive == SessionState.Drive.Current) { PSInvalidOperationException invalidOperation = - (PSInvalidOperationException) - PSTraceSource.NewInvalidOperationException( + (PSInvalidOperationException)PSTraceSource.NewInvalidOperationException( NavigationResources.RemoveDriveInUse, drive.Name); @@ -2068,6 +1621,7 @@ protected override void ProcessRecord() invalidOperation)); continue; } + SessionState.Drive.Remove(drive.Name, Force, Scope, CmdletProviderContext); } } @@ -2084,65 +1638,60 @@ protected override void ProcessRecord() if (verifyMatch && !foundMatch) { - DriveNotFoundException e = new DriveNotFoundException( + DriveNotFoundException e = new( driveName, "DriveNotFound", SessionStateStrings.DriveNotFound); WriteError(new ErrorRecord(e.ErrorRecord, e)); } } - } // ProcessRecord + } #endregion Command code - } // RemovePSDriveCommand + } #endregion RemovePSDriveCommand #region GetPSDriveCommand /// - /// Gets a specified or listing of drives that are mounted in the Monad + /// Gets a specified or listing of drives that are mounted in PowerShell /// namespace. /// - [Cmdlet(VerbsCommon.Get, "PSDrive", DefaultParameterSetName = "Name", SupportsTransactions = true, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113327")] + [Cmdlet(VerbsCommon.Get, "PSDrive", DefaultParameterSetName = NameParameterSet, SupportsTransactions = true, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2096494")] [OutputType(typeof(PSDriveInfo))] public class GetPSDriveCommand : DriveMatchingCoreCommandBase { #region Command parameters + private const string NameParameterSet = "Name"; + private const string LiteralNameParameterSet = "LiteralName"; + /// /// Gets or sets the drive name the user is looking for. /// - /// /// /// If the drive name is left empty, all drives will be /// returned. A globing or regular expression can also be /// supplied and any drive names that match the expression /// will be returned. /// - [Parameter(Position = 0, ParameterSetName = "Name", ValueFromPipelineByPropertyName = true)] + [Parameter(Position = 0, ParameterSetName = NameParameterSet, ValueFromPipelineByPropertyName = true)] [ValidateNotNullOrEmpty] public string[] Name { - get { return _name; } - set - { - if (value == null) - { - value = new string[] { "*" }; - } - _name = value; - } + get => _name; + set => _name = value ?? new string[] { "*" }; } /// - /// Gets or sets the literal name parameter to the command + /// Gets or sets the literal name parameter to the command. /// - [Parameter(Position = 0, ParameterSetName = "LiteralName", + [Parameter(Position = 0, ParameterSetName = LiteralNameParameterSet, Mandatory = true, ValueFromPipeline = false, ValueFromPipelineByPropertyName = true)] public string[] LiteralName { - get { return _name; } + get => _name; set { base.SuppressWildcardExpansion = true; @@ -2153,15 +1702,14 @@ public string[] LiteralName /// /// Gets or sets the scope parameter to the command. /// - /// [Parameter(ValueFromPipelineByPropertyName = true)] + [ArgumentCompleter(typeof(ScopeArgumentCompleter))] public string Scope { get; set; } /// /// Gets or sets the provider name for the /// drives that should be retrieved. /// - /// /// /// If the provider is left empty, all drives will be /// returned. A globing or regular expression can also be @@ -2171,15 +1719,8 @@ public string[] LiteralName [Parameter(ValueFromPipelineByPropertyName = true)] public string[] PSProvider { - get { return _provider; } - set - { - if (value == null) - { - value = Utils.EmptyArray(); - } - _provider = value; - } + get => _provider; + set => _provider = value ?? Array.Empty(); } #endregion Command parameters @@ -2194,7 +1735,7 @@ public string[] PSProvider /// /// The provider ID for the drives you want to see. /// - private string[] _provider = new string[0]; + private string[] _provider = Array.Empty(); #endregion Command data @@ -2237,7 +1778,7 @@ protected override void ProcessRecord() if (!WildcardPattern.ContainsWildcardCharacters(driveName)) { DriveNotFoundException driveNotFound = - new DriveNotFoundException( + new( driveName, "DriveNotFound", SessionStateStrings.DriveNotFound); @@ -2254,7 +1795,7 @@ protected override void ProcessRecord() catch (DriveNotFoundException driveNotFound) { ErrorRecord errorRecord = - new ErrorRecord( + new( driveNotFound, "GetLocationNoMatchingDrive", ErrorCategory.ObjectNotFound, @@ -2264,7 +1805,7 @@ protected override void ProcessRecord() catch (ProviderNotFoundException providerNotFound) { ErrorRecord errorRecord = - new ErrorRecord( + new( providerNotFound, "GetLocationNoMatchingDrive", ErrorCategory.ObjectNotFound, @@ -2286,10 +1827,10 @@ protected override void ProcessRecord() argException)); } } - } // ProcessRecord + } #endregion Command code - } // GetPSDriveCommand + } #endregion GetPSDriveCommand @@ -2302,102 +1843,74 @@ protected override void ProcessRecord() /// /// Gets the specified item using the namespace providers. /// - [Cmdlet(VerbsCommon.Get, "Item", DefaultParameterSetName = "Path", SupportsTransactions = true, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113319")] + [Cmdlet(VerbsCommon.Get, "Item", DefaultParameterSetName = PathParameterSet, SupportsTransactions = true, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2096812")] public class GetItemCommand : CoreCommandWithCredentialsBase { #region Command parameters + private const string PathParameterSet = "Path"; + private const string LiteralPathParameterSet = "LiteralPath"; + /// /// Gets or sets the path to item to get. /// - [Parameter(Position = 0, ParameterSetName = "Path", + [Parameter(Position = 0, ParameterSetName = PathParameterSet, Mandatory = true, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true)] public string[] Path { - get - { - return _paths; - } - set - { - _paths = value; - } - } // Path + get => _paths; + set => _paths = value; + } /// - /// Gets or sets the literal path parameter to the command + /// Gets or sets the literal path parameter to the command. /// - [Parameter(ParameterSetName = "LiteralPath", + [Parameter(ParameterSetName = LiteralPathParameterSet, Mandatory = true, ValueFromPipeline = false, ValueFromPipelineByPropertyName = true)] - [Alias("PSPath")] + [Alias("PSPath", "LP")] public string[] LiteralPath { - get - { - return _paths; - } // get - + get => _paths; set { base.SuppressWildcardExpansion = true; _paths = value; - } // set - } // LiteralPath + } + } /// - /// Gets or sets the filter property + /// Gets or sets the filter property. /// [Parameter] public override string Filter { - get - { - return base.Filter; - } - set - { - base.Filter = value; - } + get => base.Filter; + set => base.Filter = value; } /// - /// Gets or sets the include property + /// Gets or sets the include property. /// [Parameter] public override string[] Include { - get - { - return base.Include; - } // get - - set - { - base.Include = value; - } // set - } // Include + get => base.Include; + set => base.Include = value; + } /// - /// Gets or sets the exclude property + /// Gets or sets the exclude property. /// [Parameter] public override string[] Exclude { - get - { - return base.Exclude; - } // get - - set - { - base.Exclude = value; - } // set - } // Exclude + get => base.Exclude; + set => base.Exclude = value; + } /// - /// Gets or sets the force property + /// Gets or sets the force property. /// - /// /// /// Gives the provider guidance on how vigorous it should be about performing /// the operation. If true, the provider should do everything possible to perform @@ -2407,41 +1920,32 @@ public override string[] Exclude /// the destination is read-only, if force is true, the provider should copy over /// the existing read-only file. If force is false, the provider should write an error. /// - /// [Parameter] public override SwitchParameter Force { - get - { - return base.Force; - } - set - { - base.Force = value; - } - } // Force + get => base.Force; + set => base.Force = value; + } /// /// Gets the dynamic parameters for the get-item cmdlet. /// - /// /// /// The context under which the command is running. /// - /// /// /// An object representing the dynamic parameters for the cmdlet or null if there /// are none. /// - /// internal override object GetDynamicParameters(CmdletProviderContext context) { if (Path != null && Path.Length > 0) { return InvokeProvider.Item.GetItemDynamicParameters(Path[0], context); } + return InvokeProvider.Item.GetItemDynamicParameters(".", context); - } // GetDynamicParameters + } #endregion Command parameters @@ -2495,10 +1999,10 @@ protected override void ProcessRecord() pathNotFound)); } } - } // ProcessRecord + } #endregion Command code - } // GetItemCommand + } #endregion GetItemCommand @@ -2507,48 +2011,47 @@ protected override void ProcessRecord() /// /// Creates the specified item using the namespace providers. /// - [Cmdlet(VerbsCommon.New, "Item", DefaultParameterSetName = "pathSet", SupportsShouldProcess = true, SupportsTransactions = true, - HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113353")] + [Cmdlet(VerbsCommon.New, "Item", DefaultParameterSetName = PathParameterSet, SupportsShouldProcess = true, SupportsTransactions = true, + HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2096592")] public class NewItemCommand : CoreCommandWithCredentialsBase { #region Command parameters - private const string nameSet = "nameSet"; - private const string pathSet = "pathSet"; + private const string NameParameterSet = "nameSet"; + private const string PathParameterSet = "pathSet"; /// /// Gets or sets the container path to create the item in. /// - [Parameter(Position = 0, ParameterSetName = "pathSet", Mandatory = true, ValueFromPipelineByPropertyName = true)] - [Parameter(Position = 0, ParameterSetName = "nameSet", Mandatory = false, ValueFromPipelineByPropertyName = true)] + [Parameter(Position = 0, ParameterSetName = PathParameterSet, Mandatory = true, ValueFromPipelineByPropertyName = true)] + [Parameter(Position = 0, ParameterSetName = NameParameterSet, Mandatory = false, ValueFromPipelineByPropertyName = true)] public string[] Path { get; set; } /// - /// Gets or sets the name of the item to create + /// Gets or sets the name of the item to create. /// - [Parameter(ParameterSetName = "nameSet", Mandatory = true, ValueFromPipelineByPropertyName = true)] + [Parameter(ParameterSetName = NameParameterSet, Mandatory = true, ValueFromPipelineByPropertyName = true)] [AllowNull] [AllowEmptyString] public string Name { get; set; } /// - /// Gets or sets the type of the item to create + /// Gets or sets the type of the item to create. /// [Parameter(ValueFromPipelineByPropertyName = true)] [Alias("Type")] public string ItemType { get; set; } /// - /// Gets or sets the content of the item to create + /// Gets or sets the content of the item to create. /// [Parameter(ValueFromPipeline = true, ValueFromPipelineByPropertyName = true)] [Alias("Target")] public object Value { get; set; } /// - /// Gets or sets the force property + /// Gets or sets the force property. /// - /// /// /// Gives the provider guidance on how vigorous it should be about performing /// the operation. If true, the provider should do everything possible to perform @@ -2558,51 +2061,42 @@ public class NewItemCommand : CoreCommandWithCredentialsBase /// the destination is read-only, if force is true, the provider should copy over /// the existing read-only file. If force is false, the provider should write an error. /// - /// [Parameter] public override SwitchParameter Force { - get { return base.Force; } - set { base.Force = value; } + get => base.Force; + set => base.Force = value; } /// /// Gets the dynamic parameters for the new-item cmdlet. /// - /// /// /// The context under which the command is running. /// - /// /// /// An object representing the dynamic parameters for the cmdlet or null if there /// are none. /// - /// internal override object GetDynamicParameters(CmdletProviderContext context) { if (Path != null && Path.Length > 0) { // Path is only globbed if Name is specified. - if (String.IsNullOrEmpty(Name)) + if (string.IsNullOrEmpty(Name)) return InvokeProvider.Item.NewItemDynamicParameters(WildcardPattern.Escape(Path[0]), ItemType, Value, context); else return InvokeProvider.Item.NewItemDynamicParameters(Path[0], ItemType, Value, context); } + return InvokeProvider.Item.NewItemDynamicParameters(".", ItemType, Value, context); } /// - /// Determines if the provider for the specified path supports ShouldProcess + /// Determines if the provider for the specified path supports ShouldProcess. /// /// - protected override bool ProviderSupportsShouldProcess - { - get - { - return base.DoesProviderSupportShouldProcess(Path); - } - } + protected override bool ProviderSupportsShouldProcess => DoesProviderSupportShouldProcess(Path); #endregion Command parameters @@ -2617,10 +2111,9 @@ protected override bool ProviderSupportsShouldProcess /// protected override void ProcessRecord() { - if (Path == null || - (Path != null && Path.Length == 0)) + if (Path == null || Path.Length == 0) { - Path = new string[] { String.Empty }; + Path = new string[] { string.Empty }; } foreach (string path in Path) @@ -2658,10 +2151,10 @@ protected override void ProcessRecord() pathNotFound)); } } - } // ProcessRecord + } #endregion Command code - } // NewItemCommand + } #endregion NewItemCommand @@ -2670,37 +2163,34 @@ protected override void ProcessRecord() /// /// Sets the specified item using the namespace providers. /// - [Cmdlet(VerbsCommon.Set, "Item", SupportsShouldProcess = true, DefaultParameterSetName = "Path", SupportsTransactions = true, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113395")] + [Cmdlet(VerbsCommon.Set, "Item", SupportsShouldProcess = true, DefaultParameterSetName = PathParameterSet, SupportsTransactions = true, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2097055")] public class SetItemCommand : CoreCommandWithCredentialsBase { #region Command parameters + private const string PathParameterSet = "Path"; + private const string LiteralPathParameterSet = "LiteralPath"; + /// /// Gets or sets the path to item to set. /// - [Parameter(Position = 0, ParameterSetName = "Path", + [Parameter(Position = 0, ParameterSetName = PathParameterSet, Mandatory = true, ValueFromPipelineByPropertyName = true)] public string[] Path { - get - { - return _paths; - } - set - { - _paths = value; - } - } // Path + get => _paths; + set => _paths = value; + } /// - /// Gets or sets the literal path parameter to the command + /// Gets or sets the literal path parameter to the command. /// - [Parameter(ParameterSetName = "LiteralPath", + [Parameter(ParameterSetName = LiteralPathParameterSet, Mandatory = true, ValueFromPipeline = false, ValueFromPipelineByPropertyName = true)] - [Alias("PSPath")] + [Alias("PSPath", "LP")] public string[] LiteralPath { - get { return _paths; } + get => _paths; set { base.SuppressWildcardExpansion = true; @@ -2709,15 +2199,14 @@ public string[] LiteralPath } /// - /// Gets or sets the value of the item to be set + /// Gets or sets the value of the item to be set. /// [Parameter(Position = 1, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true)] public object Value { get; set; } /// - /// Gets or sets the force property + /// Gets or sets the force property. /// - /// /// /// Gives the provider guidance on how vigorous it should be about performing /// the operation. If true, the provider should do everything possible to perform @@ -2727,12 +2216,11 @@ public string[] LiteralPath /// the destination is read-only, if force is true, the provider should copy over /// the existing read-only file. If force is false, the provider should write an error. /// - /// [Parameter] public override SwitchParameter Force { - get { return base.Force; } - set { base.Force = value; } + get => base.Force; + set => base.Force = value; } /// @@ -2740,77 +2228,68 @@ public override SwitchParameter Force /// if the object that is set should be written to the pipeline. /// Defaults to false. /// - /// [Parameter] public SwitchParameter PassThru { - get { return _passThrough; } - set { _passThrough = value; } + get => _passThrough; + set => _passThrough = value; } /// - /// Gets or sets the filter property + /// Gets or sets the filter property. /// [Parameter] public override string Filter { - get { return base.Filter; } - set { base.Filter = value; } + get => base.Filter; + set => base.Filter = value; } /// - /// Gets or sets the include property + /// Gets or sets the include property. /// [Parameter] public override string[] Include { - get { return base.Include; } - set { base.Include = value; } - } // Include + get => base.Include; + set => base.Include = value; + } /// - /// Gets or sets the exclude property + /// Gets or sets the exclude property. /// [Parameter] public override string[] Exclude { - get { return base.Exclude; } - set { base.Exclude = value; } + get => base.Exclude; + set => base.Exclude = value; } /// /// Gets the dynamic parameters for the set-item cmdlet. /// - /// /// /// The context under which the command is running. /// - /// /// /// An object representing the dynamic parameters for the cmdlet or null if there /// are none. /// - /// internal override object GetDynamicParameters(CmdletProviderContext context) { if (Path != null && Path.Length > 0) { return InvokeProvider.Item.SetItemDynamicParameters(Path[0], Value, context); } + return InvokeProvider.Item.SetItemDynamicParameters(".", Value, context); } /// - /// Determines if the provider for the specified path supports ShouldProcess + /// Determines if the provider for the specified path supports ShouldProcess. /// /// - protected override bool ProviderSupportsShouldProcess - { - get - { - return base.DoesProviderSupportShouldProcess(_paths); - } - } + protected override bool ProviderSupportsShouldProcess => DoesProviderSupportShouldProcess(_paths); #endregion Command parameters #region Command data @@ -2875,10 +2354,10 @@ protected override void ProcessRecord() pathNotFound)); } } - } // ProcessRecord + } #endregion Command code - } // SetItemCommand + } #endregion SetItemCommand @@ -2887,118 +2366,84 @@ protected override void ProcessRecord() /// /// Removes the specified item using the namespace providers. /// - [Cmdlet(VerbsCommon.Remove, "Item", SupportsShouldProcess = true, DefaultParameterSetName = "Path", SupportsTransactions = true, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113373")] + [Cmdlet(VerbsCommon.Remove, "Item", SupportsShouldProcess = true, DefaultParameterSetName = PathParameterSet, SupportsTransactions = true, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2097103")] public class RemoveItemCommand : CoreCommandWithCredentialsBase { #region Command parameters + private const string PathParameterSet = "Path"; + private const string LiteralPathParameterSet = "LiteralPath"; + /// - /// Gets or sets the path property + /// Gets or sets the path property. /// - [Parameter(Position = 0, ParameterSetName = "Path", + [Parameter(Position = 0, ParameterSetName = PathParameterSet, Mandatory = true, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true)] public string[] Path { - get - { - return _paths; - } - set - { - _paths = value; - } - } // Path + get => _paths; + set => _paths = value; + } /// - /// Gets or sets the literal path parameter to the command + /// Gets or sets the literal path parameter to the command. /// - [Parameter(ParameterSetName = "LiteralPath", + [Parameter(ParameterSetName = LiteralPathParameterSet, Mandatory = true, ValueFromPipeline = false, ValueFromPipelineByPropertyName = true)] - [Alias("PSPath")] + [Alias("PSPath", "LP")] public string[] LiteralPath { - get - { - return _paths; - } // get - + get => _paths; set { base.SuppressWildcardExpansion = true; _paths = value; - } // set - } // LiteralPath + } + } /// - /// Gets or sets the filter property + /// Gets or sets the filter property. /// [Parameter] public override string Filter { - get - { - return base.Filter; - } - set - { - base.Filter = value; - } + get => base.Filter; + set => base.Filter = value; } /// - /// Gets or sets the include property + /// Gets or sets the include property. /// [Parameter] public override string[] Include { - get - { - return base.Include; - } // get - - set - { - base.Include = value; - } // set - } // Include + get => base.Include; + set => base.Include = value; + } /// - /// Gets or sets the exclude property + /// Gets or sets the exclude property. /// [Parameter] public override string[] Exclude { - get - { - return base.Exclude; - } // get - - set - { - base.Exclude = value; - } // set - } // Exclude + get => base.Exclude; + set => base.Exclude = value; + } /// - /// Gets or sets the recurse property + /// Gets or sets the recurse property. /// [Parameter] public SwitchParameter Recurse { - get - { - return _recurse; - } - set - { - _recurse = value; - } - } // Recurse + get => _recurse; + set => _recurse = value; + } /// - /// Gets or sets the force property + /// Gets or sets the force property. /// - /// /// /// Gives the provider guidance on how vigorous it should be about performing /// the operation. If true, the provider should do everything possible to perform @@ -3008,59 +2453,44 @@ public SwitchParameter Recurse /// the destination is read-only, if force is true, the provider should copy over /// the existing read-only file. If force is false, the provider should write an error. /// - /// [Parameter] public override SwitchParameter Force { - get - { - return base.Force; - } - set - { - base.Force = value; - } - } // Force + get => base.Force; + set => base.Force = value; + } /// /// Gets the dynamic parameters for the remove-item cmdlet. /// - /// /// /// The context under which the command is running. /// - /// /// /// An object representing the dynamic parameters for the cmdlet or null if there /// are none. /// - /// internal override object GetDynamicParameters(CmdletProviderContext context) { if (Path != null && Path.Length > 0) { return InvokeProvider.Item.RemoveItemDynamicParameters(Path[0], Recurse, context); } + return InvokeProvider.Item.RemoveItemDynamicParameters(".", Recurse, context); - } // GetDynamicParameters + } /// - /// Determines if the provider for the specified path supports ShouldProcess + /// Determines if the provider for the specified path supports ShouldProcess. /// /// - protected override bool ProviderSupportsShouldProcess - { - get - { - return base.DoesProviderSupportShouldProcess(_paths); - } - } + protected override bool ProviderSupportsShouldProcess => DoesProviderSupportShouldProcess(_paths); #endregion Command parameters #region Command data /// - /// The path used when doing a delete + /// The path used when doing a delete. /// private string[] _paths; @@ -3104,9 +2534,22 @@ protected override void ProcessRecord() new Collection(), null); } + try { resolvedPSPaths = SessionState.Path.GetResolvedPSPathFromPSPath(path, currentContext); + if (SuppressWildcardExpansion == true && resolvedPSPaths.Count == 0) + { + ItemNotFoundException pathNotFound = + new( + path, + "PathNotFound", + SessionStateStrings.PathNotFound); + WriteError(new ErrorRecord( + pathNotFound.ErrorRecord, + pathNotFound)); + continue; + } } finally { @@ -3196,8 +2639,7 @@ protected override void ProcessRecord() if (isCurrentLocationOrAncestor) { PSInvalidOperationException invalidOperation = - (PSInvalidOperationException) - PSTraceSource.NewInvalidOperationException( + (PSInvalidOperationException)PSTraceSource.NewInvalidOperationException( NavigationResources.RemoveItemInUse, resolvedPath.Path); @@ -3253,18 +2695,23 @@ protected override void ProcessRecord() bool shouldRecurse = Recurse; bool treatAsFile = false; - try + + // only check if path is a directory using DirectoryInfo if using FileSystemProvider + if (resolvedPath.Provider.Name.Equals(FileSystemProvider.ProviderName, StringComparison.OrdinalIgnoreCase)) { - System.IO.DirectoryInfo di = new System.IO.DirectoryInfo(providerPath); - if (di != null && (di.Attributes & System.IO.FileAttributes.ReparsePoint) != 0) + try { - shouldRecurse = false; - treatAsFile = true; + System.IO.DirectoryInfo di = new(providerPath); + if (InternalSymbolicLinkLinkCodeMethods.IsReparsePointLikeSymlink(di)) + { + shouldRecurse = false; + treatAsFile = true; + } + } + catch (System.IO.FileNotFoundException) + { + // not a directory } - } - catch (System.IO.FileNotFoundException) - { - // not a directory } if (!treatAsFile && !Recurse && hasChildren) @@ -3280,6 +2727,7 @@ protected override void ProcessRecord() { continue; } + shouldRecurse = true; } @@ -3331,10 +2779,10 @@ protected override void ProcessRecord() } } } - } // ProcessRecord + } #endregion Command code - } // RemoveItemCommand + } #endregion RemoveItemCommand @@ -3344,37 +2792,35 @@ protected override void ProcessRecord() /// Moves an item from the specified location to the specified destination using /// the namespace providers. /// - [Cmdlet(VerbsCommon.Move, "Item", DefaultParameterSetName = "Path", SupportsShouldProcess = true, SupportsTransactions = true, - HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113350")] + [Cmdlet(VerbsCommon.Move, "Item", DefaultParameterSetName = PathParameterSet, SupportsShouldProcess = true, SupportsTransactions = true, + HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2096591")] public class MoveItemCommand : CoreCommandWithCredentialsBase { #region Command parameters + + private const string PathParameterSet = "Path"; + private const string LiteralPathParameterSet = "LiteralPath"; + /// - /// Gets or sets the path property + /// Gets or sets the path property. /// - [Parameter(Position = 0, ParameterSetName = "Path", + [Parameter(Position = 0, ParameterSetName = PathParameterSet, Mandatory = true, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true)] public string[] Path { - get - { - return _paths; - } - set - { - _paths = value; - } - } // Path + get => _paths; + set => _paths = value; + } /// - /// Gets or sets the literal path parameter to the command + /// Gets or sets the literal path parameter to the command. /// - [Parameter(ParameterSetName = "LiteralPath", + [Parameter(ParameterSetName = LiteralPathParameterSet, Mandatory = true, ValueFromPipeline = false, ValueFromPipelineByPropertyName = true)] - [Alias("PSPath")] + [Alias("PSPath", "LP")] public string[] LiteralPath { - get { return _paths; } + get => _paths; set { base.SuppressWildcardExpansion = true; @@ -3383,15 +2829,14 @@ public string[] LiteralPath } /// - /// Gets or sets the destination property + /// Gets or sets the destination property. /// [Parameter(Position = 1, ValueFromPipelineByPropertyName = true)] public string Destination { get; set; } = "."; /// - /// Gets or sets the force property + /// Gets or sets the force property. /// - /// /// /// Gives the provider guidance on how vigorous it should be about performing /// the operation. If true, the provider should do everything possible to perform @@ -3401,42 +2846,41 @@ public string[] LiteralPath /// the destination is read-only, if force is true, the provider should copy over /// the existing read-only file. If force is false, the provider should write an error. /// - /// [Parameter] public override SwitchParameter Force { - get { return base.Force; } - set { base.Force = value; } + get => base.Force; + set => base.Force = value; } /// - /// Gets or sets the filter property + /// Gets or sets the filter property. /// [Parameter] public override string Filter { - get { return base.Filter; } - set { base.Filter = value; } + get => base.Filter; + set => base.Filter = value; } /// - /// Gets or sets the include property + /// Gets or sets the include property. /// [Parameter] public override string[] Include { - get { return base.Include; } - set { base.Include = value; } + get => base.Include; + set => base.Include = value; } /// - /// Gets or sets the exclude property + /// Gets or sets the exclude property. /// [Parameter] public override string[] Exclude { - get { return base.Exclude; } - set { base.Exclude = value; } + get => base.Exclude; + set => base.Exclude = value; } /// @@ -3444,47 +2888,38 @@ public override string[] Exclude /// if the object that is set should be written to the pipeline. /// Defaults to false. /// - /// [Parameter] public SwitchParameter PassThru { - get { return _passThrough; } - set { _passThrough = value; } + get => _passThrough; + set => _passThrough = value; } /// /// Gets the dynamic parameters for the move-item cmdlet. /// - /// /// /// The context under which the command is running. /// - /// /// /// An object representing the dynamic parameters for the cmdlet or null if there /// are none. /// - /// internal override object GetDynamicParameters(CmdletProviderContext context) { if (Path != null && Path.Length > 0) { return InvokeProvider.Item.MoveItemDynamicParameters(Path[0], Destination, context); } + return InvokeProvider.Item.MoveItemDynamicParameters(".", Destination, context); } /// - /// Determines if the provider for the specified path supports ShouldProcess + /// Determines if the provider for the specified path supports ShouldProcess. /// /// - protected override bool ProviderSupportsShouldProcess - { - get - { - return base.DoesProviderSupportShouldProcess(_paths); - } - } + protected override bool ProviderSupportsShouldProcess => DoesProviderSupportShouldProcess(_paths); #endregion Command parameters @@ -3507,7 +2942,7 @@ protected override bool ProviderSupportsShouldProcess private Collection GetResolvedPaths(string path) { - Collection results = new Collection(); + Collection results = new(); try { results = SessionState.Path.GetResolvedPSPathFromPSPath(path, CmdletProviderContext); @@ -3545,7 +2980,7 @@ private Collection GetResolvedPaths(string path) } /// - /// Moves the specified item to the specified destination + /// Moves the specified item to the specified destination. /// protected override void ProcessRecord() { @@ -3553,7 +2988,7 @@ protected override void ProcessRecord() { if (base.SuppressWildcardExpansion) { - MoveItem(path); + MoveItem(path, literalPath: true); } else { @@ -3562,210 +2997,202 @@ protected override void ProcessRecord() foreach (PathInfo resolvedPathInfo in resolvedPaths) { string resolvedPath = resolvedPathInfo.Path; - MoveItem(resolvedPath); + MoveItem(resolvedPath, literalPath: true); } } } - } // ProcessRecord + } - private void MoveItem(string path) + private void MoveItem(string path, bool literalPath = false) { CmdletProviderContext currentContext = CmdletProviderContext; + currentContext.SuppressWildcardExpansion = literalPath; - do + try { - try - { - string escapedPath = path; - if (!base.SuppressWildcardExpansion) { escapedPath = WildcardPattern.Escape(path); } - if (!InvokeProvider.Item.Exists(escapedPath, currentContext)) - { - PSInvalidOperationException invalidOperation = - (PSInvalidOperationException) - PSTraceSource.NewInvalidOperationException( - NavigationResources.MoveItemDoesntExist, - path); - - WriteError( - new ErrorRecord( - invalidOperation.ErrorRecord, - invalidOperation)); - continue; - } - } - catch (PSNotSupportedException notSupported) - { - WriteError( - new ErrorRecord( - notSupported.ErrorRecord, - notSupported)); - continue; - } - catch (DriveNotFoundException driveNotFound) - { - WriteError( - new ErrorRecord( - driveNotFound.ErrorRecord, - driveNotFound)); - continue; - } - catch (ProviderNotFoundException providerNotFound) - { - WriteError( - new ErrorRecord( - providerNotFound.ErrorRecord, - providerNotFound)); - continue; - } - catch (ItemNotFoundException pathNotFound) - { - WriteError( - new ErrorRecord( - pathNotFound.ErrorRecord, - pathNotFound)); - continue; - } - - - // See if the item to be moved is in use. - bool isCurrentLocationOrAncestor = false; - try - { - isCurrentLocationOrAncestor = SessionState.Path.IsCurrentLocationOrAncestor(path, currentContext); - } - catch (PSNotSupportedException notSupported) - { - WriteError( - new ErrorRecord( - notSupported.ErrorRecord, - notSupported)); - continue; - } - catch (DriveNotFoundException driveNotFound) - { - WriteError( - new ErrorRecord( - driveNotFound.ErrorRecord, - driveNotFound)); - continue; - } - catch (ProviderNotFoundException providerNotFound) - { - WriteError( - new ErrorRecord( - providerNotFound.ErrorRecord, - providerNotFound)); - continue; - } - catch (ItemNotFoundException pathNotFound) - { - WriteError( - new ErrorRecord( - pathNotFound.ErrorRecord, - pathNotFound)); - continue; - } - - if (isCurrentLocationOrAncestor) + if (!InvokeProvider.Item.Exists(path, currentContext)) { PSInvalidOperationException invalidOperation = - (PSInvalidOperationException) - PSTraceSource.NewInvalidOperationException( - NavigationResources.MoveItemInUse, + (PSInvalidOperationException)PSTraceSource.NewInvalidOperationException( + NavigationResources.MoveItemDoesntExist, path); WriteError( new ErrorRecord( invalidOperation.ErrorRecord, invalidOperation)); - continue; + return; } + } + catch (PSNotSupportedException notSupported) + { + WriteError( + new ErrorRecord( + notSupported.ErrorRecord, + notSupported)); + return; + } + catch (DriveNotFoundException driveNotFound) + { + WriteError( + new ErrorRecord( + driveNotFound.ErrorRecord, + driveNotFound)); + return; + } + catch (ProviderNotFoundException providerNotFound) + { + WriteError( + new ErrorRecord( + providerNotFound.ErrorRecord, + providerNotFound)); + return; + } + catch (ItemNotFoundException pathNotFound) + { + WriteError( + new ErrorRecord( + pathNotFound.ErrorRecord, + pathNotFound)); + return; + } - // Default to the CmdletProviderContext that will direct output to - // the pipeline. - - CmdletProviderContext currentCommandContext = currentContext; - currentCommandContext.PassThru = PassThru; - - tracer.WriteLine("Moving {0} to {1}", path, Destination); + // See if the item to be moved is in use. + bool isCurrentLocationOrAncestor = false; + try + { + isCurrentLocationOrAncestor = SessionState.Path.IsCurrentLocationOrAncestor(path, currentContext); + } + catch (PSNotSupportedException notSupported) + { + WriteError( + new ErrorRecord( + notSupported.ErrorRecord, + notSupported)); + return; + } + catch (DriveNotFoundException driveNotFound) + { + WriteError( + new ErrorRecord( + driveNotFound.ErrorRecord, + driveNotFound)); + return; + } + catch (ProviderNotFoundException providerNotFound) + { + WriteError( + new ErrorRecord( + providerNotFound.ErrorRecord, + providerNotFound)); + return; + } + catch (ItemNotFoundException pathNotFound) + { + WriteError( + new ErrorRecord( + pathNotFound.ErrorRecord, + pathNotFound)); + return; + } - try - { - // Now do the move - string escapedPath = path; - if (!base.SuppressWildcardExpansion) { escapedPath = WildcardPattern.Escape(path); } - InvokeProvider.Item.Move(escapedPath, Destination, currentCommandContext); - } - catch (PSNotSupportedException notSupported) - { - WriteError( - new ErrorRecord( - notSupported.ErrorRecord, - notSupported)); - continue; - } - catch (DriveNotFoundException driveNotFound) - { - WriteError( - new ErrorRecord( - driveNotFound.ErrorRecord, - driveNotFound)); - continue; - } - catch (ProviderNotFoundException providerNotFound) - { - WriteError( - new ErrorRecord( - providerNotFound.ErrorRecord, - providerNotFound)); - continue; - } - catch (ItemNotFoundException pathNotFound) - { - WriteError( - new ErrorRecord( - pathNotFound.ErrorRecord, - pathNotFound)); - continue; - } + if (isCurrentLocationOrAncestor) + { + PSInvalidOperationException invalidOperation = + (PSInvalidOperationException)PSTraceSource.NewInvalidOperationException( + NavigationResources.MoveItemInUse, + path); + + WriteError( + new ErrorRecord( + invalidOperation.ErrorRecord, + invalidOperation)); + return; + } + + // Default to the CmdletProviderContext that will direct output to + // the pipeline. + + currentContext.PassThru = PassThru; + + tracer.WriteLine("Moving {0} to {1}", path, Destination); + + try + { + // Now do the move + InvokeProvider.Item.Move(path, Destination, currentContext); + } + catch (PSNotSupportedException notSupported) + { + WriteError( + new ErrorRecord( + notSupported.ErrorRecord, + notSupported)); + return; + } + catch (DriveNotFoundException driveNotFound) + { + WriteError( + new ErrorRecord( + driveNotFound.ErrorRecord, + driveNotFound)); + return; + } + catch (ProviderNotFoundException providerNotFound) + { + WriteError( + new ErrorRecord( + providerNotFound.ErrorRecord, + providerNotFound)); + return; + } + catch (ItemNotFoundException pathNotFound) + { + WriteError( + new ErrorRecord( + pathNotFound.ErrorRecord, + pathNotFound)); + return; } - while (false); } #endregion Command code - } // MoveItemCommand + } #endregion MoveItemCommand #region RenameItemCommand /// - /// Renames a specified item to a new name using the namespace providers + /// Renames a specified item to a new name using the namespace providers. /// - [Cmdlet(VerbsCommon.Rename, "Item", SupportsShouldProcess = true, SupportsTransactions = true, DefaultParameterSetName = "ByPath", - HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113382")] + [Cmdlet(VerbsCommon.Rename, "Item", SupportsShouldProcess = true, SupportsTransactions = true, DefaultParameterSetName = ByPathParameterSet, + HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2097153")] public class RenameItemCommand : CoreCommandWithCredentialsBase { #region Command parameters + private const string ByPathParameterSet = "ByPath"; + private const string ByLiteralPathParameterSet = "ByLiteralPath"; + /// - /// Gets or sets the path property + /// Gets or sets the path property. /// - [Parameter(Position = 0, Mandatory = true, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true, ParameterSetName = "ByPath")] + [Parameter(Position = 0, Mandatory = true, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true, ParameterSetName = ByPathParameterSet)] public string Path { - get { return _path; } - set { _path = value; } + get => _path; + set => _path = value; } /// - /// Gets or sets the literal path property + /// Gets or sets the literal path property. /// - [Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true, ParameterSetName = "ByLiteralPath")] - [Alias("PSPath")] + [Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true, ParameterSetName = ByLiteralPathParameterSet)] + [Alias("PSPath", "LP")] public string LiteralPath { - get { return _path; } + get => _path; set { _path = value; @@ -3774,15 +3201,14 @@ public string LiteralPath } /// - /// Gets or sets the newName property + /// Gets or sets the newName property. /// [Parameter(Position = 1, Mandatory = true, ValueFromPipelineByPropertyName = true)] public string NewName { get; set; } /// - /// Gets or sets the force property + /// Gets or sets the force property. /// - /// /// /// Gives the provider guidance on how vigorous it should be about performing /// the operation. If true, the provider should do everything possible to perform @@ -3792,57 +3218,45 @@ public string LiteralPath /// the destination is read-only, if force is true, the provider should copy over /// the existing read-only file. If force is false, the provider should write an error. /// - /// [Parameter] public override SwitchParameter Force { - get { return base.Force; } - set { base.Force = value; } + get => base.Force; + set => base.Force = value; } - /// /// Gets or sets the pass through property which determines /// if the object that is set should be written to the pipeline. /// Defaults to false. /// - /// [Parameter] public SwitchParameter PassThru { - get { return _passThrough; } - set { _passThrough = value; } + get => _passThrough; + set => _passThrough = value; } /// /// Gets the dynamic parameters for the rename-item cmdlet. /// - /// /// /// The context under which the command is running. /// - /// /// /// An object representing the dynamic parameters for the cmdlet or null if there /// are none. /// - /// internal override object GetDynamicParameters(CmdletProviderContext context) { return InvokeProvider.Item.RenameItemDynamicParameters(Path, NewName, context); } /// - /// Determines if the provider for the specified path supports ShouldProcess + /// Determines if the provider for the specified path supports ShouldProcess. /// /// - protected override bool ProviderSupportsShouldProcess - { - get - { - return base.DoesProviderSupportShouldProcess(new string[] { _path }); - } - } + protected override bool ProviderSupportsShouldProcess => DoesProviderSupportShouldProcess(new string[] { _path }); #endregion Command parameters @@ -3863,22 +3277,85 @@ protected override bool ProviderSupportsShouldProcess #region Command code + private Collection GetResolvedPaths(string path) + { + Collection results = null; + try + { + results = SessionState.Path.GetResolvedPSPathFromPSPath(path, CmdletProviderContext); + } + catch (PSNotSupportedException notSupported) + { + WriteError( + new ErrorRecord( + notSupported.ErrorRecord, + notSupported)); + } + catch (DriveNotFoundException driveNotFound) + { + WriteError( + new ErrorRecord( + driveNotFound.ErrorRecord, + driveNotFound)); + } + catch (ProviderNotFoundException providerNotFound) + { + WriteError( + new ErrorRecord( + providerNotFound.ErrorRecord, + providerNotFound)); + } + catch (ItemNotFoundException pathNotFound) + { + WriteError( + new ErrorRecord( + pathNotFound.ErrorRecord, + pathNotFound)); + } + + return results; + } + /// - /// Moves the specified item to the specified destination + /// Moves the specified item to the specified destination. /// protected override void ProcessRecord() + { + if (SuppressWildcardExpansion) + { + RenameItem(Path, literalPath: true); + return; + } + + Collection resolvedPaths = GetResolvedPaths(Path); + if (resolvedPaths == null) + { + return; + } + + if (resolvedPaths.Count == 1) + { + RenameItem(resolvedPaths[0].Path, literalPath: true); + } + else + { + RenameItem(WildcardPattern.Unescape(Path), literalPath: true); + } + } + + private void RenameItem(string path, bool literalPath = false) { CmdletProviderContext currentContext = CmdletProviderContext; + currentContext.SuppressWildcardExpansion = literalPath; try { - if (!InvokeProvider.Item.Exists(Path, currentContext)) + if (!InvokeProvider.Item.Exists(path, currentContext)) { PSInvalidOperationException invalidOperation = - (PSInvalidOperationException) - PSTraceSource.NewInvalidOperationException( + (PSInvalidOperationException)PSTraceSource.NewInvalidOperationException( NavigationResources.RenameItemDoesntExist, - Path); + path); WriteError( new ErrorRecord( @@ -3924,7 +3401,7 @@ protected override void ProcessRecord() bool isCurrentLocationOrAncestor = false; try { - isCurrentLocationOrAncestor = SessionState.Path.IsCurrentLocationOrAncestor(_path, currentContext); + isCurrentLocationOrAncestor = SessionState.Path.IsCurrentLocationOrAncestor(path, currentContext); } catch (PSNotSupportedException notSupported) { @@ -3962,10 +3439,9 @@ protected override void ProcessRecord() if (isCurrentLocationOrAncestor) { PSInvalidOperationException invalidOperation = - (PSInvalidOperationException) - PSTraceSource.NewInvalidOperationException( + (PSInvalidOperationException)PSTraceSource.NewInvalidOperationException( NavigationResources.RenamedItemInUse, - Path); + path); WriteError( new ErrorRecord( @@ -3976,15 +3452,14 @@ protected override void ProcessRecord() // Default to the CmdletProviderContext that will direct output to // the pipeline. - currentContext.PassThru = PassThru; - tracer.WriteLine("Rename {0} to {1}", Path, NewName); + tracer.WriteLine("Rename {0} to {1}", path, NewName); try { // Now do the rename - InvokeProvider.Item.Rename(Path, NewName, currentContext); + InvokeProvider.Item.Rename(path, NewName, currentContext); } catch (PSNotSupportedException notSupported) { @@ -4018,49 +3493,47 @@ protected override void ProcessRecord() pathNotFound)); return; } - } // ProcessRecord + } #endregion Command code - } // RenameItemCommand + } #endregion RenameItemCommand #region CopyItemCommand /// - /// Copies a specified item to a new location using the namespace providers + /// Copies a specified item to a new location using the namespace providers. /// - [Cmdlet(VerbsCommon.Copy, "Item", DefaultParameterSetName = "Path", SupportsShouldProcess = true, SupportsTransactions = true, - HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113292")] + [Cmdlet(VerbsCommon.Copy, "Item", DefaultParameterSetName = PathParameterSet, SupportsShouldProcess = true, SupportsTransactions = true, + HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2096990")] public class CopyItemCommand : CoreCommandWithCredentialsBase { #region Command parameters + + private const string PathParameterSet = "Path"; + private const string LiteralPathParameterSet = "LiteralPath"; + /// - /// Gets or sets the path property + /// Gets or sets the path property. /// - [Parameter(Position = 0, ParameterSetName = "Path", + [Parameter(Position = 0, ParameterSetName = PathParameterSet, Mandatory = true, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true)] public string[] Path { - get - { - return _paths; - } - set - { - _paths = value; - } - } // Path + get => _paths; + set => _paths = value; + } /// - /// Gets or sets the literal path parameter to the command + /// Gets or sets the literal path parameter to the command. /// - [Parameter(ParameterSetName = "LiteralPath", + [Parameter(ParameterSetName = LiteralPathParameterSet, Mandatory = true, ValueFromPipeline = false, ValueFromPipelineByPropertyName = true)] - [Alias("PSPath")] + [Alias("PSPath", "LP")] public string[] LiteralPath { - get { return _paths; } + get => _paths; set { base.SuppressWildcardExpansion = true; @@ -4069,18 +3542,18 @@ public string[] LiteralPath } /// - /// Gets or sets the destination property + /// Gets or sets the destination property. /// [Parameter(Position = 1, ValueFromPipelineByPropertyName = true)] public string Destination { get; set; } /// - /// Gets or sets the container property + /// Gets or sets the container property. /// [Parameter] public SwitchParameter Container { - get { return _container; } + get => _container; set { _containerSpecified = true; @@ -4089,9 +3562,8 @@ public SwitchParameter Container } /// - /// Gets or sets the force property + /// Gets or sets the force property. /// - /// /// /// Gives the provider guidance on how vigorous it should be about performing /// the operation. If true, the provider should do everything possible to perform @@ -4101,56 +3573,54 @@ public SwitchParameter Container /// the destination is read-only, if force is true, the provider should copy over /// the existing read-only file. If force is false, the provider should write an error. /// - /// [Parameter] public override SwitchParameter Force { - get { return base.Force; } - set { base.Force = value; } + get => base.Force; + set => base.Force = value; } /// - /// Gets or sets the filter property + /// Gets or sets the filter property. /// [Parameter] public override string Filter { - get { return base.Filter; } - set { base.Filter = value; } + get => base.Filter; + set => base.Filter = value; } /// - /// Gets or sets the include property + /// Gets or sets the include property. /// [Parameter] public override string[] Include { - get { return base.Include; } - set { base.Include = value; } + get => base.Include; + set => base.Include = value; } /// - /// Gets or sets the exclude property + /// Gets or sets the exclude property. /// [Parameter] public override string[] Exclude { - get { return base.Exclude; } - set { base.Exclude = value; } + get => base.Exclude; + set => base.Exclude = value; } /// - /// Gets or sets the recurse property + /// Gets or sets the recurse property. /// [Parameter] public SwitchParameter Recurse { - get { return _recurse; } + get => _recurse; set { _recurse = value; - // If -Container is not specified but -Recurse // is, then -Container takes on the same value // as -Recurse @@ -4166,47 +3636,38 @@ public SwitchParameter Recurse /// if the object that is set should be written to the pipeline. /// Defaults to false. /// - /// [Parameter] public SwitchParameter PassThru { - get { return _passThrough; } - set { _passThrough = value; } + get => _passThrough; + set => _passThrough = value; } /// /// Gets the dynamic parameters for the copy-item cmdlet. /// - /// /// /// The context under which the command is running. /// - /// /// /// An object representing the dynamic parameters for the cmdlet or null if there /// are none. /// - /// internal override object GetDynamicParameters(CmdletProviderContext context) { if (Path != null && Path.Length > 0) { return InvokeProvider.Item.CopyItemDynamicParameters(Path[0], Destination, Recurse, context); } + return InvokeProvider.Item.CopyItemDynamicParameters(".", Destination, Recurse, context); - } // GetDynamicParameters + } /// - /// Determines if the provider for the specified path supports ShouldProcess + /// Determines if the provider for the specified path supports ShouldProcess. /// /// - protected override bool ProviderSupportsShouldProcess - { - get - { - return base.DoesProviderSupportShouldProcess(_paths); - } - } + protected override bool ProviderSupportsShouldProcess => DoesProviderSupportShouldProcess(_paths); #endregion Command parameters @@ -4240,7 +3701,7 @@ protected override bool ProviderSupportsShouldProcess #region Command code /// - /// Copies the specified item(s) to the specified destination + /// Copies the specified item(s) to the specified destination. /// protected override void ProcessRecord() { @@ -4291,64 +3752,57 @@ protected override void ProcessRecord() continue; } } - } // ProcessRecord + } #endregion Command code - } // CopyItemCommand + } #endregion CopyItemCommand #region ClearItemCommand /// - /// Clears an item at the specified location + /// Clears an item at the specified location. /// - [Cmdlet(VerbsCommon.Clear, "Item", DefaultParameterSetName = "Path", SupportsShouldProcess = true, SupportsTransactions = true, - HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113283")] + [Cmdlet(VerbsCommon.Clear, "Item", DefaultParameterSetName = PathParameterSet, SupportsShouldProcess = true, SupportsTransactions = true, + HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2096491")] public class ClearItemCommand : CoreCommandWithCredentialsBase { #region Command parameters + + private const string PathParameterSet = "Path"; + private const string LiteralPathParameterSet = "LiteralPath"; + /// - /// Gets or sets the path property + /// Gets or sets the path property. /// - [Parameter(Position = 0, ParameterSetName = "Path", + [Parameter(Position = 0, ParameterSetName = PathParameterSet, Mandatory = true, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true)] public string[] Path { - get - { - return _paths; - } - set - { - _paths = value; - } - } // Path + get => _paths; + set => _paths = value; + } /// - /// Gets or sets the literal path parameter to the command + /// Gets or sets the literal path parameter to the command. /// - [Parameter(ParameterSetName = "LiteralPath", + [Parameter(ParameterSetName = LiteralPathParameterSet, Mandatory = true, ValueFromPipeline = false, ValueFromPipelineByPropertyName = true)] - [Alias("PSPath")] + [Alias("PSPath", "LP")] public string[] LiteralPath { - get - { - return _paths; - } // get - + get => _paths; set { base.SuppressWildcardExpansion = true; _paths = value; - } // set - } // LiteralPath + } + } /// - /// Gets or sets the force property + /// Gets or sets the force property. /// - /// /// /// Gives the provider guidance on how vigorous it should be about performing /// the operation. If true, the provider should do everything possible to perform @@ -4358,103 +3812,68 @@ public string[] LiteralPath /// the destination is read-only, if force is true, the provider should copy over /// the existing read-only file. If force is false, the provider should write an error. /// - /// [Parameter] public override SwitchParameter Force { - get - { - return base.Force; - } - set - { - base.Force = value; - } - } // Force + get => base.Force; + set => base.Force = value; + } /// - /// Gets or sets the filter property + /// Gets or sets the filter property. /// [Parameter] public override string Filter { - get - { - return base.Filter; - } - set - { - base.Filter = value; - } - } // Filter + get => base.Filter; + set => base.Filter = value; + } /// - /// Gets or sets the include property + /// Gets or sets the include property. /// [Parameter] public override string[] Include { - get - { - return base.Include; - } // get - - set - { - base.Include = value; - } // set - } // Include + get => base.Include; + set => base.Include = value; + } /// - /// Gets or sets the exclude property + /// Gets or sets the exclude property. /// [Parameter] public override string[] Exclude { - get - { - return base.Exclude; - } // get - - set - { - base.Exclude = value; - } // set - } // Exclude + get => base.Exclude; + set => base.Exclude = value; + } /// /// Gets the dynamic parameters for the clear-item cmdlet. /// - /// /// /// The context under which the command is running. /// - /// /// /// An object representing the dynamic parameters for the cmdlet or null if there /// are none. /// - /// internal override object GetDynamicParameters(CmdletProviderContext context) { if (Path != null && Path.Length > 0) { return InvokeProvider.Item.ClearItemDynamicParameters(Path[0], context); } + return InvokeProvider.Item.ClearItemDynamicParameters(".", context); - } // GetDynamicParameters + } /// - /// Determines if the provider for the specified path supports ShouldProcess + /// Determines if the provider for the specified path supports ShouldProcess. /// /// - protected override bool ProviderSupportsShouldProcess - { - get - { - return base.DoesProviderSupportShouldProcess(_paths); - } - } + protected override bool ProviderSupportsShouldProcess => DoesProviderSupportShouldProcess(_paths); #endregion Command parameters @@ -4470,7 +3889,7 @@ protected override bool ProviderSupportsShouldProcess #region Command code /// - /// Clears the specified item + /// Clears the specified item. /// protected override void ProcessRecord() { @@ -4522,143 +3941,109 @@ protected override void ProcessRecord() continue; } } - } // ProcessRecord - #endregion Command code + } - } // ClearItemCommand + #endregion Command code + } #endregion ClearItemCommand #region InvokeItemCommand /// - /// Invokes an item at the specified location + /// Invokes an item at the specified location. /// - [Cmdlet(VerbsLifecycle.Invoke, "Item", DefaultParameterSetName = "Path", SupportsShouldProcess = true, SupportsTransactions = true, - HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113345")] + [Cmdlet(VerbsLifecycle.Invoke, "Item", DefaultParameterSetName = PathParameterSet, SupportsShouldProcess = true, SupportsTransactions = true, + HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2096590")] public class InvokeItemCommand : CoreCommandWithCredentialsBase { #region Command parameters + + private const string PathParameterSet = "Path"; + private const string LiteralPathParameterSet = "LiteralPath"; + /// - /// Gets or sets the path property + /// Gets or sets the path property. /// - [Parameter(Position = 0, ParameterSetName = "Path", + [Parameter(Position = 0, ParameterSetName = PathParameterSet, Mandatory = true, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true)] public string[] Path { - get - { - return _paths; - } - set - { - _paths = value; - } - } // Path + get => _paths; + set => _paths = value; + } /// - /// Gets or sets the literal path parameter to the command + /// Gets or sets the literal path parameter to the command. /// - [Parameter(ParameterSetName = "LiteralPath", + [Parameter(ParameterSetName = LiteralPathParameterSet, Mandatory = true, ValueFromPipeline = false, ValueFromPipelineByPropertyName = true)] - [Alias("PSPath")] + [Alias("PSPath", "LP")] public string[] LiteralPath { - get - { - return _paths; - } // get - + get => _paths; set { base.SuppressWildcardExpansion = true; _paths = value; - } // set - } // LiteralPath + } + } /// - /// Gets or sets the filter property + /// Gets or sets the filter property. /// [Parameter] public override string Filter { - get - { - return base.Filter; - } - set - { - base.Filter = value; - } - } // Filter + get => base.Filter; + set => base.Filter = value; + } /// - /// Gets or sets the include property + /// Gets or sets the include property. /// [Parameter] public override string[] Include { - get - { - return base.Include; - } // get - - set - { - base.Include = value; - } // set - } // Include + get => base.Include; + set => base.Include = value; + } /// - /// Gets or sets the exclude property + /// Gets or sets the exclude property. /// [Parameter] public override string[] Exclude { - get - { - return base.Exclude; - } // get - - set - { - base.Exclude = value; - } // set - } // Exclude + get => base.Exclude; + set => base.Exclude = value; + } /// /// Gets the dynamic parameters for the invoke-item cmdlet. /// - /// /// /// The context under which the command is running. /// - /// /// /// An object representing the dynamic parameters for the cmdlet or null if there /// are none. /// - /// internal override object GetDynamicParameters(CmdletProviderContext context) { if (Path != null && Path.Length > 0) { return InvokeProvider.Item.InvokeItemDynamicParameters(Path[0], context); } + return InvokeProvider.Item.InvokeItemDynamicParameters(".", context); - } // GetDynamicParameters + } /// - /// Determines if the provider for the specified path supports ShouldProcess + /// Determines if the provider for the specified path supports ShouldProcess. /// /// - protected override bool ProviderSupportsShouldProcess - { - get - { - return base.DoesProviderSupportShouldProcess(_paths); - } - } + protected override bool ProviderSupportsShouldProcess => DoesProviderSupportShouldProcess(_paths); #endregion Command parameters @@ -4674,7 +4059,7 @@ protected override bool ProviderSupportsShouldProcess #region Command code /// - /// Invokes the specified item + /// Invokes the specified item. /// protected override void ProcessRecord() { @@ -4720,10 +4105,10 @@ protected override void ProcessRecord() continue; } } - } // ProcessRecord + } #endregion Command code - } // InvokeItemCommand + } #endregion InvokeItemCommand @@ -4734,9 +4119,9 @@ protected override void ProcessRecord() #region GetProviderCommand /// - /// Gets a core command provider by name + /// Gets a core command provider by name. /// - [Cmdlet(VerbsCommon.Get, "PSProvider", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113329")] + [Cmdlet(VerbsCommon.Get, "PSProvider", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2096816")] [OutputType(typeof(ProviderInfo))] public class GetPSProviderCommand : CoreCommandBase { @@ -4745,13 +4130,12 @@ public class GetPSProviderCommand : CoreCommandBase /// /// Gets or sets the provider that will be removed. /// - /// [Parameter(Position = 0, ValueFromPipelineByPropertyName = true)] - [ValidateNotNullOrEmpty()] + [ValidateNotNullOrEmpty] public string[] PSProvider { - get { return _provider; } - set { _provider = value ?? Utils.EmptyArray(); } + get => _provider; + set => _provider = value ?? Array.Empty(); } #endregion Command parameters @@ -4760,7 +4144,7 @@ public string[] PSProvider /// /// The string ID of the provider to remove. /// - private string[] _provider = new string[0]; + private string[] _provider = Array.Empty(); #endregion Command data @@ -4771,9 +4155,7 @@ public string[] PSProvider /// protected override void ProcessRecord() { - if (PSProvider == null || - (PSProvider != null && - PSProvider.Length == 0)) + if (PSProvider == null || PSProvider.Length == 0) { // Get all the providers @@ -4831,13 +4213,12 @@ protected override void ProcessRecord() } } } - } // ProcessRecord + } #endregion Command code - } // GetProviderCommand + } #endregion GetProviderCommand #endregion Provider commands -} // namespace Microsoft.PowerShell.Commands - +} diff --git a/src/Microsoft.PowerShell.Commands.Management/commands/management/NewPropertyCommand.cs b/src/Microsoft.PowerShell.Commands.Management/commands/management/NewPropertyCommand.cs index e89d9db2284..fb134ce7a11 100644 --- a/src/Microsoft.PowerShell.Commands.Management/commands/management/NewPropertyCommand.cs +++ b/src/Microsoft.PowerShell.Commands.Management/commands/management/NewPropertyCommand.cs @@ -1,9 +1,11 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; using System.Management.Automation; -using Dbg = System.Management.Automation; +using System.Management.Automation.Language; namespace Microsoft.PowerShell.Commands { @@ -11,13 +13,13 @@ namespace Microsoft.PowerShell.Commands /// A command to create a new property on an object. /// [Cmdlet(VerbsCommon.New, "ItemProperty", DefaultParameterSetName = "Path", SupportsShouldProcess = true, SupportsTransactions = true, - HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113354")] + HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2096813")] public class NewItemPropertyCommand : ItemPropertyCommandBase { #region Parameters /// - /// Gets or sets the path parameter to the command + /// Gets or sets the path parameter to the command. /// [Parameter(Position = 0, ParameterSetName = "Path", Mandatory = true)] public string[] Path @@ -25,38 +27,37 @@ public string[] Path get { return paths; - } // get + } set { paths = value; - } // set - } // Path + } + } /// - /// Gets or sets the literal path parameter to the command + /// Gets or sets the literal path parameter to the command. /// [Parameter(ParameterSetName = "LiteralPath", Mandatory = true, ValueFromPipeline = false, ValueFromPipelineByPropertyName = true)] - [Alias("PSPath")] + [Alias("PSPath", "LP")] public string[] LiteralPath { get { return paths; - } // get + } set { base.SuppressWildcardExpansion = true; paths = value; - } // set - } // LiteralPath + } + } /// - /// The name of the property to create on the item + /// The name of the property to create on the item. /// - /// [Parameter(Mandatory = true, Position = 1, ValueFromPipelineByPropertyName = true)] [Alias("PSProperty")] public string Name { get; set; } @@ -64,22 +65,22 @@ public string[] LiteralPath /// /// The type of the property to create on the item. /// - /// [Parameter(ValueFromPipelineByPropertyName = true)] [Alias("Type")] +#if !UNIX + [ArgumentCompleter(typeof(PropertyTypeArgumentCompleter))] +#endif public string PropertyType { get; set; } /// /// The value of the property to create on the item. /// - /// [Parameter(ValueFromPipelineByPropertyName = true)] public object Value { get; set; } /// - /// Gets or sets the force property + /// Gets or sets the force property. /// - /// /// /// Gives the provider guidance on how vigorous it should be about performing /// the operation. If true, the provider should do everything possible to perform @@ -89,7 +90,6 @@ public string[] LiteralPath /// the destination is read-only, if force is true, the provider should copy over /// the existing read-only file. If force is false, the provider should write an error. /// - /// [Parameter] public override SwitchParameter Force { @@ -97,35 +97,34 @@ public override SwitchParameter Force { return base.Force; } + set { base.Force = value; } - } // Force + } /// /// A virtual method for retrieving the dynamic parameters for a cmdlet. Derived cmdlets /// that require dynamic parameters should override this method and return the /// dynamic parameter object. /// - /// /// /// The context under which the command is running. /// - /// /// /// An object representing the dynamic parameters for the cmdlet or null if there /// are none. /// - /// internal override object GetDynamicParameters(CmdletProviderContext context) { if (Path != null && Path.Length > 0) { return InvokeProvider.Property.NewPropertyDynamicParameters(Path[0], Name, PropertyType, Value, context); } + return InvokeProvider.Property.NewPropertyDynamicParameters(".", Name, PropertyType, Value, context); - } // GetDynamicParameters + } #endregion Parameters @@ -136,7 +135,7 @@ internal override object GetDynamicParameters(CmdletProviderContext context) #region Command code /// - /// Creates the property on the item + /// Creates the property on the item. /// protected override void ProcessRecord() { @@ -179,9 +178,118 @@ protected override void ProcessRecord() continue; } } - } // ProcessRecord + } #endregion Command code + } + +#if !UNIX + /// + /// Provides argument completion for PropertyType parameter. + /// + public class PropertyTypeArgumentCompleter : IArgumentCompleter + { + private static readonly CompletionHelpers.CompletionDisplayInfoMapper RegistryPropertyTypeDisplayInfoMapper = registryPropertyType => registryPropertyType switch + { + "String" => ( + ToolTip: TabCompletionStrings.RegistryStringToolTip, + ListItemText: "String"), + "ExpandString" => ( + ToolTip: TabCompletionStrings.RegistryExpandStringToolTip, + ListItemText: "ExpandString"), + "Binary" => ( + ToolTip: TabCompletionStrings.RegistryBinaryToolTip, + ListItemText: "Binary"), + "DWord" => ( + ToolTip: TabCompletionStrings.RegistryDWordToolTip, + ListItemText: "DWord"), + "MultiString" => ( + ToolTip: TabCompletionStrings.RegistryMultiStringToolTip, + ListItemText: "MultiString"), + "QWord" => ( + ToolTip: TabCompletionStrings.RegistryQWordToolTip, + ListItemText: "QWord"), + _ => ( + ToolTip: TabCompletionStrings.RegistryUnknownToolTip, + ListItemText: "Unknown"), + }; + + private static readonly IReadOnlyList s_RegistryPropertyTypes = new List(capacity: 7) + { + "String", + "ExpandString", + "Binary", + "DWord", + "MultiString", + "QWord", + "Unknown" + }; + + /// + /// Returns completion results for PropertyType parameter. + /// + /// The command name. + /// The parameter name. + /// The word to complete. + /// The command AST. + /// The fake bound parameters. + /// List of Completion Results. + public IEnumerable CompleteArgument( + string commandName, + string parameterName, + string wordToComplete, + CommandAst commandAst, + IDictionary fakeBoundParameters) + => IsRegistryProvider(fakeBoundParameters) + ? CompletionHelpers.GetMatchingResults( + wordToComplete, + possibleCompletionValues: s_RegistryPropertyTypes, + displayInfoMapper: RegistryPropertyTypeDisplayInfoMapper, + resultType: CompletionResultType.ParameterValue) + : []; + + /// + /// Checks if parameter paths are from Registry provider. + /// + /// The fake bound parameters. + /// Boolean indicating if paths are from Registry Provider. + private static bool IsRegistryProvider(IDictionary fakeBoundParameters) + { + Collection paths; + + if (fakeBoundParameters.Contains("Path")) + { + paths = ResolvePath(fakeBoundParameters["Path"], isLiteralPath: false); + } + else if (fakeBoundParameters.Contains("LiteralPath")) + { + paths = ResolvePath(fakeBoundParameters["LiteralPath"], isLiteralPath: true); + } + else + { + paths = ResolvePath(@".\", isLiteralPath: false); + } + + return paths.Count > 0 && paths[0].Provider.NameEquals("Registry"); + } + + /// + /// Resolve path or literal path using Resolve-Path. + /// + /// The path to resolve. + /// Specifies if path is literal path. + /// Collection of Pathinfo objects. + private static Collection ResolvePath(object path, bool isLiteralPath) + { + using var ps = System.Management.Automation.PowerShell.Create(RunspaceMode.CurrentRunspace); + + ps.AddCommand("Microsoft.PowerShell.Management\\Resolve-Path"); + ps.AddParameter(isLiteralPath ? "LiteralPath" : "Path", path); + + Collection output = ps.Invoke(); - } // NewItemPropertyCommand -} // namespace Microsoft.PowerShell.Commands + return output; + } + } +#endif +} diff --git a/src/Microsoft.PowerShell.Commands.Management/commands/management/ParsePathCommand.cs b/src/Microsoft.PowerShell.Commands.Management/commands/management/ParsePathCommand.cs index a1f29117de4..ca616301ebb 100644 --- a/src/Microsoft.PowerShell.Commands.Management/commands/management/ParsePathCommand.cs +++ b/src/Microsoft.PowerShell.Commands.Management/commands/management/ParsePathCommand.cs @@ -1,21 +1,21 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. using System; using System.Collections.ObjectModel; using System.Collections.Specialized; using System.Management.Automation; using System.Management.Automation.Internal; + using Dbg = System.Management.Automation; namespace Microsoft.PowerShell.Commands { /// - /// A command to resolve MSH paths containing glob characters to - /// MSH paths that match the glob strings. + /// A command to resolve PowerShell paths containing glob characters to + /// PowerShell paths that match the glob strings. /// - [Cmdlet(VerbsCommon.Split, "Path", DefaultParameterSetName = "ParentSet", SupportsTransactions = true, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113404")] + [Cmdlet(VerbsCommon.Split, "Path", DefaultParameterSetName = "ParentSet", SupportsTransactions = true, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2097149")] [OutputType(typeof(string), ParameterSetName = new[] { leafSet, leafBaseSet, extensionSet, @@ -29,26 +29,25 @@ public class SplitPathCommand : CoreCommandWithCredentialsBase #region Parameters /// - /// The parameter set name to get the parent path + /// The parameter set name to get the parent path. /// private const string parentSet = "ParentSet"; /// - /// The parameter set name to get the leaf name + /// The parameter set name to get the leaf name. /// private const string leafSet = "LeafSet"; /// - /// The parameter set name to get the leaf base name + /// The parameter set name to get the leaf base name. /// private const string leafBaseSet = "LeafBaseSet"; /// - /// The parameter set name to get the extension + /// The parameter set name to get the extension. /// private const string extensionSet = "ExtensionSet"; - /// /// The parameter set name to get the qualifier set. /// @@ -70,7 +69,7 @@ public class SplitPathCommand : CoreCommandWithCredentialsBase private const string literalPathSet = "LiteralPathSet"; /// - /// Gets or sets the path parameter to the command + /// Gets or sets the path parameter to the command. /// [Parameter(Position = 0, ParameterSetName = parentSet, Mandatory = true, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true)] [Parameter(Position = 0, ParameterSetName = leafSet, Mandatory = true, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true)] @@ -82,92 +81,80 @@ public class SplitPathCommand : CoreCommandWithCredentialsBase public string[] Path { get; set; } /// - /// Gets or sets the literal path parameter to the command + /// Gets or sets the literal path parameter to the command. /// [Parameter(ParameterSetName = "LiteralPathSet", Mandatory = true, ValueFromPipeline = false, ValueFromPipelineByPropertyName = true)] - [Alias("PSPath")] + [Alias("PSPath", "LP")] public string[] LiteralPath { get { return Path; - } // get + } set { base.SuppressWildcardExpansion = true; Path = value; - } // set - } // LiteralPath + } + } /// - /// Determines if the qualifier should be returned + /// Determines if the qualifier should be returned. /// - /// /// /// If true the qualifier of the path will be returned. /// The qualifier is the drive or provider that is qualifying - /// the MSH path. + /// the PowerShell path. /// - /// - [Parameter(Position = 1, ValueFromPipelineByPropertyName = true, ParameterSetName = qualifierSet, Mandatory = false)] + [Parameter(ParameterSetName = qualifierSet, Mandatory = true, ValueFromPipelineByPropertyName = true)] public SwitchParameter Qualifier { get; set; } /// - /// Determines if the qualifier should be returned + /// Determines if the qualifier should be returned. /// - /// /// /// If true the qualifier of the path will be returned. /// The qualifier is the drive or provider that is qualifying - /// the MSH path. + /// the PowerShell path. /// - /// - [Parameter(ParameterSetName = noQualifierSet, Mandatory = false, ValueFromPipelineByPropertyName = true)] + [Parameter(ParameterSetName = noQualifierSet, Mandatory = true, ValueFromPipelineByPropertyName = true)] public SwitchParameter NoQualifier { get; set; } /// - /// Determines if the parent path should be returned + /// Determines if the parent path should be returned. /// - /// /// /// If true the parent of the path will be returned. /// - /// [Parameter(ParameterSetName = parentSet, Mandatory = false, ValueFromPipelineByPropertyName = true)] public SwitchParameter Parent { get; set; } = true; /// - /// Determines if the leaf name should be returned + /// Determines if the leaf name should be returned. /// - /// /// /// If true the leaf name of the path will be returned. /// - /// - [Parameter(ParameterSetName = leafSet, Mandatory = false, ValueFromPipelineByPropertyName = true)] + [Parameter(ParameterSetName = leafSet, Mandatory = true, ValueFromPipelineByPropertyName = true)] public SwitchParameter Leaf { get; set; } /// - /// Determines if the leaf base name (name without extension) should be returned + /// Determines if the leaf base name (name without extension) should be returned. /// - /// /// /// If true the leaf base name of the path will be returned. /// - /// - [Parameter(ParameterSetName = leafBaseSet, Mandatory = false, ValueFromPipelineByPropertyName = true)] + [Parameter(ParameterSetName = leafBaseSet, Mandatory = true, ValueFromPipelineByPropertyName = true)] public SwitchParameter LeafBase { get; set; } /// - /// Determines if the extension should be returned + /// Determines if the extension should be returned. /// - /// /// /// If true the extension of the path will be returned. /// - /// - [Parameter(ParameterSetName = extensionSet, Mandatory = false, ValueFromPipelineByPropertyName = true)] + [Parameter(ParameterSetName = extensionSet, Mandatory = true, ValueFromPipelineByPropertyName = true)] public SwitchParameter Extension { get; set; } /// @@ -180,14 +167,13 @@ public string[] LiteralPath /// /// Determines if the path is an absolute path. /// - [Parameter(ParameterSetName = isAbsoluteSet)] + [Parameter(ParameterSetName = isAbsoluteSet, Mandatory = true)] public SwitchParameter IsAbsolute { get; set; } #endregion Parameters #region parameter data - #endregion parameter data #region Command code @@ -198,7 +184,7 @@ public string[] LiteralPath /// protected override void ProcessRecord() { - StringCollection pathsToParse = new StringCollection(); + StringCollection pathsToParse = new(); if (Resolve) { @@ -303,158 +289,144 @@ protected override void ProcessRecord() { string result = null; - switch (ParameterSetName) + // Check switch parameters in order of specificity + if (IsAbsolute) { - case isAbsoluteSet: - string ignored; - bool isPathAbsolute = - SessionState.Path.IsPSAbsolute(pathsToParse[index], out ignored); + string ignored; + bool isPathAbsolute = + SessionState.Path.IsPSAbsolute(pathsToParse[index], out ignored); - WriteObject(isPathAbsolute); - continue; + WriteObject(isPathAbsolute); + continue; + } + else if (Qualifier) + { + int separatorIndex = pathsToParse[index].IndexOf(':'); - case qualifierSet: - int separatorIndex = pathsToParse[index].IndexOf(":", StringComparison.CurrentCulture); + if (separatorIndex < 0) + { + FormatException e = + new( + StringUtil.Format(NavigationResources.ParsePathFormatError, pathsToParse[index])); + WriteError( + new ErrorRecord( + e, + "ParsePathFormatError", // RENAME + ErrorCategory.InvalidArgument, + pathsToParse[index])); + continue; + } + else + { + // Check to see if it is provider or drive qualified - if (separatorIndex < 0) + if (SessionState.Path.IsProviderQualified(pathsToParse[index])) { - FormatException e = - new FormatException( - StringUtil.Format(NavigationResources.ParsePathFormatError, pathsToParse[index])); - WriteError( - new ErrorRecord( - e, - "ParsePathFormatError", // RENAME - ErrorCategory.InvalidArgument, - pathsToParse[index])); - continue; - } - else - { - // Check to see if it is provider or drive qualified - - if (SessionState.Path.IsProviderQualified(pathsToParse[index])) - { - // The plus 2 is for the length of the provider separator - // which is "::" - - result = - pathsToParse[index].Substring( - 0, - separatorIndex + 2); - } - else - { - result = - pathsToParse[index].Substring( - 0, - separatorIndex + 1); - } - } - break; + // The plus 2 is for the length of the provider separator + // which is "::" - case parentSet: - case literalPathSet: - try - { result = - SessionState.Path.ParseParent( - pathsToParse[index], - String.Empty, - CmdletProviderContext, - true); + pathsToParse[index].Substring( + 0, + separatorIndex + 2); } - catch (PSNotSupportedException) - { - // Since getting the parent path is not supported, - // the provider must be a container, item, or drive - // provider. Since the paths for these types of - // providers can't be split, asking for the parent - // is asking for an empty string. - result = String.Empty; - } - - break; - - case leafSet: - case leafBaseSet: - case extensionSet: - try + else { - // default handles leafSet result = - SessionState.Path.ParseChildName( - pathsToParse[index], - CmdletProviderContext, - true); - if (LeafBase) - { - result = System.IO.Path.GetFileNameWithoutExtension(result); - } - else if (Extension) - { - result = System.IO.Path.GetExtension(result); - } - } - catch (PSNotSupportedException) - { - // Since getting the leaf part of a path is not supported, - // the provider must be a container, item, or drive - // provider. Since the paths for these types of - // providers can't be split, asking for the leaf - // is asking for the specified path back. - result = pathsToParse[index]; + pathsToParse[index].Substring( + 0, + separatorIndex + 1); } - catch (DriveNotFoundException driveNotFound) + } + } + else if (Leaf || LeafBase || Extension) + { + try + { + result = + SessionState.Path.ParseChildName( + pathsToParse[index], + CmdletProviderContext, + true); + if (LeafBase) { - WriteError( - new ErrorRecord( - driveNotFound.ErrorRecord, - driveNotFound)); - continue; + result = System.IO.Path.GetFileNameWithoutExtension(result); } - catch (ProviderNotFoundException providerNotFound) + else if (Extension) { - WriteError( - new ErrorRecord( - providerNotFound.ErrorRecord, - providerNotFound)); - continue; + result = System.IO.Path.GetExtension(result); } - - break; - - case noQualifierSet: - result = RemoveQualifier(pathsToParse[index]); - break; - - default: - Dbg.Diagnostics.Assert( - false, - "Only a known parameter set should be called"); - break; - } // switch + } + catch (PSNotSupportedException) + { + // Since getting the leaf part of a path is not supported, + // the provider must be a container, item, or drive + // provider. Since the paths for these types of + // providers can't be split, asking for the leaf + // is asking for the specified path back. + result = pathsToParse[index]; + } + catch (DriveNotFoundException driveNotFound) + { + WriteError( + new ErrorRecord( + driveNotFound.ErrorRecord, + driveNotFound)); + continue; + } + catch (ProviderNotFoundException providerNotFound) + { + WriteError( + new ErrorRecord( + providerNotFound.ErrorRecord, + providerNotFound)); + continue; + } + } + else if (NoQualifier) + { + result = RemoveQualifier(pathsToParse[index]); + } + else + { + // None of the switch parameters are true: default to -Parent behavior + try + { + result = + SessionState.Path.ParseParent( + pathsToParse[index], + string.Empty, + CmdletProviderContext, + true); + } + catch (PSNotSupportedException) + { + // Since getting the parent path is not supported, + // the provider must be a container, item, or drive + // provider. Since the paths for these types of + // providers can't be split, asking for the parent + // is asking for an empty string. + result = string.Empty; + } + } if (result != null) { WriteObject(result); } - } // for each path - } // ProcessRecord + } + } #endregion Command code /// /// Removes either the drive or provider qualifier or both from the path. /// - /// /// /// The path to strip the provider qualifier from. /// - /// /// /// The path without the qualifier. /// - /// private string RemoveQualifier(string path) { Dbg.Diagnostics.Assert( @@ -465,7 +437,7 @@ private string RemoveQualifier(string path) if (SessionState.Path.IsProviderQualified(path)) { - int index = path.IndexOf("::", StringComparison.CurrentCulture); + int index = path.IndexOf("::", StringComparison.Ordinal); if (index != -1) { @@ -475,7 +447,7 @@ private string RemoveQualifier(string path) } else { - string driveName = String.Empty; + string driveName = string.Empty; if (SessionState.Path.IsPSAbsolute(path, out driveName)) { @@ -489,7 +461,6 @@ private string RemoveQualifier(string path) } return result; - } // RemoveQualifier - } // SplitPathCommand -} // namespace Microsoft.PowerShell.Commands - + } + } +} diff --git a/src/Microsoft.PowerShell.Commands.Management/commands/management/PassThroughContentCommandBase.cs b/src/Microsoft.PowerShell.Commands.Management/commands/management/PassThroughContentCommandBase.cs index 20ea5a5c394..19bad39785b 100644 --- a/src/Microsoft.PowerShell.Commands.Management/commands/management/PassThroughContentCommandBase.cs +++ b/src/Microsoft.PowerShell.Commands.Management/commands/management/PassThroughContentCommandBase.cs @@ -1,22 +1,20 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. using System.Management.Automation; -using Dbg = System.Management.Automation; namespace Microsoft.PowerShell.Commands { /// /// The base class for the */content commands that also take - /// a passthrough parameter + /// a passthrough parameter. /// public class PassThroughContentCommandBase : ContentCommandBase { #region Parameters /// - /// Gets or sets the passthrough parameter to the command + /// Gets or sets the passthrough parameter to the command. /// [Parameter] public SwitchParameter PassThru @@ -24,16 +22,16 @@ public SwitchParameter PassThru get { return _passThrough; - } // get + } set { _passThrough = value; - } // set - } // PassThru + } + } /// - /// Determines if the provider for the specified path supports ShouldProcess + /// Determines if the provider for the specified path supports ShouldProcess. /// /// protected override bool ProviderSupportsShouldProcess @@ -62,20 +60,17 @@ protected override bool ProviderSupportsShouldProcess /// Initializes a CmdletProviderContext instance to the current context of /// the command. /// - /// /// /// A CmdletProviderContext instance initialized to the context of the current /// command. /// - /// internal CmdletProviderContext GetCurrentContext() { CmdletProviderContext currentCommandContext = CmdletProviderContext; currentCommandContext.PassThru = PassThru; return currentCommandContext; - } // GetCurrentContext + } #endregion protected members - } // PassThroughContentCommandBase -} // namespace Microsoft.PowerShell.Commands - + } +} diff --git a/src/Microsoft.PowerShell.Commands.Management/commands/management/PassThroughPropertyCommandBase.cs b/src/Microsoft.PowerShell.Commands.Management/commands/management/PassThroughPropertyCommandBase.cs index 8b25bf179f8..8b76ac36786 100644 --- a/src/Microsoft.PowerShell.Commands.Management/commands/management/PassThroughPropertyCommandBase.cs +++ b/src/Microsoft.PowerShell.Commands.Management/commands/management/PassThroughPropertyCommandBase.cs @@ -1,22 +1,20 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. using System.Management.Automation; -using Dbg = System.Management.Automation; namespace Microsoft.PowerShell.Commands { /// /// The base class for the */property commands that also take - /// a passthrough parameter + /// a passthrough parameter. /// public class PassThroughItemPropertyCommandBase : ItemPropertyCommandBase { #region Parameters /// - /// Gets or sets the passthrough parameter to the command + /// Gets or sets the passthrough parameter to the command. /// [Parameter] public SwitchParameter PassThru @@ -24,18 +22,17 @@ public SwitchParameter PassThru get { return _passThrough; - } // get + } set { _passThrough = value; - } // set - } // PassThru + } + } /// - /// Gets or sets the force property + /// Gets or sets the force property. /// - /// /// /// Gives the provider guidance on how vigorous it should be about performing /// the operation. If true, the provider should do everything possible to perform @@ -45,7 +42,6 @@ public SwitchParameter PassThru /// the destination is read-only, if force is true, the provider should copy over /// the existing read-only file. If force is false, the provider should write an error. /// - /// [Parameter] public override SwitchParameter Force { @@ -53,11 +49,12 @@ public override SwitchParameter Force { return base.Force; } + set { base.Force = value; } - } // Force + } #endregion Parameters @@ -74,7 +71,7 @@ public override SwitchParameter Force #region protected members /// - /// Determines if the provider for the specified path supports ShouldProcess + /// Determines if the provider for the specified path supports ShouldProcess. /// /// protected override bool ProviderSupportsShouldProcess @@ -89,20 +86,17 @@ protected override bool ProviderSupportsShouldProcess /// Initializes a CmdletProviderContext instance to the current context of /// the command. /// - /// /// /// A CmdletProviderContext instance initialized to the context of the current /// command. /// - /// internal CmdletProviderContext GetCurrentContext() { CmdletProviderContext currentCommandContext = CmdletProviderContext; currentCommandContext.PassThru = PassThru; return currentCommandContext; - } // GetCurrentContext + } #endregion protected members - } // PassThroughItemPropertyCommandBase -} // namespace Microsoft.PowerShell.Commands - + } +} diff --git a/src/Microsoft.PowerShell.Commands.Management/commands/management/PingPathCommand.cs b/src/Microsoft.PowerShell.Commands.Management/commands/management/PingPathCommand.cs deleted file mode 100644 index ea69de6c381..00000000000 --- a/src/Microsoft.PowerShell.Commands.Management/commands/management/PingPathCommand.cs +++ /dev/null @@ -1,213 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -using System.Management.Automation; -using Dbg = System.Management.Automation; - -namespace Microsoft.PowerShell.Commands -{ - /// - /// The valid values for the -PathType parameter for test-path - /// - public enum TestPathType - { - /// - /// If the item at the path exists, true will be returned. - /// - Any, - - /// - /// If the item at the path exists and is a container, true will be returned. - /// - Container, - - /// - /// If the item at the path exists and is not a container, true will be returned. - /// - Leaf - } - - /// - /// A command to determine if an item exists at a specified path - /// - [Cmdlet(VerbsDiagnostic.Test, "Path", DefaultParameterSetName = "Path", SupportsTransactions = true, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113418")] - [OutputType(typeof(bool))] - public class TestPathCommand : CoreCommandWithCredentialsBase - { - #region Parameters - - /// - /// Gets or sets the path parameter to the command - /// - [Parameter(Position = 0, ParameterSetName = "Path", - Mandatory = true, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true)] - public string[] Path - { - get { return _paths; } - set { _paths = value; } - } - - /// - /// Gets or sets the literal path parameter to the command - /// - [Parameter(ParameterSetName = "LiteralPath", - Mandatory = true, ValueFromPipeline = false, ValueFromPipelineByPropertyName = true)] - [Alias("PSPath")] - public string[] LiteralPath - { - get { return _paths; } - set - { - base.SuppressWildcardExpansion = true; - _paths = value; - } - } - - /// - /// Gets or sets the filter property - /// - [Parameter] - public override string Filter - { - get { return base.Filter; } - set { base.Filter = value; } - } - - /// - /// Gets or sets the include property - /// - [Parameter] - public override string[] Include - { - get { return base.Include; } - set { base.Include = value; } - } - - /// - /// Gets or sets the exclude property - /// - [Parameter] - public override string[] Exclude - { - get { return base.Exclude; } - set { base.Exclude = value; } - } - - /// - /// Gets or sets the isContainer property - /// - [Parameter] - [Alias("Type")] - public TestPathType PathType { get; set; } = TestPathType.Any; - - /// - /// Gets or sets the IsValid parameter - /// - [Parameter] - public SwitchParameter IsValid { get; set; } = new SwitchParameter(); - - /// - /// A virtual method for retrieving the dynamic parameters for a cmdlet. Derived cmdlets - /// that require dynamic parameters should override this method and return the - /// dynamic parameter object. - /// - /// - /// - /// The context under which the command is running. - /// - /// - /// - /// An object representing the dynamic parameters for the cmdlet or null if there - /// are none. - /// - /// - internal override object GetDynamicParameters(CmdletProviderContext context) - { - object result = null; - - if (this.PathType == TestPathType.Any && !IsValid) - { - if (Path != null && Path.Length > 0) - { - result = InvokeProvider.Item.ItemExistsDynamicParameters(Path[0], context); - } - else - { - result = InvokeProvider.Item.ItemExistsDynamicParameters(".", context); - } - } - return result; - } // GetDynamicParameters - - #endregion Parameters - - #region parameter data - - /// - /// The path to the item to ping - /// - private string[] _paths; - - #endregion parameter data - - #region Command code - - /// - /// Determines if an item at the specified path exists. - /// - protected override void ProcessRecord() - { - CmdletProviderContext currentContext = CmdletProviderContext; - - foreach (string path in _paths) - { - bool result = false; - - try - { - if (IsValid) - { - result = SessionState.Path.IsValid(path, currentContext); - } - else - { - if (this.PathType == TestPathType.Container) - { - result = InvokeProvider.Item.IsContainer(path, currentContext); - } - else if (this.PathType == TestPathType.Leaf) - { - result = - InvokeProvider.Item.Exists(path, currentContext) && - !InvokeProvider.Item.IsContainer(path, currentContext); - } - else - { - result = InvokeProvider.Item.Exists(path, currentContext); - } - } - } - // Any of the known exceptions means the path does not exist. - catch (PSNotSupportedException) - { - } - catch (DriveNotFoundException) - { - } - catch (ProviderNotFoundException) - { - } - catch (ItemNotFoundException) - { - } - - WriteObject(result); - } - } // ProcessRecord - #endregion Command code - - - } // PingPathCommand -} // namespace Microsoft.PowerShell.Commands - diff --git a/src/Microsoft.PowerShell.Commands.Management/commands/management/Process.cs b/src/Microsoft.PowerShell.Commands.Management/commands/management/Process.cs index 6023199fcc9..c959cfcb33e 100644 --- a/src/Microsoft.PowerShell.Commands.Management/commands/management/Process.cs +++ b/src/Microsoft.PowerShell.Commands.Management/commands/management/Process.cs @@ -1,80 +1,64 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. using System; -using System.Text; using System.Collections; using System.Collections.Generic; +using System.Collections.ObjectModel; using System.Collections.Specialized; -using System.Diagnostics; // Process class using System.ComponentModel; // Win32Exception -using System.Runtime.ConstrainedExecution; -using System.Runtime.Serialization; -using System.Threading; -using System.Management.Automation; +using System.Diagnostics; // Process class using System.Diagnostics.CodeAnalysis; -using System.Net; using System.IO; +using System.Management.Automation; +using System.Management.Automation.Internal; +using System.Management.Automation.Language; +using System.Net; using System.Runtime.InteropServices; -using System.Security; -using System.Security.Permissions; +using System.Runtime.Serialization; using System.Security.Principal; -using Microsoft.Win32.SafeHandles; -using System.Management.Automation.Internal; -using Microsoft.PowerShell.Commands.Internal; +using System.Text; +using System.Threading; using Microsoft.Management.Infrastructure; - -using FileNakedHandle = System.IntPtr; -using DWORD = System.UInt32; +using Microsoft.PowerShell.Commands.Internal; +using Microsoft.Win32.SafeHandles; namespace Microsoft.PowerShell.Commands { - // 2004/12/17-JonN ProcessNameGlobAttribute was deeply wrong. - // For example, if you pass in a single Process, it will match - // all processes with the same name. - // I have removed the globbing code. - #region ProcessBaseCommand /// - /// This class implements the base for process commands + /// This class implements the base for process commands. /// public abstract class ProcessBaseCommand : Cmdlet { #region Parameters /// - /// The various process selection modes + /// The various process selection modes. /// internal enum MatchMode { /// - /// Select all processes + /// Select all processes. /// All, /// - /// Select processes matching the supplied names + /// Select processes matching the supplied names. /// ByName, /// - /// Select the processes matching the id + /// Select the processes matching the id. /// ById, /// /// Select the processes specified as input. /// ByInput - }; + } /// /// The current process selection mode. /// internal MatchMode myMode = MatchMode.All; - /// - /// The computer from which to retrieve processes. - /// - [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] - protected string[] SuppliedComputerName { get; set; } = Utils.EmptyArray(); - /// /// The Name parameter is declared in subclasses, /// since it is optional for GetProcess and mandatory for StopProcess. @@ -103,12 +87,14 @@ public virtual Process[] InputObject { return _input; } + set { myMode = MatchMode.ByInput; _input = value; } } + private Process[] _input = null; #endregion Parameters @@ -116,8 +102,8 @@ public virtual Process[] InputObject // We use a Dictionary to optimize the check whether the object // is already in the list. - private List _matchingProcesses = new List(); - private Dictionary _keys = new Dictionary(); + private List _matchingProcesses = new(); + private readonly Dictionary _keys = new(); /// /// Retrieve the list of all processes matching the Name, Id @@ -145,24 +131,24 @@ internal List MatchingProcesses() // before being stopped. PM confirms that this is fine. _matchingProcesses.Sort(ProcessComparison); return _matchingProcesses; - } // MatchingProcesses + } /// - /// sort function to sort by Name first, then Id + /// Sort function to sort by Name first, then Id. /// - /// first Process object - /// second Process object + /// First Process object. + /// Second Process object. /// - /// as String.Compare: returns less than zero if x less than y, - /// greater than 0 if x greater than y, 0 if x == y + /// As string.Compare: returns less than zero if x less than y, + /// greater than 0 if x greater than y, 0 if x == y. /// private static int ProcessComparison(Process x, Process y) { - int diff = String.Compare( + int diff = string.Compare( SafeGetProcessName(x), SafeGetProcessName(y), - StringComparison.CurrentCultureIgnoreCase); - if (0 != diff) + StringComparison.OrdinalIgnoreCase); + if (diff != 0) return diff; return SafeGetProcessId(x) - SafeGetProcessId(y); } @@ -177,11 +163,12 @@ private static int ProcessComparison(Process x, Process y) /// private void RetrieveMatchingProcessesByProcessName() { - if (null == processNames) + if (processNames == null) { _matchingProcesses = new List(AllProcesses); return; } + foreach (string pattern in processNames) { WildcardPattern wildcard = @@ -194,20 +181,30 @@ private void RetrieveMatchingProcessesByProcessName() found = true; AddIdempotent(process); } + if (!found && !WildcardPattern.ContainsWildcardCharacters(pattern)) { + string errorText = ProcessResources.NoProcessFoundForGivenName; + string errorName = nameof(ProcessResources.NoProcessFoundForGivenName); + + if (int.TryParse(pattern, out int x) && x >= 0) + { + errorText = ProcessResources.RecommendIdTagForGivenName; + errorName = nameof(ProcessResources.RecommendIdTagForGivenName); + } + WriteNonTerminatingError( - pattern, - 0, - pattern, - null, - ProcessResources.NoProcessFoundForGivenName, - "NoProcessFoundForGivenName", - ErrorCategory.ObjectNotFound); + processName: pattern, + processId: 0, + targetObject: pattern, + innerException: null, + resourceId: errorText, + errorId: errorName, + category: ErrorCategory.ObjectNotFound); } } - } // MatchingProcessesByProcessName + } /// /// Retrieves the list of all processes matching the Id @@ -218,34 +215,24 @@ private void RetrieveMatchingProcessesByProcessName() /// private void RetrieveMatchingProcessesById() { - if (null == processIds) + if (processIds == null) { Diagnostics.Assert(false, "null processIds"); throw PSTraceSource.NewInvalidOperationException(); } + foreach (int processId in processIds) { Process process; try { - if (SuppliedComputerName.Length > 0) - { - foreach (string computerName in SuppliedComputerName) - { - process = Process.GetProcessById(processId, computerName); - AddIdempotent(process); - } - } - else - { - process = Process.GetProcessById(processId); - AddIdempotent(process); - } + process = Process.GetProcessById(processId); + AddIdempotent(process); } catch (ArgumentException) { WriteNonTerminatingError( - "", + string.Empty, processId, processId, null, @@ -255,7 +242,7 @@ private void RetrieveMatchingProcessesById() continue; } } - } // MatchingProcessesById + } /// /// Retrieves the list of all processes matching the InputObject @@ -264,53 +251,31 @@ private void RetrieveMatchingProcessesById() /// private void RetrieveProcessesByInput() { - if (null == InputObject) + if (InputObject == null) { Diagnostics.Assert(false, "null InputObject"); throw PSTraceSource.NewInvalidOperationException(); } + foreach (Process process in InputObject) { SafeRefresh(process); AddIdempotent(process); } - } // MatchingProcessesByInput + } /// - /// Retrieve the master list of all processes + /// Gets an array of all processes. /// - /// + /// An array of components that represents all the process resources. /// /// MSDN does not document the list of exceptions, /// but it is reasonable to expect that SecurityException is /// among them. Errors here will terminate the cmdlet. /// - internal Process[] AllProcesses - { - get - { - if (null == _allProcesses) - { - List processes = new List(); - - if (SuppliedComputerName.Length > 0) - { - foreach (string computerName in SuppliedComputerName) - { - processes.AddRange(Process.GetProcesses(computerName)); - } - } - else - { - processes.AddRange(Process.GetProcesses()); - } + internal Process[] AllProcesses => _allProcesses ??= Process.GetProcesses(); - _allProcesses = processes.ToArray(); - } - return _allProcesses; - } - } - private Process[] _allProcesses = null; + private Process[] _allProcesses; /// /// Add to , @@ -318,7 +283,7 @@ internal Process[] AllProcesses /// We use a Dictionary to optimize the check whether the object /// is already in the list. /// - /// process to add to list + /// Process to add to list. private void AddIdempotent( Process process) { @@ -377,9 +342,9 @@ internal void WriteNonTerminatingError( string message = StringUtil.Format(resourceId, processName, processId, - (null == innerException) ? "" : innerException.Message); + (innerException == null) ? string.Empty : innerException.Message); ProcessCommandException exception = - new ProcessCommandException(message, innerException); + new(message, innerException); exception.ProcessName = processName; WriteError(new ErrorRecord( @@ -396,11 +361,11 @@ internal static string SafeGetProcessName(Process process) } catch (Win32Exception) { - return ""; + return string.Empty; } catch (InvalidOperationException) { - return ""; + return string.Empty; } } @@ -463,15 +428,15 @@ internal static bool TryHasExited(Process process) } #endregion Internal - }//ProcessBaseCommand + } #endregion ProcessBaseCommand #region GetProcessCommand /// - /// This class implements the get-process command + /// This class implements the get-process command. /// [Cmdlet(VerbsCommon.Get, "Process", DefaultParameterSetName = NameParameterSet, - HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113324", RemotingCapability = RemotingCapability.SupportedByCommand)] + HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2096814", RemotingCapability = RemotingCapability.SupportedByCommand)] [OutputType(typeof(ProcessModule), typeof(FileVersionInfo), typeof(Process))] public sealed class GetProcessCommand : ProcessBaseCommand { @@ -489,16 +454,19 @@ public sealed class GetProcessCommand : ProcessBaseCommand #region Parameters /// - /// Has the list of process names on which to this command will work + /// Has the list of process names on which to this command will work. /// - // [ProcessNameGlobAttribute] [Parameter(Position = 0, ParameterSetName = NameParameterSet, ValueFromPipelineByPropertyName = true)] [Parameter(Position = 0, ParameterSetName = NameWithUserNameParameterSet, ValueFromPipelineByPropertyName = true)] [Alias("ProcessName")] [ValidateNotNullOrEmpty] public string[] Name { - get { return processNames; } + get + { + return processNames; + } + set { myMode = MatchMode.ByName; @@ -507,7 +475,7 @@ public string[] Name } /// - /// gets/sets an array of process IDs + /// Gets/sets an array of process IDs. /// [Parameter(ParameterSetName = IdParameterSet, Mandatory = true, ValueFromPipelineByPropertyName = true)] [Parameter(ParameterSetName = IdWithUserNameParameterSet, Mandatory = true, ValueFromPipelineByPropertyName = true)] @@ -518,6 +486,7 @@ public int[] Id { return processIds; } + set { myMode = MatchMode.ById; @@ -526,7 +495,7 @@ public int[] Id } /// - /// Input is a stream of [collections of] Process objects + /// Input is a stream of [collections of] Process objects. /// [Parameter(ParameterSetName = InputObjectParameterSet, Mandatory = true, ValueFromPipeline = true)] [Parameter(ParameterSetName = InputObjectWithUserNameParameterSet, Mandatory = true, ValueFromPipeline = true)] @@ -536,6 +505,7 @@ public override Process[] InputObject { return base.InputObject; } + set { base.InputObject = value; @@ -543,54 +513,25 @@ public override Process[] InputObject } /// - /// Include the UserName + /// Include the UserName. /// [Parameter(ParameterSetName = NameWithUserNameParameterSet, Mandatory = true)] [Parameter(ParameterSetName = IdWithUserNameParameterSet, Mandatory = true)] [Parameter(ParameterSetName = InputObjectWithUserNameParameterSet, Mandatory = true)] - public SwitchParameter IncludeUserName - { - get { return _includeUserName; } - set { _includeUserName = value; } - } - private bool _includeUserName = false; - + public SwitchParameter IncludeUserName { get; set; } /// - /// gets/sets the destination computer name + /// To display the modules of a process. /// - [Parameter(Mandatory = false, ParameterSetName = NameParameterSet, ValueFromPipelineByPropertyName = true)] - [Parameter(Mandatory = false, ParameterSetName = IdParameterSet, ValueFromPipelineByPropertyName = true)] - [Parameter(Mandatory = false, ParameterSetName = InputObjectParameterSet, ValueFromPipelineByPropertyName = true)] - [Alias("Cn")] - [ValidateNotNullOrEmpty()] - [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] - public string[] ComputerName - { - get - { - return SuppliedComputerName; - } - set - { - SuppliedComputerName = value; - } - } - - - /// - ///To display the modules of a process - /// - [Parameter(ParameterSetName = NameParameterSet)] [Parameter(ParameterSetName = IdParameterSet)] [Parameter(ParameterSetName = InputObjectParameterSet)] [ValidateNotNull] public SwitchParameter Module { get; set; } - /// - ///To display the fileversioninfo of the main module of a process - /// + /// + /// To display the fileversioninfo of the main module of a process. + /// [Parameter(ParameterSetName = NameParameterSet)] [Parameter(ParameterSetName = IdParameterSet)] [Parameter(ParameterSetName = InputObjectParameterSet)] @@ -603,34 +544,12 @@ public string[] ComputerName #region Overrides /// - /// Check the elevation mode if IncludeUserName is specified - /// - protected override void BeginProcessing() - { - // The parameter 'IncludeUserName' requires administrator privilege - if (IncludeUserName.IsPresent && !Utils.IsAdministrator()) - { - var ex = new InvalidOperationException(ProcessResources.IncludeUserNameRequiresElevation); - var er = new ErrorRecord(ex, "IncludeUserNameRequiresElevation", ErrorCategory.InvalidOperation, null); - ThrowTerminatingError(er); - } - } - - /// - /// Write the process objects + /// Write the process objects. /// protected override void ProcessRecord() { - if (ComputerName.Length > 0 && (FileVersionInfo.IsPresent || Module.IsPresent)) - { - Exception ex = new InvalidOperationException(ProcessResources.NoComputerNameWithFileVersion); - ErrorRecord er = new ErrorRecord(ex, "InvalidOperationException", ErrorCategory.InvalidOperation, ComputerName); - ThrowTerminatingError(er); - } - foreach (Process process in MatchingProcesses()) { - //if module and fileversion are to be displayed if (Module.IsPresent && FileVersionInfo.IsPresent) { ProcessModule tempmodule = null; @@ -639,7 +558,7 @@ protected override void ProcessRecord() ProcessModuleCollection modules = process.Modules; foreach (ProcessModule pmodule in modules) { - //assigning to tempmodule to rethrow for exceptions on 64 bit machines + // Assigning to tempmodule to rethrow for exceptions on 64 bit machines tempmodule = pmodule; WriteObject(pmodule.FileVersionInfo, true); } @@ -677,7 +596,6 @@ protected override void ProcessRecord() } else if (Module.IsPresent) { - //if only modules are to be displayed try { WriteObject(process.Modules, true); @@ -700,6 +618,10 @@ protected override void ProcessRecord() WriteNonTerminatingError(process, ex, ProcessResources.CouldNotEnumerateModules, "CouldNotEnumerateModules", ErrorCategory.PermissionDenied); } } + catch (PipelineStoppedException) + { + throw; + } catch (Exception exception) { WriteNonTerminatingError(process, exception, ProcessResources.CouldNotEnumerateModules, "CouldNotEnumerateModules", ErrorCategory.PermissionDenied); @@ -707,10 +629,13 @@ protected override void ProcessRecord() } else if (FileVersionInfo.IsPresent) { - //if fileversion of each process is to be displayed try { - WriteObject(PsUtils.GetMainModule(process).FileVersionInfo, true); + ProcessModule mainModule = process.MainModule; + if (mainModule != null) + { + WriteObject(mainModule.FileVersionInfo, true); + } } catch (InvalidOperationException exception) { @@ -726,7 +651,7 @@ protected override void ProcessRecord() { if (exception.HResult == 299) { - WriteObject(PsUtils.GetMainModule(process).FileVersionInfo, true); + WriteObject(process.MainModule?.FileVersionInfo, true); } else { @@ -745,22 +670,22 @@ protected override void ProcessRecord() } else { - WriteObject(IncludeUserName.IsPresent ? AddUserNameToProcess(process) : (object)process); + WriteObject(IncludeUserName.IsPresent ? AddUserNameToProcess(process) : process); } - }//for loop - } // ProcessRecord + } + } #endregion Overrides #region Privates /// - /// New PSTypeName added to the process object + /// New PSTypeName added to the process object. /// private const string TypeNameForProcessWithUserName = "System.Diagnostics.Process#IncludeUserName"; /// - /// Add the 'UserName' NoteProperty to the Process object + /// Add the 'UserName' NoteProperty to the Process object. /// /// /// @@ -770,7 +695,7 @@ private static PSObject AddUserNameToProcess(Process process) string userName = RetrieveProcessUserName(process); PSObject processAsPsobj = PSObject.AsPSObject(process); - PSNoteProperty noteProperty = new PSNoteProperty("UserName", userName); + PSNoteProperty noteProperty = new("UserName", userName); processAsPsobj.Properties.Add(noteProperty, true); processAsPsobj.TypeNames.Insert(0, TypeNameForProcessWithUserName); @@ -778,9 +703,8 @@ private static PSObject AddUserNameToProcess(Process process) return processAsPsobj; } - /// - /// Retrieve the UserName through PInvoke + /// Retrieve the UserName through PInvoke. /// /// /// @@ -797,72 +721,57 @@ private static string RetrieveProcessUserName(Process process) try { - do + int error; + if (!Win32Native.OpenProcessToken(process.Handle, TOKEN_QUERY, out processTokenHandler)) { - int error; - if (!Win32Native.OpenProcessToken(process.Handle, TOKEN_QUERY, out processTokenHandler)) { break; } + return null; + } - // Set the default length to be 256, so it will be sufficient for most cases - int tokenInfoLength = 256; - tokenUserInfo = Marshal.AllocHGlobal(tokenInfoLength); - if (!Win32Native.GetTokenInformation(processTokenHandler, Win32Native.TOKEN_INFORMATION_CLASS.TokenUser, tokenUserInfo, tokenInfoLength, out tokenInfoLength)) + // Set the default length to be 256, so it will be sufficient for most cases. + int tokenInfoLength = 256; + tokenUserInfo = Marshal.AllocHGlobal(tokenInfoLength); + if (!Win32Native.GetTokenInformation(processTokenHandler, Win32Native.TOKEN_INFORMATION_CLASS.TokenUser, tokenUserInfo, tokenInfoLength, out tokenInfoLength)) + { + error = Marshal.GetLastWin32Error(); + if (error == Win32Native.ERROR_INSUFFICIENT_BUFFER) { - error = Marshal.GetLastWin32Error(); - if (error == Win32Native.ERROR_INSUFFICIENT_BUFFER) - { - Marshal.FreeHGlobal(tokenUserInfo); - tokenUserInfo = Marshal.AllocHGlobal(tokenInfoLength); + Marshal.FreeHGlobal(tokenUserInfo); + tokenUserInfo = Marshal.AllocHGlobal(tokenInfoLength); - if (!Win32Native.GetTokenInformation(processTokenHandler, Win32Native.TOKEN_INFORMATION_CLASS.TokenUser, tokenUserInfo, tokenInfoLength, out tokenInfoLength)) { break; } - } - else + if (!Win32Native.GetTokenInformation(processTokenHandler, Win32Native.TOKEN_INFORMATION_CLASS.TokenUser, tokenUserInfo, tokenInfoLength, out tokenInfoLength)) { - break; + return null; } } - - var tokenUser = Marshal.PtrToStructure(tokenUserInfo); - - // Set the default length to be 256, so it will be sufficient for most cases - int userNameLength = 256, domainNameLength = 256; - var userNameStr = new StringBuilder(userNameLength); - var domainNameStr = new StringBuilder(domainNameLength); - Win32Native.SID_NAME_USE accountType; - - if (!Win32Native.LookupAccountSid(null, tokenUser.User.Sid, userNameStr, ref userNameLength, domainNameStr, ref domainNameLength, out accountType)) + else { - error = Marshal.GetLastWin32Error(); - if (error == Win32Native.ERROR_INSUFFICIENT_BUFFER) - { - userNameStr.EnsureCapacity(userNameLength); - domainNameStr.EnsureCapacity(domainNameLength); - - if (!Win32Native.LookupAccountSid(null, tokenUser.User.Sid, userNameStr, ref userNameLength, domainNameStr, ref domainNameLength, out accountType)) { break; } - } - else - { - break; - } + return null; } + } - userName = domainNameStr + "\\" + userNameStr; - } while (false); + var tokenUser = Marshal.PtrToStructure(tokenUserInfo); + SecurityIdentifier sid = new SecurityIdentifier(tokenUser.User.Sid); + userName = sid.Translate(typeof(System.Security.Principal.NTAccount)).Value; } catch (NotSupportedException) { - // The Process not started yet, or it's a process from a remote machine + // The Process not started yet, or it's a process from a remote machine. + } + catch (IdentityNotMappedException) + { + // SID cannot be mapped to a user } catch (InvalidOperationException) { - // The Process has exited, Process.Handle will raise this exception + // The Process has exited, Process.Handle will raise this exception. } catch (Win32Exception) { - // We might get an AccessDenied error + // We might get an AccessDenied error. } catch (Exception) { - // I don't expect to get other exceptions, + // I don't expect to get other exceptions. } finally { @@ -876,20 +785,20 @@ private static string RetrieveProcessUserName(Process process) Win32Native.CloseHandle(processTokenHandler); } } - #endif return userName; } #endregion Privates - }//GetProcessCommand + } #endregion GetProcessCommand #region WaitProcessCommand /// - /// This class implements the Wait-process command + /// This class implements the Wait-process command. /// - [Cmdlet(VerbsLifecycle.Wait, "Process", DefaultParameterSetName = "Name", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=135277")] + [Cmdlet(VerbsLifecycle.Wait, "Process", DefaultParameterSetName = "Name", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2097146")] + [OutputType(typeof(Process))] public sealed class WaitProcessCommand : ProcessBaseCommand { #region Parameters @@ -911,6 +820,7 @@ public int[] Id { return processIds; } + set { myMode = MatchMode.ById; @@ -919,7 +829,7 @@ public int[] Id } /// - /// Name of the processes to wait on for termination + /// Name of the processes to wait on for termination. /// [Parameter( ParameterSetName = "Name", @@ -930,7 +840,11 @@ public int[] Id [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] public string[] Name { - get { return processNames; } + get + { + return processNames; + } + set { myMode = MatchMode.ByName; @@ -939,7 +853,7 @@ public string[] Name } /// - /// If specified, wait for this number of seconds + /// If specified, wait for this number of seconds. /// [Parameter(Position = 1)] [Alias("TimeoutSec")] @@ -951,33 +865,47 @@ public int Timeout { return _timeout; } + set { _timeout = value; _timeOutSpecified = true; } } + + /// + /// Gets or sets a value indicating whether to return after any one process exits. + /// + [Parameter] + public SwitchParameter Any { get; set; } + + /// + /// Gets or sets a value indicating whether to return the Process objects after waiting. + /// + [Parameter] + public SwitchParameter PassThru { get; set; } + private int _timeout = 0; private bool _timeOutSpecified; - #endregion Parameters private bool _disposed = false; #region IDisposable /// - /// Dispose method of IDisposable interface. + /// Dispose method of IDisposable interface. /// public void Dispose() { - if (_disposed == false) + if (!_disposed) { if (_waitHandle != null) { _waitHandle.Dispose(); _waitHandle = null; } + _disposed = true; } } @@ -988,12 +916,9 @@ public void Dispose() // Handle Exited event and display process information. private void myProcess_Exited(object sender, System.EventArgs e) { - if (0 == System.Threading.Interlocked.Decrement(ref _numberOfProcessesToWaitFor)) + if (Any || (Interlocked.Decrement(ref _numberOfProcessesToWaitFor) == 0)) { - if (_waitHandle != null) - { - _waitHandle.Set(); - } + _waitHandle?.Set(); } } @@ -1001,19 +926,18 @@ private void myProcess_Exited(object sender, System.EventArgs e) #region Overrides - private List _processList = new List(); + private readonly List _processList = new(); - //Wait handle which is used by thread to sleep. + // Wait handle which is used by thread to sleep. private ManualResetEvent _waitHandle; private int _numberOfProcessesToWaitFor; - /// - /// gets the list of process + /// Gets the list of process. /// protected override void ProcessRecord() { - //adding the processes into the list + // adding the processes into the list foreach (Process process in MatchingProcesses()) { // Idle process has processid zero,so handle that because we cannot wait on it. @@ -1024,17 +948,18 @@ protected override void ProcessRecord() } // It cannot wait on itself - if (process.Id.Equals(System.Diagnostics.Process.GetCurrentProcess().Id)) + if (process.Id.Equals(Environment.ProcessId)) { WriteNonTerminatingError(process, null, ProcessResources.WaitOnItself, "WaitOnItself", ErrorCategory.ObjectNotFound); continue; } + _processList.Add(process); } - } // ProcessRecord + } /// - /// Wait for the process to terminate + /// Wait for the process to terminate. /// protected override void EndProcessing() { @@ -1043,10 +968,15 @@ protected override void EndProcessing() { try { - if (!process.HasExited) + // Check for processes that exit too soon for us to add an event. + if (Any && process.HasExited) + { + _waitHandle.Set(); + } + else if (!process.HasExited) { process.EnableRaisingEvents = true; - process.Exited += new EventHandler(myProcess_Exited); + process.Exited += myProcess_Exited; if (!process.HasExited) { System.Threading.Interlocked.Increment(ref _numberOfProcessesToWaitFor); @@ -1055,58 +985,62 @@ protected override void EndProcessing() } catch (Win32Exception exception) { - WriteNonTerminatingError(process, exception, ProcessResources.Process_is_not_terminated, "ProcessNotTerminated", ErrorCategory.CloseError); + WriteNonTerminatingError(process, exception, ProcessResources.ProcessIsNotTerminated, "ProcessNotTerminated", ErrorCategory.CloseError); } } + bool hasTimedOut = false; if (_numberOfProcessesToWaitFor > 0) { if (_timeOutSpecified) { - _waitHandle.WaitOne(_timeout * 1000); + hasTimedOut = !_waitHandle.WaitOne(_timeout * 1000); } else { _waitHandle.WaitOne(); } } - foreach (Process process in _processList) + + if (hasTimedOut || (!Any && _numberOfProcessesToWaitFor > 0)) { - try + foreach (Process process in _processList) { - if (!process.HasExited) + try { - //write the error - string message = StringUtil.Format(ProcessResources.ProcessNotTerminated, new object[] { process.ProcessName, process.Id }); - ErrorRecord errorRecord = new ErrorRecord(new TimeoutException(message), "ProcessNotTerminated", ErrorCategory.CloseError, process); - WriteError(errorRecord); + if (!process.HasExited) + { + string message = StringUtil.Format(ProcessResources.ProcessNotTerminated, new object[] { process.ProcessName, process.Id }); + ErrorRecord errorRecord = new(new TimeoutException(message), "ProcessNotTerminated", ErrorCategory.CloseError, process); + WriteError(errorRecord); + } + } + catch (Win32Exception exception) + { + WriteNonTerminatingError(process, exception, ProcessResources.ProcessIsNotTerminated, "ProcessNotTerminated", ErrorCategory.CloseError); } - } - catch (Win32Exception exception) - { - WriteNonTerminatingError(process, exception, ProcessResources.Process_is_not_terminated, "ProcessNotTerminated", ErrorCategory.CloseError); } } - } - /// - /// StopProcessing - /// - protected override void StopProcessing() - { - if (_waitHandle != null) + if (PassThru) { - _waitHandle.Set(); + WriteObject(_processList, enumerateCollection: true); } } + + /// + /// StopProcessing. + /// + protected override void StopProcessing() => _waitHandle?.Set(); + #endregion Overrides - }//WaitProcessCommand + } #endregion WaitProcessCommand #region StopProcessCommand /// - /// This class implements the stop-process command + /// This class implements the stop-process command. /// /// /// Processes will be sorted before being stopped. PM confirms @@ -1114,15 +1048,14 @@ protected override void StopProcessing() /// [Cmdlet(VerbsLifecycle.Stop, "Process", DefaultParameterSetName = "Id", - SupportsShouldProcess = true, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113412")] + SupportsShouldProcess = true, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2097058")] [OutputType(typeof(Process))] public sealed class StopProcessCommand : ProcessBaseCommand { #region Parameters /// - /// Has the list of process names on which to this command will work + /// Has the list of process names on which to this command will work. /// - // [ProcessNameGlobAttribute] [Parameter( ParameterSetName = "Name", Mandatory = true, @@ -1134,6 +1067,7 @@ public string[] Name { return processNames; } + set { processNames = value; @@ -1142,7 +1076,7 @@ public string[] Name } /// - /// gets/sets an array of process IDs + /// Gets/sets an array of process IDs. /// [Parameter( Position = 0, @@ -1155,6 +1089,7 @@ public int[] Id { return processIds; } + set { myMode = MatchMode.ById; @@ -1163,7 +1098,7 @@ public int[] Id } /// - /// gets/sets an array of objects + /// Gets/sets an array of objects. /// [Parameter( Position = 0, @@ -1177,6 +1112,7 @@ public int[] Id { return base.InputObject; } + set { base.InputObject = value; @@ -1191,12 +1127,10 @@ public int[] Id public SwitchParameter PassThru { get { return _passThru; } + set { _passThru = value; } } - - //Addition by v-ramch Mar 18 2008 - //Added force parameter /// /// Specifies whether to force a process to kill /// even if it has dependent services. @@ -1231,7 +1165,10 @@ protected override void ProcessRecord() SafeGetProcessName(process), SafeGetProcessId(process)); - if (!ShouldProcess(targetString)) { continue; } + if (!ShouldProcess(targetString)) + { + continue; + } try { @@ -1255,7 +1192,6 @@ protected override void ProcessRecord() } catch (Win32Exception ex) { - // This process could not be stopped, so write a non-terminating error. WriteNonTerminatingError( process, ex, ProcessResources.CouldNotStopProcess, "CouldNotStopProcess", ErrorCategory.CloseError); @@ -1264,8 +1200,7 @@ protected override void ProcessRecord() try { - //check if the process is current process and kill it at last - if (Process.GetCurrentProcess().Id == SafeGetProcessId(process)) + if (Environment.ProcessId == SafeGetProcessId(process)) { _shouldKillCurrentProcess = true; continue; @@ -1273,7 +1208,6 @@ protected override void ProcessRecord() if (Platform.IsWindows && !Force) { - // Check if the process is owned by current user if (!IsProcessOwnedByCurrentUser(process)) { string message = StringUtil.Format( @@ -1287,13 +1221,12 @@ protected override void ProcessRecord() } } - //if the process is svchost stop all the dependent services before killing process - if (string.Equals(SafeGetProcessName(process), "SVCHOST", StringComparison.CurrentCultureIgnoreCase)) + // If the process is svchost stop all the dependent services before killing process + if (string.Equals(SafeGetProcessName(process), "SVCHOST", StringComparison.OrdinalIgnoreCase)) { StopDependentService(process); } - // kill the process if (!process.HasExited) { process.Kill(); @@ -1303,8 +1236,6 @@ protected override void ProcessRecord() { if (!TryHasExited(process)) { - // This process could not be stopped, - // so write a non-terminating error. WriteNonTerminatingError( process, exception, ProcessResources.CouldNotStopProcess, "CouldNotStopProcess", ErrorCategory.CloseError); @@ -1315,8 +1246,6 @@ protected override void ProcessRecord() { if (!TryHasExited(process)) { - // This process could not be stopped, - // so write a non-terminating error. WriteNonTerminatingError( process, exception, ProcessResources.CouldNotStopProcess, "CouldNotStopProcess", ErrorCategory.CloseError); @@ -1327,8 +1256,7 @@ protected override void ProcessRecord() if (PassThru) WriteObject(process); } - } // ProcessRecord - + } /// /// Kill the current process here. @@ -1339,31 +1267,31 @@ protected override void EndProcessing() { StopProcess(Process.GetCurrentProcess()); } - }//EndProcessing + } #endregion Overrides #region Private /// - /// should the current powershell process to be killed + /// Should the current powershell process to be killed. /// private bool _shouldKillCurrentProcess; /// - /// Boolean variables to display the warning using shouldcontinue + /// Boolean variables to display the warning using ShouldContinue. /// - private bool _yesToAll,_noToAll; + private bool _yesToAll, _noToAll; /// - /// Current windows user name + /// Current windows user name. /// private string _currentUserName; /// - /// gets the owner of the process + /// Gets the owner of the process. /// /// - /// returns the owner + /// Returns the owner. private bool IsProcessOwnedByCurrentUser(Process process) { const uint TOKEN_QUERY = 0x0008; @@ -1382,30 +1310,33 @@ private bool IsProcessOwnedByCurrentUser(Process process) using (var processUser = new WindowsIdentity(ph)) { - return string.Equals(processUser.Name, _currentUserName, StringComparison.CurrentCultureIgnoreCase); + return string.Equals(processUser.Name, _currentUserName, StringComparison.OrdinalIgnoreCase); } } } catch (IdentityNotMappedException) { - //Catching IdentityMappedException - //Need not throw error. + // Catching IdentityMappedException + // Need not throw error. } catch (ArgumentException) { - //Catching ArgumentException. In Win2k3 Token is zero - //Need not throw error. + // Catching ArgumentException. In Win2k3 Token is zero + // Need not throw error. } finally { - if (ph != IntPtr.Zero) { Win32Native.CloseHandle(ph); } + if (ph != IntPtr.Zero) + { + Win32Native.CloseHandle(ph); + } } return false; } /// - /// Stop the service that depends on the process and its child services + /// Stop the service that depends on the process and its child services. /// /// private void StopDependentService(Process process) @@ -1423,7 +1354,6 @@ private void StopDependentService(Process process) string serviceName = oService.CimInstanceProperties["Name"].Value.ToString(); using (var service = new System.ServiceProcess.ServiceController(serviceName)) { - //try stopping the service, if cant we are not writing exception try { service.Stop(); @@ -1445,10 +1375,10 @@ private void StopDependentService(Process process) } /// - /// stops the given process throws non terminating error if cant - /// process to be stopped - /// true if process stopped successfully else false + /// Stops the given process throws non terminating error if can't. /// + /// Process to be stopped. + /// True if process stopped successfully else false. private void StopProcess(Process process) { Exception exception = null; @@ -1468,7 +1398,7 @@ private void StopProcess(Process process) exception = e; } - if (null != exception) + if (exception != null) { if (!TryHasExited(process)) { @@ -1482,14 +1412,14 @@ private void StopProcess(Process process) } #endregion Private - }//StopProcessCommand + } #endregion StopProcessCommand #region DebugProcessCommand /// - /// This class implements the Debug-process command + /// This class implements the Debug-process command. /// - [Cmdlet(VerbsDiagnostic.Debug, "Process", DefaultParameterSetName = "Name", SupportsShouldProcess = true, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=135206")] + [Cmdlet(VerbsDiagnostic.Debug, "Process", DefaultParameterSetName = "Name", SupportsShouldProcess = true, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2096809")] public sealed class DebugProcessCommand : ProcessBaseCommand { #region Parameters @@ -1511,6 +1441,7 @@ public int[] Id { return processIds; } + set { myMode = MatchMode.ById; @@ -1519,7 +1450,7 @@ public int[] Id } /// - /// Name of the processes to wait on for termination + /// Name of the processes to wait on for termination. /// [Parameter( ParameterSetName = "Name", @@ -1530,7 +1461,11 @@ public int[] Id [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] public string[] Name { - get { return processNames; } + get + { + return processNames; + } + set { myMode = MatchMode.ByName; @@ -1543,11 +1478,10 @@ public string[] Name #region Overrides /// - /// gets the list of process and attach the debugger to the processes + /// Gets the list of process and attach the debugger to the processes. /// protected override void ProcessRecord() { - //for the processes foreach (Process process in MatchingProcesses()) { string targetMessage = StringUtil.Format( @@ -1555,9 +1489,12 @@ protected override void ProcessRecord() SafeGetProcessName(process), SafeGetProcessId(process)); - if (!ShouldProcess(targetMessage)) { continue; } + if (!ShouldProcess(targetMessage)) + { + continue; + } - // sometimes Idle process has processid zero,so handle that because we cannot attach debugger to it. + // Sometimes Idle process has processid zero,so handle that because we cannot attach debugger to it. if (process.Id == 0) { WriteNonTerminatingError( @@ -1570,7 +1507,10 @@ protected override void ProcessRecord() { // If the process has exited, we skip it. If the process is from a remote // machine, then we generate a non-terminating error. - if (process.HasExited) { continue; } + if (process.HasExited) + { + continue; + } } catch (NotSupportedException ex) { @@ -1590,12 +1530,12 @@ protected override void ProcessRecord() AttachDebuggerToProcess(process); } - } // ProcessRecord + } #endregion Overrides /// - /// Attach debugger to the process + /// Attach debugger to the process. /// private void AttachDebuggerToProcess(Process process) { @@ -1621,8 +1561,7 @@ private void AttachDebuggerToProcess(Process process) } catch (CimException e) { - string message = e.Message; - if (!string.IsNullOrEmpty(message)) { message = message.Trim(); } + string message = e.Message?.Trim(); var errorRecord = new ErrorRecord( new InvalidOperationException(StringUtil.Format(ProcessResources.DebuggerError, message)), @@ -1634,20 +1573,22 @@ private void AttachDebuggerToProcess(Process process) } /// - /// Map the return code from 'AttachDebugger' to error message + /// Map the return code from 'AttachDebugger' to error message. /// - private string MapReturnCodeToErrorMessage(int returnCode) + private static string MapReturnCodeToErrorMessage(int returnCode) { - string errorMessage = string.Empty; - switch (returnCode) + string errorMessage = returnCode switch { - case 2: errorMessage = ProcessResources.AttachDebuggerReturnCode2; break; - case 3: errorMessage = ProcessResources.AttachDebuggerReturnCode3; break; - case 8: errorMessage = ProcessResources.AttachDebuggerReturnCode8; break; - case 9: errorMessage = ProcessResources.AttachDebuggerReturnCode9; break; - case 21: errorMessage = ProcessResources.AttachDebuggerReturnCode21; break; - default: Diagnostics.Assert(false, "Unreachable code."); break; - } + 2 => ProcessResources.AttachDebuggerReturnCode2, + 3 => ProcessResources.AttachDebuggerReturnCode3, + 8 => ProcessResources.AttachDebuggerReturnCode8, + 9 => ProcessResources.AttachDebuggerReturnCode9, + 21 => ProcessResources.AttachDebuggerReturnCode21, + _ => string.Empty + }; + + Diagnostics.Assert(!string.IsNullOrEmpty(errorMessage), "Error message should not be null or empty."); + return errorMessage; } } @@ -1656,38 +1597,35 @@ private string MapReturnCodeToErrorMessage(int returnCode) #region StartProcessCommand /// - /// This class implements the Start-process command + /// This class implements the Start-process command. /// - [Cmdlet(VerbsLifecycle.Start, "Process", DefaultParameterSetName = "Default", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=135261")] + [Cmdlet(VerbsLifecycle.Start, "Process", DefaultParameterSetName = "Default", SupportsShouldProcess = true, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2097141")] [OutputType(typeof(Process))] public sealed class StartProcessCommand : PSCmdlet, IDisposable { - private ManualResetEvent _waithandle = null; + private readonly CancellationTokenSource _cancellationTokenSource = new(); private bool _isDefaultSetParameterSpecified = false; #region Parameters /// - /// Path/FileName of the process to start + /// Path/FileName of the process to start. /// [Parameter(Mandatory = true, Position = 0)] [ValidateNotNullOrEmpty] - [Alias("PSPath")] + [Alias("PSPath", "Path")] public string FilePath { get; set; } - /// - /// Arguments for the process + /// Arguments for the process. /// [Parameter(Position = 1)] [Alias("Args")] - [ValidateNotNullOrEmpty] [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] public string[] ArgumentList { get; set; } - /// - /// Credentials for the process + /// Credentials for the process. /// [Parameter(ParameterSetName = "Default")] [Alias("RunAs")] @@ -1695,170 +1633,226 @@ public sealed class StartProcessCommand : PSCmdlet, IDisposable [Credential] public PSCredential Credential { - get { return _credential; } + get + { + return _credential; + } + set { _credential = value; _isDefaultSetParameterSpecified = true; } } + private PSCredential _credential; /// - /// working directory of the process + /// Working directory of the process. /// [Parameter] [ValidateNotNullOrEmpty] public string WorkingDirectory { get; set; } - /// - /// load user profile from registry + /// Load user profile from registry. /// [Parameter(ParameterSetName = "Default")] [Alias("Lup")] public SwitchParameter LoadUserProfile { - get { return _loaduserprofile; } + get + { + return _loaduserprofile; + } + set { _loaduserprofile = value; _isDefaultSetParameterSpecified = true; } } + private SwitchParameter _loaduserprofile = SwitchParameter.Present; /// - /// starts process in a new window + /// Starts process in the current console window. /// [Parameter(ParameterSetName = "Default")] [Alias("nnw")] public SwitchParameter NoNewWindow { - get { return _nonewwindow; } + get + { + return _nonewwindow; + } + set { _nonewwindow = value; _isDefaultSetParameterSpecified = true; } } + private SwitchParameter _nonewwindow; /// - /// passthru parameter + /// PassThru parameter. /// [Parameter] public SwitchParameter PassThru { get; set; } - /// - /// Redirect error + /// Redirect error. /// [Parameter(ParameterSetName = "Default")] [Alias("RSE")] [ValidateNotNullOrEmpty] public string RedirectStandardError { - get { return _redirectstandarderror; } + get + { + return _redirectstandarderror; + } + set { _redirectstandarderror = value; _isDefaultSetParameterSpecified = true; } } - private string _redirectstandarderror; + private string _redirectstandarderror; /// - /// Redirect input + /// Redirect input. /// [Parameter(ParameterSetName = "Default")] [Alias("RSI")] [ValidateNotNullOrEmpty] public string RedirectStandardInput { - get { return _redirectstandardinput; } + get + { + return _redirectstandardinput; + } + set { _redirectstandardinput = value; _isDefaultSetParameterSpecified = true; } } - private string _redirectstandardinput; + private string _redirectstandardinput; /// - /// Redirect output + /// Redirect output. /// [Parameter(ParameterSetName = "Default")] [Alias("RSO")] [ValidateNotNullOrEmpty] public string RedirectStandardOutput { - get { return _redirectstandardoutput; } + get + { + return _redirectstandardoutput; + } + set { _redirectstandardoutput = value; _isDefaultSetParameterSpecified = true; } } + private string _redirectstandardoutput; /// - /// Verb + /// Verb. /// /// - /// The 'Verb' parameter is not supported in OneCore PowerShell - /// because 'UseShellExecute' is not supported in CoreCLR. + /// The 'Verb' parameter is only supported on Windows Desktop. /// [Parameter(ParameterSetName = "UseShellExecute")] [ValidateNotNullOrEmpty] + [ArgumentCompleter(typeof(VerbArgumentCompleter))] public string Verb { get; set; } /// - /// Window style of the process window + /// Window style of the process window. /// [Parameter] [ValidateNotNullOrEmpty] public ProcessWindowStyle WindowStyle { - get { return _windowstyle; } - set + get + { + return _windowstyle; + } + + set { _windowstyle = value; _windowstyleSpecified = true; } } + private ProcessWindowStyle _windowstyle = ProcessWindowStyle.Normal; private bool _windowstyleSpecified = false; /// - /// wait for th eprocess to terminate + /// Wait for the process to terminate. /// [Parameter] public SwitchParameter Wait { get; set; } /// - /// Default Environment + /// Default Environment. /// [Parameter(ParameterSetName = "Default")] public SwitchParameter UseNewEnvironment { - get { return _UseNewEnvironment; } + get + { + return _UseNewEnvironment; + } + set { _UseNewEnvironment = value; _isDefaultSetParameterSpecified = true; } } + private SwitchParameter _UseNewEnvironment; + /// + /// Gets or sets the environment variables for the process. + /// + [Parameter] + public Hashtable Environment + { + get + { + return _environment; + } + + set + { + _environment = value; + _isDefaultSetParameterSpecified = true; + } + } + + private Hashtable _environment; + #endregion #region overrides /// - /// BeginProcessing + /// BeginProcessing. /// protected override void BeginProcessing() { @@ -1871,7 +1865,7 @@ protected override void BeginProcessing() if (_nonewwindow && _windowstyleSpecified) { message = StringUtil.Format(ProcessResources.ContradictParametersSpecified, "-NoNewWindow", "-WindowStyle"); - ErrorRecord er = new ErrorRecord(new InvalidOperationException(message), "InvalidOperationException", ErrorCategory.InvalidOperation, null); + ErrorRecord er = new(new InvalidOperationException(message), "InvalidOperationException", ErrorCategory.InvalidOperation, null); WriteError(er); return; } @@ -1889,17 +1883,16 @@ protected override void BeginProcessing() if (!string.IsNullOrEmpty(message)) { - ErrorRecord er = new ErrorRecord(new NotSupportedException(message), "NotSupportedException", ErrorCategory.NotImplemented, null); + ErrorRecord er = new(new NotSupportedException(message), "NotSupportedException", ErrorCategory.NotImplemented, null); ThrowTerminatingError(er); } } - //create an instance of the ProcessStartInfo Class - ProcessStartInfo startInfo = new ProcessStartInfo(); - //use ShellExecute by default if we are running on full windows SKUs + ProcessStartInfo startInfo = new(); + // Use ShellExecute by default if we are running on full windows SKUs startInfo.UseShellExecute = Platform.IsWindowsDesktop; - //Path = Mandatory parameter -> Will not be empty. + // Path = Mandatory parameter -> Will not be empty. try { CommandInfo cmdinfo = CommandDiscovery.LookupCommandInfo( @@ -1910,39 +1903,45 @@ protected override void BeginProcessing() } catch (CommandNotFoundException) { + // codeql[cs/microsoft/command-line-injection-shell-execution] - This is expected Poweshell behavior where user inputted paths are supported for the context of this method. The user assumes trust for the file path they are specifying and the process is on the user's system except for remoting in which case restricted remoting security guidelines should be used. startInfo.FileName = FilePath; +#if UNIX + // Arguments are passed incorrectly to the executable used for ShellExecute and not to filename https://github.com/dotnet/corefx/issues/30718 + // so don't use ShellExecute if arguments are specified + + // Linux relies on `xdg-open` and macOS relies on `open` which behave differently than Windows ShellExecute when running console commands + // as a new console will be opened. So to avoid that, we only use ShellExecute on non-Windows if the filename is not an actual command (like a URI) + startInfo.UseShellExecute = (ArgumentList == null); +#endif } - //Arguments + if (ArgumentList != null) { - StringBuilder sb = new StringBuilder(); - foreach (string str in ArgumentList) - { - sb.Append(str); - sb.Append(' '); - } - startInfo.Arguments = sb.ToString(); ; + startInfo.Arguments = string.Join(' ', ArgumentList); } - - //WorkingDirectory if (WorkingDirectory != null) { - //WorkingDirectory -> Not Exist -> Throw Error + // WorkingDirectory -> Not Exist -> Throw Error WorkingDirectory = ResolveFilePath(WorkingDirectory); if (!Directory.Exists(WorkingDirectory)) { message = StringUtil.Format(ProcessResources.InvalidInput, "WorkingDirectory"); - ErrorRecord er = new ErrorRecord(new DirectoryNotFoundException(message), "DirectoryNotFoundException", ErrorCategory.InvalidOperation, null); + ErrorRecord er = new(new DirectoryNotFoundException(message), "DirectoryNotFoundException", ErrorCategory.InvalidOperation, null); WriteError(er); return; } + startInfo.WorkingDirectory = WorkingDirectory; } else { - //Working Directory not specified -> Assign Current Path. - startInfo.WorkingDirectory = ResolveFilePath(this.SessionState.Path.CurrentFileSystemLocation.Path); + // Working Directory not specified -> Assign Current Path, but only if it still exists + var currentDirectory = PathUtils.ResolveFilePath(this.SessionState.Path.CurrentFileSystemLocation.Path, this, isLiteralPath: true); + if (Directory.Exists(currentDirectory)) + { + startInfo.WorkingDirectory = currentDirectory; + } } if (this.ParameterSetName.Equals("Default")) @@ -1952,32 +1951,34 @@ protected override void BeginProcessing() startInfo.UseShellExecute = false; } - //UseNewEnvironment if (_UseNewEnvironment) { startInfo.EnvironmentVariables.Clear(); - LoadEnvironmentVariable(startInfo, Environment.GetEnvironmentVariables(EnvironmentVariableTarget.Machine)); - LoadEnvironmentVariable(startInfo, Environment.GetEnvironmentVariables(EnvironmentVariableTarget.User)); + LoadEnvironmentVariable(startInfo, System.Environment.GetEnvironmentVariables(EnvironmentVariableTarget.Machine)); + LoadEnvironmentVariable(startInfo, System.Environment.GetEnvironmentVariables(EnvironmentVariableTarget.User)); + } + + if (_environment != null) + { + LoadEnvironmentVariable(startInfo, _environment); } - //WindowStyle startInfo.WindowStyle = _windowstyle; - //NewWindow - if (_nonewwindow) + // When starting a process as another user, the 'CreateNoWindow' property value is ignored and a new window is created. + // See details at https://learn.microsoft.com/dotnet/api/system.diagnostics.processstartinfo.createnowindow?view=net-9.0#remarks + if (_nonewwindow && _credential is null) { startInfo.CreateNoWindow = _nonewwindow; } #if !UNIX - //LoadUserProfile. startInfo.LoadUserProfile = _loaduserprofile; #endif if (_credential != null) { - //Gets NetworkCredentials NetworkCredential nwcredential = _credential.GetNetworkCredential(); startInfo.UserName = nwcredential.UserName; - if (String.IsNullOrEmpty(nwcredential.Domain)) + if (string.IsNullOrEmpty(nwcredential.Domain)) { startInfo.Domain = "."; } @@ -1985,59 +1986,60 @@ protected override void BeginProcessing() { startInfo.Domain = nwcredential.Domain; } + startInfo.Password = _credential.Password; } - //RedirectionInput File Check -> Not Exist -> Throw Error + // RedirectionInput File Check -> Not Exist -> Throw Error if (_redirectstandardinput != null) { _redirectstandardinput = ResolveFilePath(_redirectstandardinput); if (!File.Exists(_redirectstandardinput)) { message = StringUtil.Format(ProcessResources.InvalidInput, "RedirectStandardInput '" + this.RedirectStandardInput + "'"); - ErrorRecord er = new ErrorRecord(new FileNotFoundException(message), "FileNotFoundException", ErrorCategory.InvalidOperation, null); + ErrorRecord er = new(new FileNotFoundException(message), "FileNotFoundException", ErrorCategory.InvalidOperation, null); WriteError(er); return; } } - //RedirectionInput == RedirectionOutput -> Throw Error + // RedirectionInput == RedirectionOutput -> Throw Error if (_redirectstandardinput != null && _redirectstandardoutput != null) { _redirectstandardinput = ResolveFilePath(_redirectstandardinput); _redirectstandardoutput = ResolveFilePath(_redirectstandardoutput); - if (_redirectstandardinput.Equals(_redirectstandardoutput, StringComparison.CurrentCultureIgnoreCase)) + if (_redirectstandardinput.Equals(_redirectstandardoutput, StringComparison.OrdinalIgnoreCase)) { message = StringUtil.Format(ProcessResources.DuplicateEntry, "RedirectStandardInput", "RedirectStandardOutput"); - ErrorRecord er = new ErrorRecord(new InvalidOperationException(message), "InvalidOperationException", ErrorCategory.InvalidOperation, null); + ErrorRecord er = new(new InvalidOperationException(message), "InvalidOperationException", ErrorCategory.InvalidOperation, null); WriteError(er); return; } } - //RedirectionInput == RedirectionError -> Throw Error + // RedirectionInput == RedirectionError -> Throw Error if (_redirectstandardinput != null && _redirectstandarderror != null) { _redirectstandardinput = ResolveFilePath(_redirectstandardinput); _redirectstandarderror = ResolveFilePath(_redirectstandarderror); - if (_redirectstandardinput.Equals(_redirectstandarderror, StringComparison.CurrentCultureIgnoreCase)) + if (_redirectstandardinput.Equals(_redirectstandarderror, StringComparison.OrdinalIgnoreCase)) { message = StringUtil.Format(ProcessResources.DuplicateEntry, "RedirectStandardInput", "RedirectStandardError"); - ErrorRecord er = new ErrorRecord(new InvalidOperationException(message), "InvalidOperationException", ErrorCategory.InvalidOperation, null); + ErrorRecord er = new(new InvalidOperationException(message), "InvalidOperationException", ErrorCategory.InvalidOperation, null); WriteError(er); return; } } - //RedirectionOutput == RedirectionError -> Throw Error + // RedirectionOutput == RedirectionError -> Throw Error if (_redirectstandardoutput != null && _redirectstandarderror != null) { _redirectstandarderror = ResolveFilePath(_redirectstandarderror); _redirectstandardoutput = ResolveFilePath(_redirectstandardoutput); - if (_redirectstandardoutput.Equals(_redirectstandarderror, StringComparison.CurrentCultureIgnoreCase)) + if (_redirectstandardoutput.Equals(_redirectstandarderror, StringComparison.OrdinalIgnoreCase)) { message = StringUtil.Format(ProcessResources.DuplicateEntry, "RedirectStandardOutput", "RedirectStandardError"); - ErrorRecord er = new ErrorRecord(new InvalidOperationException(message), "InvalidOperationException", ErrorCategory.InvalidOperation, null); + ErrorRecord er = new(new InvalidOperationException(message), "InvalidOperationException", ErrorCategory.InvalidOperation, null); WriteError(er); return; } @@ -2045,16 +2047,90 @@ protected override void BeginProcessing() } else if (ParameterSetName.Equals("UseShellExecute")) { - //Verb - if (Verb != null) { startInfo.Verb = Verb; } - //WindowStyle + if (Verb != null) + { + startInfo.Verb = Verb; + } + startInfo.WindowStyle = _windowstyle; } - //Starts the Process - Process process = Start(startInfo); + string targetMessage = StringUtil.Format(ProcessResources.StartProcessTarget, startInfo.FileName, startInfo.Arguments.Trim()); + if (!ShouldProcess(targetMessage)) + { + return; + } + + Process process = null; + +#if !UNIX + using JobProcessCollection jobObject = new(); + bool? jobAssigned = null; +#endif + if (startInfo.UseShellExecute) + { + process = StartWithShellExecute(startInfo); + } + else + { +#if UNIX + process = new Process() { StartInfo = startInfo }; + SetupInputOutputRedirection(process); + process.Start(); + if (process.StartInfo.RedirectStandardOutput) + { + process.BeginOutputReadLine(); + } + + if (process.StartInfo.RedirectStandardError) + { + process.BeginErrorReadLine(); + } + + if (process.StartInfo.RedirectStandardInput) + { + WriteToStandardInput(process); + } +#else + using ProcessInformation processInfo = StartWithCreateProcess(startInfo); + process = Process.GetProcessById(processInfo.ProcessId); + + // Starting a process as another user might make it impossible + // to get the process handle from the S.D.Process object. Use + // the ALL_ACCESS token from CreateProcess here to setup the + // job object assignment early if -Wait was specified. + // https://github.com/PowerShell/PowerShell/issues/17033 + if (Wait) + { + jobAssigned = jobObject.AssignProcessToJobObject(processInfo.Process); + } + + // Since the process wasn't spawned by .NET, we need to trigger .NET to get a lock on the handle of the process. + // Otherwise, accessing properties like `ExitCode` will throw the following exception: + // "Process was not started by this object, so requested information cannot be determined." + // Fetching the process handle will trigger the `Process` object to update its internal state by calling `SetProcessHandle`, + // the result is discarded as it's not used later in this code. + try + { + _ = process.Handle; + } + catch (Win32Exception e) + { + // If the caller was not an admin and the process was started with another user's credentials .NET + // won't be able to retrieve the process handle. As this is not a critical failure we treat this as + // a warning. + if (PassThru) + { + string msg = StringUtil.Format(ProcessResources.FailedToCreateProcessObject, e.Message); + WriteDebug(msg); + } + } + + // Resume the process now that is has been set up. + processInfo.Resume(); +#endif + } - //Wait and Passthru Implementation. if (PassThru.IsPresent) { if (process != null) @@ -2064,7 +2140,7 @@ protected override void BeginProcessing() else { message = StringUtil.Format(ProcessResources.CannotStarttheProcess); - ErrorRecord er = new ErrorRecord(new InvalidOperationException(message), "InvalidOperationException", ErrorCategory.InvalidOperation, null); + ErrorRecord er = new(new InvalidOperationException(message), "InvalidOperationException", ErrorCategory.InvalidOperation, null); ThrowTerminatingError(er); } } @@ -2076,23 +2152,20 @@ protected override void BeginProcessing() if (!process.HasExited) { #if UNIX - process.WaitForExit(); + process.WaitForExitAsync(_cancellationTokenSource.Token).GetAwaiter().GetResult(); #else - _waithandle = new ManualResetEvent(false); - - // Create and start the job object - ProcessCollection jobObject = new ProcessCollection(); - if (jobObject.AssignProcessToJobObject(process)) + // Add the process to the job, this may have already + // been done in StartWithCreateProcess. + if (jobAssigned == true || (jobAssigned is null && jobObject.AssignProcessToJobObject(process.SafeHandle))) { // Wait for the job object to finish - jobObject.WaitOne(_waithandle); + jobObject.WaitForExit(_cancellationTokenSource.Token); } - else if (!process.HasExited) + else { // WinBlue: 27537 Start-Process -Wait doesn't work in a remote session on Windows 7 or lower. - process.Exited += new EventHandler(myProcess_Exited); - process.EnableRaisingEvents = true; - process.WaitForExit(); + // A Remote session is in it's own job and nested job support was only added in Windows 8/Server 2012. + process.WaitForExitAsync(_cancellationTokenSource.Token).GetAwaiter().GetResult(); } #endif } @@ -2100,7 +2173,7 @@ protected override void BeginProcessing() else { message = StringUtil.Format(ProcessResources.CannotStarttheProcess); - ErrorRecord er = new ErrorRecord(new InvalidOperationException(message), "InvalidOperationException", ErrorCategory.InvalidOperation, null); + ErrorRecord er = new(new InvalidOperationException(message), "InvalidOperationException", ErrorCategory.InvalidOperation, null); ThrowTerminatingError(er); } } @@ -2108,20 +2181,14 @@ protected override void BeginProcessing() /// /// Implements ^c, after creating a process. /// - protected override void StopProcessing() - { - if (_waithandle != null) - { - _waithandle.Set(); - } - } + protected override void StopProcessing() => _cancellationTokenSource.Cancel(); #endregion #region IDisposable Overrides /// - /// Dispose WaitHandle used to honor -Wait parameter + /// Dispose WaitHandle used to honor -Wait parameter. /// public void Dispose() { @@ -2131,35 +2198,20 @@ public void Dispose() private void Dispose(bool isDisposing) { - if (_waithandle != null) - { - _waithandle.Dispose(); - _waithandle = null; - } + _cancellationTokenSource.Dispose(); } #endregion #region Private Methods - /// - /// When Process exits the wait handle is set. - /// - private void myProcess_Exited(object sender, System.EventArgs e) - { - if (_waithandle != null) - { - _waithandle.Set(); - } - } - private string ResolveFilePath(string path) { string filepath = PathUtils.ResolveFilePath(path, this); return filepath; } - private void LoadEnvironmentVariable(ProcessStartInfo startinfo, IDictionary EnvironmentVariables) + private static void LoadEnvironmentVariable(ProcessStartInfo startinfo, IDictionary EnvironmentVariables) { var processEnvironment = startinfo.EnvironmentVariables; foreach (DictionaryEntry entry in EnvironmentVariables) @@ -2168,48 +2220,23 @@ private void LoadEnvironmentVariable(ProcessStartInfo startinfo, IDictionary Env { processEnvironment.Remove(entry.Key.ToString()); } - if (entry.Key.ToString().Equals("PATH")) - { - processEnvironment.Add(entry.Key.ToString(), Environment.GetEnvironmentVariable(entry.Key.ToString(), EnvironmentVariableTarget.Machine) + ";" + Environment.GetEnvironmentVariable(entry.Key.ToString(), EnvironmentVariableTarget.User)); - } - else - { - processEnvironment.Add(entry.Key.ToString(), entry.Value.ToString()); - } - } - } - private Process Start(ProcessStartInfo startInfo) - { + if (entry.Value != null) + { + if (entry.Key.ToString().Equals("PATH")) + { #if UNIX - Process process = new Process() { StartInfo = startInfo }; - SetupInputOutputRedirection(process); - process.Start(); - if (process.StartInfo.RedirectStandardOutput) - { - process.BeginOutputReadLine(); - } - if (process.StartInfo.RedirectStandardError) - { - process.BeginErrorReadLine(); - } - if (process.StartInfo.RedirectStandardInput) - { - WriteToStandardInput(process); - } - return process; + processEnvironment.Add(entry.Key.ToString(), entry.Value.ToString()); #else - Process process = null; - if (startInfo.UseShellExecute) - { - process = StartWithShellExecute(startInfo); - } - else - { - process = StartWithCreateProcess(startInfo); - } - return process; + processEnvironment.Add(entry.Key.ToString(), entry.Value.ToString() + Path.PathSeparator + System.Environment.GetEnvironmentVariable(entry.Key.ToString(), EnvironmentVariableTarget.Machine) + Path.PathSeparator + System.Environment.GetEnvironmentVariable(entry.Key.ToString(), EnvironmentVariableTarget.User)); #endif + } + else + { + processEnvironment.Add(entry.Key.ToString(), entry.Value.ToString()); + } + } + } } #if UNIX @@ -2218,7 +2245,7 @@ private Process Start(ProcessStartInfo startInfo) private void StdOutputHandler(object sendingProcess, DataReceivedEventArgs outLine) { - if (!String.IsNullOrEmpty(outLine.Data)) + if (!string.IsNullOrEmpty(outLine.Data)) { _outputWriter.WriteLine(outLine.Data); _outputWriter.Flush(); @@ -2227,7 +2254,7 @@ private void StdOutputHandler(object sendingProcess, DataReceivedEventArgs outLi private void StdErrorHandler(object sendingProcess, DataReceivedEventArgs outLine) { - if (!String.IsNullOrEmpty(outLine.Data)) + if (!string.IsNullOrEmpty(outLine.Data)) { _errorWriter.WriteLine(outLine.Data); _errorWriter.Flush(); @@ -2247,14 +2274,8 @@ private void StreamClosing() { Thread.Sleep(1000); - if (_outputWriter != null) - { - _outputWriter.Dispose(); - } - if (_errorWriter != null) - { - _errorWriter.Dispose(); - } + _outputWriter?.Dispose(); + _errorWriter?.Dispose(); } private void SetupInputOutputRedirection(Process p) @@ -2311,53 +2332,51 @@ private void WriteToStandardInput(Process p) string line = reader.ReadToEnd(); writer.WriteLine(line); } + writer.Dispose(); } #else - private SafeFileHandle GetSafeFileHandleForRedirection(string RedirectionPath, uint dwCreationDisposition) - { - System.IntPtr hFileHandle = System.IntPtr.Zero; - ProcessNativeMethods.SECURITY_ATTRIBUTES lpSecurityAttributes = new ProcessNativeMethods.SECURITY_ATTRIBUTES(); - - hFileHandle = ProcessNativeMethods.CreateFileW(RedirectionPath, - ProcessNativeMethods.GENERIC_READ | ProcessNativeMethods.GENERIC_WRITE, - ProcessNativeMethods.FILE_SHARE_WRITE | ProcessNativeMethods.FILE_SHARE_READ, - lpSecurityAttributes, - dwCreationDisposition, - ProcessNativeMethods.FILE_ATTRIBUTE_NORMAL, - System.IntPtr.Zero); - if (hFileHandle == System.IntPtr.Zero) + private SafeFileHandle GetSafeFileHandleForRedirection(string RedirectionPath, FileMode mode) + { + SafeFileHandle sf = null; + try { - int error = Marshal.GetLastWin32Error(); - Win32Exception win32ex = new Win32Exception(error); + sf = File.OpenHandle(RedirectionPath, mode, FileAccess.ReadWrite, FileShare.ReadWrite | FileShare.Inheritable, FileOptions.WriteThrough); + } + catch (Win32Exception win32ex) + { + sf?.Dispose(); string message = StringUtil.Format(ProcessResources.InvalidStartProcess, win32ex.Message); - ErrorRecord er = new ErrorRecord(new InvalidOperationException(message), "InvalidOperationException", ErrorCategory.InvalidOperation, null); + ErrorRecord er = new(new InvalidOperationException(message), "InvalidOperationException", ErrorCategory.InvalidOperation, null); ThrowTerminatingError(er); } - SafeFileHandle sf = new SafeFileHandle(hFileHandle, true); + return sf; } private static StringBuilder BuildCommandLine(string executableFileName, string arguments) { - StringBuilder builder = new StringBuilder(); + StringBuilder builder = new(); string str = executableFileName.Trim(); - bool flag = str.StartsWith("\"", StringComparison.Ordinal) && str.EndsWith("\"", StringComparison.Ordinal); + bool flag = str.StartsWith('"') && str.EndsWith('"'); if (!flag) { - builder.Append("\""); + builder.Append('"'); } + builder.Append(str); if (!flag) { - builder.Append("\""); + builder.Append('"'); } + if (!string.IsNullOrEmpty(arguments)) { - builder.Append(" "); + builder.Append(' '); builder.Append(arguments); } + return builder; } @@ -2369,132 +2388,168 @@ private static byte[] ConvertEnvVarsToByteArray(StringDictionary sd) string[] strArray2 = new string[sd.Count]; sd.Values.CopyTo(strArray2, 0); Array.Sort(array, strArray2, StringComparer.OrdinalIgnoreCase); - StringBuilder builder = new StringBuilder(); + StringBuilder builder = new(); for (int i = 0; i < sd.Count; i++) { - builder.Append(array[i]);// + builder.Append(array[i]); builder.Append('='); builder.Append(strArray2[i]); builder.Append('\0'); } + builder.Append('\0'); // Use Unicode encoding bytes = Encoding.Unicode.GetBytes(builder.ToString()); - if (bytes.Length > 0xffff) + + return bytes; + } + + private void SetStartupInfo(ProcessStartInfo startinfo, ref ProcessNativeMethods.STARTUPINFO lpStartupInfo, ref int creationFlags) + { + // If we are starting a process using the current console window, we need to set its standard handles + // explicitly when they are not redirected because otherwise they won't be set and the new process will + // fail with the "invalid handle" error. + // + // However, if we are starting a process with a new console window, we should not explicitly set those + // standard handles when they are not redirected, but instead let Windows figure out the default to use + // when creating the process. Otherwise, the standard input handles of the current window and the new + // window will get weirdly tied together and cause problems. + bool hasRedirection = startinfo.CreateNoWindow + || _redirectstandardinput is not null + || _redirectstandardoutput is not null + || _redirectstandarderror is not null; + + // RedirectionStandardInput + if (_redirectstandardinput != null) { - throw new InvalidOperationException("EnvironmentBlockTooLong"); + startinfo.RedirectStandardInput = true; + _redirectstandardinput = ResolveFilePath(_redirectstandardinput); + lpStartupInfo.hStdInput = GetSafeFileHandleForRedirection(_redirectstandardinput, FileMode.Open); } - return bytes; + else if (startinfo.CreateNoWindow) + { + lpStartupInfo.hStdInput = new SafeFileHandle( + ProcessNativeMethods.GetStdHandle(-10), + ownsHandle: false); + } + + // RedirectionStandardOutput + if (_redirectstandardoutput != null) + { + startinfo.RedirectStandardOutput = true; + _redirectstandardoutput = ResolveFilePath(_redirectstandardoutput); + lpStartupInfo.hStdOutput = GetSafeFileHandleForRedirection(_redirectstandardoutput, FileMode.Create); + } + else if (startinfo.CreateNoWindow) + { + lpStartupInfo.hStdOutput = new SafeFileHandle( + ProcessNativeMethods.GetStdHandle(-11), + ownsHandle: false); + } + + // RedirectionStandardError + if (_redirectstandarderror != null) + { + startinfo.RedirectStandardError = true; + _redirectstandarderror = ResolveFilePath(_redirectstandarderror); + lpStartupInfo.hStdError = GetSafeFileHandleForRedirection(_redirectstandarderror, FileMode.Create); + } + else if (startinfo.CreateNoWindow) + { + lpStartupInfo.hStdError = new SafeFileHandle( + ProcessNativeMethods.GetStdHandle(-12), + ownsHandle: false); + } + + if (hasRedirection) + { + // Set STARTF_USESTDHANDLES only if there is redirection. + lpStartupInfo.dwFlags = 0x100; + } + + if (startinfo.CreateNoWindow) + { + // No new window: Inherit the parent process's console window + creationFlags = 0x00000000; + } + else + { + // CREATE_NEW_CONSOLE + creationFlags |= 0x00000010; + + // STARTF_USESHOWWINDOW + lpStartupInfo.dwFlags |= 0x00000001; + + // On headless SKUs like NanoServer and IoT, window style can only be the default value 'Normal'. + switch (startinfo.WindowStyle) + { + case ProcessWindowStyle.Normal: + // SW_SHOWNORMAL + lpStartupInfo.wShowWindow = 1; + break; + case ProcessWindowStyle.Minimized: + // SW_SHOWMINIMIZED + lpStartupInfo.wShowWindow = 2; + break; + case ProcessWindowStyle.Maximized: + // SW_SHOWMAXIMIZED + lpStartupInfo.wShowWindow = 3; + break; + case ProcessWindowStyle.Hidden: + // SW_HIDE + lpStartupInfo.wShowWindow = 0; + break; + } + } + + // Create the new process suspended so we have a chance to get a corresponding Process object in case it terminates quickly. + creationFlags |= 0x00000004; } /// /// This method will be used on all windows platforms, both full desktop and headless SKUs. /// - private Process StartWithCreateProcess(ProcessStartInfo startinfo) + private ProcessInformation StartWithCreateProcess(ProcessStartInfo startinfo) { - ProcessNativeMethods.STARTUPINFO lpStartupInfo = new ProcessNativeMethods.STARTUPINFO(); - SafeNativeMethods.PROCESS_INFORMATION lpProcessInformation = new SafeNativeMethods.PROCESS_INFORMATION(); + ProcessNativeMethods.STARTUPINFO lpStartupInfo = new(); + ProcessNativeMethods.PROCESS_INFORMATION lpProcessInformation = new(); int error = 0; - GCHandle pinnedEnvironmentBlock = new GCHandle(); - string message = String.Empty; + GCHandle pinnedEnvironmentBlock = new(); + IntPtr AddressOfEnvironmentBlock = IntPtr.Zero; + string message = string.Empty; - //building the cmdline with the file name given and it's arguments + // building the cmdline with the file name given and it's arguments StringBuilder cmdLine = BuildCommandLine(startinfo.FileName, startinfo.Arguments); try { - //RedirectionStandardInput - if (_redirectstandardinput != null) - { - startinfo.RedirectStandardInput = true; - _redirectstandardinput = ResolveFilePath(_redirectstandardinput); - lpStartupInfo.hStdInput = GetSafeFileHandleForRedirection(_redirectstandardinput, ProcessNativeMethods.OPEN_EXISTING); - } - else - { - lpStartupInfo.hStdInput = new SafeFileHandle(ProcessNativeMethods.GetStdHandle(-10), false); - } - //RedirectionStandardOutput - if (_redirectstandardoutput != null) - { - startinfo.RedirectStandardOutput = true; - _redirectstandardoutput = ResolveFilePath(_redirectstandardoutput); - lpStartupInfo.hStdOutput = GetSafeFileHandleForRedirection(_redirectstandardoutput, ProcessNativeMethods.CREATE_ALWAYS); - } - else - { - lpStartupInfo.hStdOutput = new SafeFileHandle(ProcessNativeMethods.GetStdHandle(-11), false); - } - //RedirectionStandardError - if (_redirectstandarderror != null) - { - startinfo.RedirectStandardError = true; - _redirectstandarderror = ResolveFilePath(_redirectstandarderror); - lpStartupInfo.hStdError = GetSafeFileHandleForRedirection(_redirectstandarderror, ProcessNativeMethods.CREATE_ALWAYS); - } - else - { - lpStartupInfo.hStdError = new SafeFileHandle(ProcessNativeMethods.GetStdHandle(-12), false); - } - //STARTF_USESTDHANDLES - lpStartupInfo.dwFlags = 0x100; - int creationFlags = 0; - if (startinfo.CreateNoWindow) - { - //No new window: Inherit the parent process's console window - creationFlags = 0x00000000; - } - else - { - //CREATE_NEW_CONSOLE - creationFlags |= 0x00000010; - //STARTF_USESHOWWINDOW - lpStartupInfo.dwFlags |= 0x00000001; - - // On headless SKUs like NanoServer and IoT, window style can only be the default value 'Normal'. - switch (startinfo.WindowStyle) - { - case ProcessWindowStyle.Normal: - //SW_SHOWNORMAL - lpStartupInfo.wShowWindow = 1; - break; - case ProcessWindowStyle.Minimized: - //SW_SHOWMINIMIZED - lpStartupInfo.wShowWindow = 2; - break; - case ProcessWindowStyle.Maximized: - //SW_SHOWMAXIMIZED - lpStartupInfo.wShowWindow = 3; - break; - case ProcessWindowStyle.Hidden: - //SW_HIDE - lpStartupInfo.wShowWindow = 0; - break; - } - } - - // Create the new process suspended so we have a chance to get a corresponding Process object in case it terminates quickly. - creationFlags |= 0x00000004; + SetStartupInfo(startinfo, ref lpStartupInfo, ref creationFlags); - IntPtr AddressOfEnvironmentBlock = IntPtr.Zero; - var environmentVars = startinfo.EnvironmentVariables; - if (environmentVars != null) + // We follow the logic: + // - Ignore `UseNewEnvironment` when we run a process as another user. + // Setting initial environment variables makes sense only for current user. + // - Set environment variables if they present in ProcessStartupInfo. + if (!UseNewEnvironment) { - if (this.UseNewEnvironment) + var environmentVars = startinfo.EnvironmentVariables; + if (environmentVars != null) { // All Windows Operating Systems that we support are Windows NT systems, so we use Unicode for environment. creationFlags |= 0x400; + pinnedEnvironmentBlock = GCHandle.Alloc(ConvertEnvVarsToByteArray(environmentVars), GCHandleType.Pinned); AddressOfEnvironmentBlock = pinnedEnvironmentBlock.AddrOfPinnedObject(); } } + bool flag; if (_credential != null) { + // Run process as another user. ProcessNativeMethods.LogonFlags logonFlags = 0; if (startinfo.LoadUserProfile) { @@ -2505,7 +2560,7 @@ private Process StartWithCreateProcess(ProcessStartInfo startinfo) try { password = (startinfo.Password == null) ? Marshal.StringToCoTaskMemUni(string.Empty) : Marshal.SecureStringToCoTaskMemUnicode(startinfo.Password); - flag = ProcessNativeMethods.CreateProcessWithLogonW(startinfo.UserName, startinfo.Domain, password, logonFlags, null, cmdLine, creationFlags, AddressOfEnvironmentBlock, startinfo.WorkingDirectory, lpStartupInfo, lpProcessInformation); + flag = ProcessNativeMethods.CreateProcessWithLogonW(startinfo.UserName, startinfo.Domain, password, logonFlags, null, cmdLine, creationFlags, AddressOfEnvironmentBlock, startinfo.WorkingDirectory, lpStartupInfo, ref lpProcessInformation); if (!flag) { error = Marshal.GetLastWin32Error(); @@ -2524,13 +2579,14 @@ private Process StartWithCreateProcess(ProcessStartInfo startinfo) } else { - Win32Exception win32ex = new Win32Exception(error); + Win32Exception win32ex = new(error); message = StringUtil.Format(ProcessResources.InvalidStartProcess, win32ex.Message); } - er = er ?? new ErrorRecord(new InvalidOperationException(message), "InvalidOperationException", ErrorCategory.InvalidOperation, null); + er ??= new ErrorRecord(new InvalidOperationException(message), "InvalidOperationException", ErrorCategory.InvalidOperation, null); ThrowTerminatingError(er); } + goto Label_03AE; } finally @@ -2540,28 +2596,40 @@ private Process StartWithCreateProcess(ProcessStartInfo startinfo) Marshal.ZeroFreeCoTaskMemUnicode(password); } } - }//end of if + } + + // Run process as current user. + if (UseNewEnvironment) + { + // All Windows Operating Systems that we support are Windows NT systems, so we use Unicode for environment. + creationFlags |= 0x400; - ProcessNativeMethods.SECURITY_ATTRIBUTES lpProcessAttributes = new ProcessNativeMethods.SECURITY_ATTRIBUTES(); - ProcessNativeMethods.SECURITY_ATTRIBUTES lpThreadAttributes = new ProcessNativeMethods.SECURITY_ATTRIBUTES(); - flag = ProcessNativeMethods.CreateProcess(null, cmdLine, lpProcessAttributes, lpThreadAttributes, true, creationFlags, AddressOfEnvironmentBlock, startinfo.WorkingDirectory, lpStartupInfo, lpProcessInformation); + IntPtr token = WindowsIdentity.GetCurrent().Token; + if (!ProcessNativeMethods.CreateEnvironmentBlock(out AddressOfEnvironmentBlock, token, false)) + { + Win32Exception win32ex = new(error); + message = StringUtil.Format(ProcessResources.InvalidStartProcess, win32ex.Message); + var errorRecord = new ErrorRecord(new InvalidOperationException(message), "InvalidOperationException", ErrorCategory.InvalidOperation, null); + ThrowTerminatingError(errorRecord); + } + } + + ProcessNativeMethods.SECURITY_ATTRIBUTES lpProcessAttributes = new(); + ProcessNativeMethods.SECURITY_ATTRIBUTES lpThreadAttributes = new(); + flag = ProcessNativeMethods.CreateProcess(null, cmdLine, lpProcessAttributes, lpThreadAttributes, true, creationFlags, AddressOfEnvironmentBlock, startinfo.WorkingDirectory, lpStartupInfo, ref lpProcessInformation); if (!flag) { error = Marshal.GetLastWin32Error(); - Win32Exception win32ex = new Win32Exception(error); + Win32Exception win32ex = new(error); message = StringUtil.Format(ProcessResources.InvalidStartProcess, win32ex.Message); - ErrorRecord er = new ErrorRecord(new InvalidOperationException(message), "InvalidOperationException", ErrorCategory.InvalidOperation, null); + ErrorRecord er = new(new InvalidOperationException(message), "InvalidOperationException", ErrorCategory.InvalidOperation, null); ThrowTerminatingError(er); } Label_03AE: - // At this point, we should have a suspended process. Get the .Net Process object, resume the process, and return. - Process result = Process.GetProcessById(lpProcessInformation.dwProcessId); - ProcessNativeMethods.ResumeThread(lpProcessInformation.hThread); - - return result; + return new ProcessInformation(lpProcessInformation); } finally { @@ -2569,11 +2637,15 @@ private Process StartWithCreateProcess(ProcessStartInfo startinfo) { pinnedEnvironmentBlock.Free(); } + else + { + ProcessNativeMethods.DestroyEnvironmentBlock(AddressOfEnvironmentBlock); + } lpStartupInfo.Dispose(); - lpProcessInformation.Dispose(); } } +#endif /// /// This method will be used only on Windows full desktop. @@ -2588,138 +2660,134 @@ private Process StartWithShellExecute(ProcessStartInfo startInfo) catch (Win32Exception ex) { string message = StringUtil.Format(ProcessResources.InvalidStartProcess, ex.Message); - ErrorRecord er = new ErrorRecord(new InvalidOperationException(message), "InvalidOperationException", ErrorCategory.InvalidOperation, null); + ErrorRecord er = new(new InvalidOperationException(message), "InvalidOperationException", ErrorCategory.InvalidOperation, null); ThrowTerminatingError(er); } + return result; } -#endif #endregion } -#if !UNIX /// - /// ProcessCollection is a helper class used by Start-Process -Wait cmdlet to monitor the - /// child processes created by the main process hosted by the Start-process cmdlet. + /// Provides argument completion for Verb parameter. /// - internal class ProcessCollection + public class VerbArgumentCompleter : IArgumentCompleter { /// - /// JobObjectHandle is a reference to the job object used to track - /// the child processes created by the main process hosted by the Start-Process cmdlet. - /// - private Microsoft.PowerShell.Commands.SafeJobHandle _jobObjectHandle; - - /// - /// ProcessCollection constructor. + /// Returns completion results for verb parameter. /// - internal ProcessCollection() + /// The command name. + /// The parameter name. + /// The word to complete. + /// The command AST. + /// The fake bound parameters. + /// List of Completion Results. + public IEnumerable CompleteArgument( + string commandName, + string parameterName, + string wordToComplete, + CommandAst commandAst, + IDictionary fakeBoundParameters) { - IntPtr jobObjectHandleIntPtr = NativeMethods.CreateJobObject(IntPtr.Zero, null); - _jobObjectHandle = new SafeJobHandle(jobObjectHandleIntPtr); - } + // -Verb is not supported on non-Windows platforms as well as Windows headless SKUs + if (!Platform.IsWindowsDesktop) + { + return Array.Empty(); + } - /// - /// Start API assigns the process to the JobObject and starts monitoring - /// the child processes hosted by the process created by Start-Process cmdlet. - /// - internal bool AssignProcessToJobObject(Process process) - { - // Add the process to the job object - bool result = NativeMethods.AssignProcessToJobObject(_jobObjectHandle, process.Handle); - return result; - } + // Completion: Start-Process -FilePath -Verb + if (commandName.Equals("Start-Process", StringComparison.OrdinalIgnoreCase) + && fakeBoundParameters.Contains("FilePath")) + { + string filePath = fakeBoundParameters["FilePath"].ToString(); - /// - /// Checks to see if the JobObject is empty (has no assigned processes). - /// If job is empty the auto reset event supplied as input would be set. - /// - internal void CheckJobStatus(Object stateInfo) - { - ManualResetEvent emptyJobAutoEvent = (ManualResetEvent)stateInfo; - int dwSize = 0; - const int JOB_OBJECT_BASIC_PROCESS_ID_LIST = 3; - JOBOBJECT_BASIC_PROCESS_ID_LIST JobList = new JOBOBJECT_BASIC_PROCESS_ID_LIST(); + // Complete file verbs if extension exists + if (Path.HasExtension(filePath)) + { + return CompleteFileVerbs(wordToComplete, filePath); + } - dwSize = Marshal.SizeOf(JobList); - if (NativeMethods.QueryInformationJobObject(_jobObjectHandle, - JOB_OBJECT_BASIC_PROCESS_ID_LIST, - ref JobList, dwSize, IntPtr.Zero)) - { - if (JobList.NumberOfAssignedProcess == 0) + // Otherwise check if command is an Application to resolve executable full path with extension + // e.g if powershell was given, resolve to powershell.exe to get verbs + using var ps = System.Management.Automation.PowerShell.Create(RunspaceMode.CurrentRunspace); + + var commandInfo = new CmdletInfo("Get-Command", typeof(GetCommandCommand)); + + ps.AddCommand(commandInfo); + ps.AddParameter("Name", filePath); + ps.AddParameter("CommandType", CommandTypes.Application); + + Collection commands = ps.Invoke(); + + // Start-Process & Get-Command select first found application based on PATHEXT environment variable + if (commands.Count >= 1) { - emptyJobAutoEvent.Set(); + return CompleteFileVerbs(wordToComplete, filePath: commands[0].Source); } } + + return Array.Empty(); } /// - /// WaitOne blocks the current thread until the current instance receives a signal, using - /// a System.TimeSpan to measure the time interval and specifying whether to - /// exit the synchronization domain before the wait. + /// Completes file verbs. /// - /// - /// WaitHandle to use for waiting on the job object. - /// - internal void WaitOne(ManualResetEvent waitHandleToUse) - { - TimerCallback jobObjectStatusCb = this.CheckJobStatus; - using (Timer stateTimer = new Timer(jobObjectStatusCb, waitHandleToUse, 0, 1000)) - { - waitHandleToUse.WaitOne(); - } - } + /// The word to complete. + /// The file path to get verbs. + /// List of file verbs to complete. + private static IEnumerable CompleteFileVerbs(string wordToComplete, string filePath) + => CompletionHelpers.GetMatchingResults( + wordToComplete, + possibleCompletionValues: new ProcessStartInfo(filePath).Verbs); } +#if !UNIX /// - /// JOBOBJECT_BASIC_PROCESS_ID_LIST Contains the process identifier list for a job object. - /// If the job is nested, the process identifier list consists of all - /// processes associated with the job and its child jobs. + /// ProcessInformation is a helper class that wraps the native PROCESS_INFORMATION structure + /// returned by CreateProcess or CreateProcessWithLogon. It ensures the process and thread + /// HANDLEs are disposed once it's not needed. /// - [StructLayout(LayoutKind.Sequential)] - internal struct JOBOBJECT_BASIC_PROCESS_ID_LIST + internal sealed class ProcessInformation : IDisposable { - /// - /// The number of process identifiers to be stored in ProcessIdList. - /// - public uint NumberOfAssignedProcess; + public SafeProcessHandle Process { get; } - /// - /// The number of process identifiers returned in the ProcessIdList buffer. - /// If this number is less than NumberOfAssignedProcesses, increase - /// the size of the buffer to accommodate the complete list. - /// - public uint NumberOfProcessIdsInList; + public SafeProcessHandle Thread { get; } - /// - /// A variable-length array of process identifiers returned by this call. - /// Array elements 0 through NumberOfProcessIdsInList minus 1 - /// contain valid process identifiers. - /// - public IntPtr ProcessIdList; - } + public Int32 ProcessId { get; } - internal static class ProcessNativeMethods - { - // Fields - internal static UInt32 GENERIC_READ = 0x80000000; - internal static UInt32 GENERIC_WRITE = 0x40000000; - internal static UInt32 FILE_ATTRIBUTE_NORMAL = 0x80000000; - internal static UInt32 CREATE_ALWAYS = 2; - internal static UInt32 FILE_SHARE_WRITE = 0x00000002; - internal static UInt32 FILE_SHARE_READ = 0x00000001; - internal static UInt32 OF_READWRITE = 0x00000002; - internal static UInt32 OPEN_EXISTING = 3; + public Int32 ThreadId { get; } + internal ProcessInformation(ProcessNativeMethods.PROCESS_INFORMATION info) + { + Process = new(info.hProcess, true); + Thread = new(info.hThread, true); + ProcessId = info.dwProcessId; + ThreadId = info.dwThreadId; + } + + public void Resume() + { + ProcessNativeMethods.ResumeThread(Thread.DangerousGetHandle()); + } - // Methods - // static NativeMethods(); + public void Dispose() + { + Process.Dispose(); + Thread.Dispose(); + GC.SuppressFinalize(this); + } + ~ProcessInformation() => Dispose(); + } - [DllImport(PinvokeDllNames.GetStdHandleDllName, CharSet = CharSet.Ansi, SetLastError = true)] + internal static class ProcessNativeMethods + { + [DllImport(PinvokeDllNames.GetStdHandleDllName, SetLastError = true)] public static extern IntPtr GetStdHandle(int whichHandle); [DllImport(PinvokeDllNames.CreateProcessWithLogonWDllName, CharSet = CharSet.Unicode, SetLastError = true, ExactSpelling = true)] + [return: MarshalAs(UnmanagedType.Bool)] internal static extern bool CreateProcessWithLogonW(string userName, string domain, IntPtr password, @@ -2730,9 +2798,10 @@ internal static extern bool CreateProcessWithLogonW(string userName, IntPtr environmentBlock, [MarshalAs(UnmanagedType.LPWStr)] string lpCurrentDirectory, STARTUPINFO lpStartupInfo, - SafeNativeMethods.PROCESS_INFORMATION lpProcessInformation); + ref PROCESS_INFORMATION lpProcessInformation); [DllImport(PinvokeDllNames.CreateProcessDllName, CharSet = CharSet.Unicode, SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] public static extern bool CreateProcess([MarshalAs(UnmanagedType.LPWStr)] string lpApplicationName, [MarshalAs(UnmanagedType.LPWStr)] StringBuilder lpCommandLine, SECURITY_ATTRIBUTES lpProcessAttributes, @@ -2742,22 +2811,18 @@ public static extern bool CreateProcess([MarshalAs(UnmanagedType.LPWStr)] string IntPtr lpEnvironment, [MarshalAs(UnmanagedType.LPWStr)] string lpCurrentDirectory, STARTUPINFO lpStartupInfo, - SafeNativeMethods.PROCESS_INFORMATION lpProcessInformation); + ref PROCESS_INFORMATION lpProcessInformation); [DllImport(PinvokeDllNames.ResumeThreadDllName, CharSet = CharSet.Unicode, SetLastError = true)] public static extern uint ResumeThread(IntPtr threadHandle); - [DllImport(PinvokeDllNames.CreateFileDllName, CharSet = CharSet.Unicode, SetLastError = true)] - public static extern FileNakedHandle CreateFileW( - [In, MarshalAs(UnmanagedType.LPWStr)] string lpFileName, - DWORD dwDesiredAccess, - DWORD dwShareMode, - ProcessNativeMethods.SECURITY_ATTRIBUTES lpSecurityAttributes, - DWORD dwCreationDisposition, - DWORD dwFlagsAndAttributes, - System.IntPtr hTemplateFile - ); + [DllImport("userenv.dll", CharSet = CharSet.Unicode, SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool CreateEnvironmentBlock(out IntPtr lpEnvironment, IntPtr hToken, bool bInherit); + [DllImport("userenv.dll", CharSet = CharSet.Unicode, SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + public static extern bool DestroyEnvironmentBlock(IntPtr lpEnvironment); [Flags] internal enum LogonFlags @@ -2767,11 +2832,21 @@ internal enum LogonFlags } [StructLayout(LayoutKind.Sequential)] - internal class SECURITY_ATTRIBUTES + internal struct PROCESS_INFORMATION + { + public IntPtr hProcess; + public IntPtr hThread; + public int dwProcessId; + public int dwThreadId; + } + + [StructLayout(LayoutKind.Sequential)] + internal sealed class SECURITY_ATTRIBUTES { public int nLength; public SafeLocalMemHandle lpSecurityDescriptor; public bool bInheritHandle; + public SECURITY_ATTRIBUTES() { this.nLength = 12; @@ -2787,23 +2862,24 @@ internal SafeLocalMemHandle() : base(true) { } - [SecurityPermission(SecurityAction.LinkDemand, UnmanagedCode = true)] + internal SafeLocalMemHandle(IntPtr existingHandle, bool ownsHandle) : base(ownsHandle) { base.SetHandle(existingHandle); } - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success), DllImport(PinvokeDllNames.LocalFreeDllName)] + + [DllImport(PinvokeDllNames.LocalFreeDllName)] private static extern IntPtr LocalFree(IntPtr hMem); + protected override bool ReleaseHandle() { return (LocalFree(base.handle) == IntPtr.Zero); } } - [StructLayout(LayoutKind.Sequential)] - internal class STARTUPINFO + internal sealed class STARTUPINFO { public int cb; public IntPtr lpReserved; @@ -2823,6 +2899,7 @@ internal class STARTUPINFO public SafeFileHandle hStdInput; public SafeFileHandle hStdOutput; public SafeFileHandle hStdError; + public STARTUPINFO() { this.lpReserved = IntPtr.Zero; @@ -2844,11 +2921,13 @@ public void Dispose(bool disposing) this.hStdInput.Dispose(); this.hStdInput = null; } + if ((this.hStdOutput != null) && !this.hStdOutput.IsInvalid) { this.hStdOutput.Dispose(); this.hStdOutput = null; } + if ((this.hStdError != null) && !this.hStdError.IsInvalid) { this.hStdError.Dispose(); @@ -2863,103 +2942,36 @@ public void Dispose() } } } - - internal static class SafeNativeMethods - { - [ReliabilityContract(Consistency.WillNotCorruptState, Cer.Success), DllImport(PinvokeDllNames.CloseHandleDllName, SetLastError = true, ExactSpelling = true)] - public static extern bool CloseHandle(IntPtr handle); - - [StructLayout(LayoutKind.Sequential)] - internal class PROCESS_INFORMATION - { - public IntPtr hProcess; - public IntPtr hThread; - public int dwProcessId; - public int dwThreadId; - - public PROCESS_INFORMATION() - { - this.hProcess = IntPtr.Zero; - this.hThread = IntPtr.Zero; - } - - /// - /// Dispose - /// - public void Dispose() - { - Dispose(true); - } - - /// - /// Dispose - /// - /// - private void Dispose(bool disposing) - { - if (disposing) - { - if (this.hProcess != IntPtr.Zero) - { - CloseHandle(this.hProcess); - this.hProcess = IntPtr.Zero; - } - - if (this.hThread != IntPtr.Zero) - { - CloseHandle(this.hThread); - this.hThread = IntPtr.Zero; - } - } - } - } - } - - [SuppressUnmanagedCodeSecurity] - internal sealed class SafeJobHandle : SafeHandleZeroOrMinusOneIsInvalid - { - internal SafeJobHandle(IntPtr jobHandle) - : base(true) - { - base.SetHandle(jobHandle); - } - - protected override bool ReleaseHandle() - { - return SafeNativeMethods.CloseHandle(base.handle); - } - } #endif #endregion #region ProcessCommandException /// - /// Non-terminating errors occurring in the process noun commands + /// Non-terminating errors occurring in the process noun commands. /// - [Serializable] public class ProcessCommandException : SystemException { #region ctors /// - /// unimplemented standard constructor + /// Unimplemented standard constructor. /// - /// doesn't return + /// Doesn't return. public ProcessCommandException() : base() { throw new NotImplementedException(); } /// - /// standard constructor + /// Standard constructor. /// /// - /// constructed object + /// Constructed object. public ProcessCommandException(string message) : base(message) { } /// - /// standard constructor + /// Standard constructor. /// /// /// @@ -2971,53 +2983,36 @@ public ProcessCommandException(string message, Exception innerException) #region Serialization /// - /// serialization constructor + /// Serialization constructor. /// /// /// - /// constructed object + /// Constructed object. + [Obsolete("Legacy serialization support is deprecated since .NET 8", DiagnosticId = "SYSLIB0051")] protected ProcessCommandException( SerializationInfo info, StreamingContext context) - : base(info, context) { - _processName = info.GetString("ProcessName"); + throw new NotSupportedException(); } - /// - /// Serializer - /// - /// serialization information - /// streaming context - [SecurityPermissionAttribute( - SecurityAction.Demand, - SerializationFormatter = true)] - public override void GetObjectData( - SerializationInfo info, - StreamingContext context) - { - base.GetObjectData(info, context); - - if (info == null) - throw new ArgumentNullException("info"); - info.AddValue("ProcessName", _processName); - } #endregion Serialization #region Properties /// - /// Name of the process which could not be found or operated upon + /// Name of the process which could not be found or operated upon. /// /// public string ProcessName { get { return _processName; } + set { _processName = value; } } - private string _processName = String.Empty; + + private string _processName = string.Empty; #endregion Properties - } // class ProcessCommandException + } #endregion ProcessCommandException -}//Microsoft.PowerShell.Commands - +} diff --git a/src/Microsoft.PowerShell.Commands.Management/commands/management/PropertyCommandBase.cs b/src/Microsoft.PowerShell.Commands.Management/commands/management/PropertyCommandBase.cs index bd707d4cc2e..edc0bbc3a91 100644 --- a/src/Microsoft.PowerShell.Commands.Management/commands/management/PropertyCommandBase.cs +++ b/src/Microsoft.PowerShell.Commands.Management/commands/management/PropertyCommandBase.cs @@ -1,21 +1,20 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +using System; using System.Management.Automation; -using Dbg = System.Management.Automation; namespace Microsoft.PowerShell.Commands { /// - /// The base class for the */property commands + /// The base class for the */property commands. /// public class ItemPropertyCommandBase : CoreCommandWithCredentialsBase { #region Parameters /// - /// Gets or sets the filter parameter + /// Gets or sets the filter parameter. /// [Parameter] public override string Filter @@ -23,16 +22,16 @@ public override string Filter get { return base.Filter; - } // get + } set { base.Filter = value; - } // set - } // Filter + } + } /// - /// Gets or sets the include property + /// Gets or sets the include property. /// [Parameter] public override string[] Include @@ -40,16 +39,16 @@ public override string[] Include get { return base.Include; - } // get + } set { base.Include = value; - } // set - } // Include + } + } /// - /// Gets or sets the exclude property + /// Gets or sets the exclude property. /// [Parameter] public override string[] Exclude @@ -57,22 +56,22 @@ public override string[] Exclude get { return base.Exclude; - } // get + } set { base.Exclude = value; - } // set - } // Exclude + } + } #endregion Parameters #region parameter data /// - /// The path to the item + /// The path to the item. /// - internal string[] paths = new string[0]; + internal string[] paths = Array.Empty(); #endregion parameter data - } // ItemPropertyCommandBase -} // namespace Microsoft.PowerShell.Commands + } +} diff --git a/src/Microsoft.PowerShell.Commands.Management/commands/management/RegisterWMIEventCommand.cs b/src/Microsoft.PowerShell.Commands.Management/commands/management/RegisterWMIEventCommand.cs index 0319b0e8130..0d547abfa9d 100644 --- a/src/Microsoft.PowerShell.Commands.Management/commands/management/RegisterWMIEventCommand.cs +++ b/src/Microsoft.PowerShell.Commands.Management/commands/management/RegisterWMIEventCommand.cs @@ -1,16 +1,15 @@ -// -// Copyright (C) Microsoft. All rights reserved. -// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. using System; using System.Collections; using System.Collections.Generic; -using System.Management.Automation; using System.Collections.ObjectModel; using System.Diagnostics; -using System.Threading; using System.Management; +using System.Management.Automation; using System.Text; +using System.Threading; namespace Microsoft.PowerShell.Commands { @@ -24,42 +23,41 @@ public class RegisterWmiEventCommand : ObjectEventRegistrationBase #region parameters /// - /// The WMI namespace to use + /// The WMI namespace to use. /// [Parameter] [Alias("NS")] public string Namespace { get; set; } = "root\\cimv2"; /// - /// The credential to use + /// The credential to use. /// [Parameter] - [Credential()] + [Credential] public PSCredential Credential { get; set; } /// - /// The ComputerName in which to query + /// The ComputerName in which to query. /// [Parameter] [Alias("Cn")] [ValidateNotNullOrEmpty] public string ComputerName { get; set; } = "localhost"; - /// - /// The WMI class to use + /// The WMI class to use. /// [Parameter(Position = 0, Mandatory = true, ParameterSetName = "class")] public string Class { get; set; } = null; /// - /// The query string to search for objects + /// The query string to search for objects. /// [Parameter(Position = 0, Mandatory = true, ParameterSetName = "query")] public string Query { get; set; } = null; /// - /// Timeout in milliseconds + /// Timeout in milliseconds. /// [Parameter] [Alias("TimeoutMSec")] @@ -69,6 +67,7 @@ public Int64 Timeout { return _timeOut; } + set { _timeOut = value; @@ -87,36 +86,36 @@ private string BuildEventQuery(string objectName) returnValue.Append(objectName); return returnValue.ToString(); } + private string GetScopeString(string computer, string namespaceParameter) { StringBuilder returnValue = new StringBuilder("\\\\"); returnValue.Append(computer); - returnValue.Append("\\"); + returnValue.Append('\\'); returnValue.Append(namespaceParameter); return returnValue.ToString(); } #endregion helper functions - /// - /// Returns the object that generates events to be monitored + /// Returns the object that generates events to be monitored. /// - protected override Object GetSourceObject() + protected override object GetSourceObject() { string wmiQuery = this.Query; if (this.Class != null) { - //Validate class format + // Validate class format for (int i = 0; i < this.Class.Length; i++) { - if (Char.IsLetterOrDigit(this.Class[i]) || this.Class[i].Equals('_')) + if (char.IsLetterOrDigit(this.Class[i]) || this.Class[i].Equals('_')) { continue; } ErrorRecord errorRecord = new ErrorRecord( new ArgumentException( - String.Format( + string.Format( Thread.CurrentThread.CurrentCulture, "Class", this.Class)), "INVALID_QUERY_IDENTIFIER", @@ -135,7 +134,7 @@ protected override Object GetSourceObject() if (this.Credential != null) { System.Net.NetworkCredential cred = this.Credential.GetNetworkCredential(); - if (String.IsNullOrEmpty(cred.Domain)) + if (string.IsNullOrEmpty(cred.Domain)) { conOptions.Username = cred.UserName; } @@ -143,6 +142,7 @@ protected override Object GetSourceObject() { conOptions.Username = cred.Domain + "\\" + cred.UserName; } + conOptions.Password = cred.Password; } @@ -159,9 +159,9 @@ protected override Object GetSourceObject() } /// - /// Returns the event name to be monitored on the input object + /// Returns the event name to be monitored on the input object. /// - protected override String GetSourceObjectEventName() + protected override string GetSourceObjectEventName() { return "EventArrived"; } diff --git a/src/Microsoft.PowerShell.Commands.Management/commands/management/RemovePropertyCommand.cs b/src/Microsoft.PowerShell.Commands.Management/commands/management/RemovePropertyCommand.cs index 67f035b1f27..cc60d91adbe 100644 --- a/src/Microsoft.PowerShell.Commands.Management/commands/management/RemovePropertyCommand.cs +++ b/src/Microsoft.PowerShell.Commands.Management/commands/management/RemovePropertyCommand.cs @@ -1,9 +1,8 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +using System; using System.Management.Automation; -using Dbg = System.Management.Automation; namespace Microsoft.PowerShell.Commands { @@ -11,13 +10,13 @@ namespace Microsoft.PowerShell.Commands /// A command to remove a property from an item. /// [Cmdlet(VerbsCommon.Remove, "ItemProperty", DefaultParameterSetName = "Path", SupportsShouldProcess = true, SupportsTransactions = true, - HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113374")] + HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2097013")] public class RemoveItemPropertyCommand : ItemPropertyCommandBase { #region Parameters /// - /// Gets or sets the path parameter to the command + /// Gets or sets the path parameter to the command. /// [Parameter(Position = 0, ParameterSetName = "Path", Mandatory = true, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true)] @@ -26,50 +25,49 @@ public string[] Path get { return paths; - } // get + } set { paths = value; - } // set - } // Path + } + } /// - /// Gets or sets the literal path parameter to the command + /// Gets or sets the literal path parameter to the command. /// [Parameter(ParameterSetName = "LiteralPath", Mandatory = true, ValueFromPipeline = false, ValueFromPipelineByPropertyName = true)] - [Alias("PSPath")] + [Alias("PSPath", "LP")] public string[] LiteralPath { get { return paths; - } // get + } set { base.SuppressWildcardExpansion = true; paths = value; - } // set - } // LiteralPath + } + } /// - /// The name of the property to create on the item + /// The name of the property to create on the item. /// - /// [Parameter(Mandatory = true, Position = 1, ValueFromPipelineByPropertyName = true)] [Alias("PSProperty")] public string[] Name { get { return _property; } - set { _property = value ?? Utils.EmptyArray(); } + + set { _property = value ?? Array.Empty(); } } /// - /// Gets or sets the force property + /// Gets or sets the force property. /// - /// /// /// Gives the provider guidance on how vigorous it should be about performing /// the operation. If true, the provider should do everything possible to perform @@ -79,11 +77,11 @@ public string[] Name /// the destination is read-only, if force is true, the provider should copy over /// the existing read-only file. If force is false, the provider should write an error. /// - /// [Parameter] public override SwitchParameter Force { get { return base.Force; } + set { base.Force = value; } } @@ -92,16 +90,13 @@ public override SwitchParameter Force /// that require dynamic parameters should override this method and return the /// dynamic parameter object. /// - /// /// /// The context under which the command is running. /// - /// /// /// An object representing the dynamic parameters for the cmdlet or null if there /// are none. /// - /// internal override object GetDynamicParameters(CmdletProviderContext context) { string propertyName = null; @@ -114,8 +109,9 @@ internal override object GetDynamicParameters(CmdletProviderContext context) { return InvokeProvider.Property.RemovePropertyDynamicParameters(Path[0], propertyName, context); } + return InvokeProvider.Property.RemovePropertyDynamicParameters(".", propertyName, context); - } // GetDynamicParameters + } #endregion Parameters @@ -124,14 +120,14 @@ internal override object GetDynamicParameters(CmdletProviderContext context) /// /// The property to be created. /// - private string[] _property = new string[0]; + private string[] _property = Array.Empty(); #endregion parameter data #region Command code /// - /// Removes the property from the item + /// Removes the property from the item. /// protected override void ProcessRecord() { @@ -177,9 +173,8 @@ protected override void ProcessRecord() } } } - } // ProcessRecord + } #endregion Command code - - } // RemoveItemPropertyCommand -} // namespace Microsoft.PowerShell.Commands + } +} diff --git a/src/Microsoft.PowerShell.Commands.Management/commands/management/RemoveWMIObjectCommand.cs b/src/Microsoft.PowerShell.Commands.Management/commands/management/RemoveWMIObjectCommand.cs index ac10af1dc98..cce50ea1563 100644 --- a/src/Microsoft.PowerShell.Commands.Management/commands/management/RemoveWMIObjectCommand.cs +++ b/src/Microsoft.PowerShell.Commands.Management/commands/management/RemoveWMIObjectCommand.cs @@ -1,6 +1,5 @@ -// -// Copyright (C) Microsoft. All rights reserved. -// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. using System; using System.Management; @@ -9,7 +8,7 @@ namespace Microsoft.PowerShell.Commands { /// - /// A command to Remove WMI Object + /// A command to Remove WMI Object. /// [Cmdlet(VerbsCommon.Remove, "WmiObject", DefaultParameterSetName = "class", SupportsShouldProcess = true, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113381", RemotingCapability = RemotingCapability.OwnedByCommand)] @@ -17,31 +16,33 @@ public class RemoveWmiObject : WmiBaseCmdlet { #region Parameters /// - /// The WMI Object to use + /// The WMI Object to use. /// - /// [Parameter(ValueFromPipeline = true, Mandatory = true, ParameterSetName = "object")] public ManagementObject InputObject { get { return _inputObject; } + set { _inputObject = value; } } /// - /// The WMI Path to use + /// The WMI Path to use. /// [Parameter(Mandatory = true, ParameterSetName = "path")] public string Path { get { return _path; } + set { _path = value; } } /// - /// The WMI class to use + /// The WMI class to use. /// [Parameter(Position = 0, Mandatory = true, ParameterSetName = "class")] public string Class { get { return _className; } + set { _className = value; } } @@ -64,6 +65,7 @@ protected override void ProcessRecord() RunAsJob("Remove-WMIObject"); return; } + if (_inputObject != null) { try @@ -72,6 +74,7 @@ protected override void ProcessRecord() { return; } + _inputObject.Delete(); } catch (ManagementException e) @@ -84,6 +87,7 @@ protected override void ProcessRecord() ErrorRecord errorRecord = new ErrorRecord(e, "RemoveWMICOMException", ErrorCategory.InvalidOperation, null); WriteError(errorRecord); } + return; } else @@ -94,13 +98,13 @@ protected override void ProcessRecord() if (_path != null) { mPath = new ManagementPath(_path); - if (String.IsNullOrEmpty(mPath.NamespacePath)) + if (string.IsNullOrEmpty(mPath.NamespacePath)) { mPath.NamespacePath = this.Namespace; } else if (namespaceSpecified) { - //ThrowTerminatingError + // ThrowTerminatingError ThrowTerminatingError(new ErrorRecord( new InvalidOperationException(), "NamespaceSpecifiedWithPath", @@ -110,19 +114,21 @@ protected override void ProcessRecord() if (mPath.Server != "." && serverNameSpecified) { - //ThrowTerminatingError + // ThrowTerminatingError ThrowTerminatingError(new ErrorRecord( new InvalidOperationException(), "ComputerNameSpecifiedWithPath", ErrorCategory.InvalidOperation, this.ComputerName)); } + if (!(mPath.Server == "." && serverNameSpecified)) { string[] serverName = new string[] { mPath.Server }; ComputerName = serverName; } } + foreach (string name in ComputerName) { try @@ -140,6 +146,7 @@ protected override void ProcessRecord() ManagementObject mInstance = new ManagementObject(mPath); mObject = mInstance; } + ManagementScope mScope = new ManagementScope(mPath, options); mObject.Scope = mScope; } @@ -150,10 +157,12 @@ protected override void ProcessRecord() mObject = mClass; mObject.Scope = scope; } + if (!ShouldProcess(mObject["__PATH"].ToString())) { continue; } + mObject.Delete(); } catch (ManagementException e) diff --git a/src/Microsoft.PowerShell.Commands.Management/commands/management/RenamePropertyCommand.cs b/src/Microsoft.PowerShell.Commands.Management/commands/management/RenamePropertyCommand.cs index c5077bfda79..bdefc6b2c64 100644 --- a/src/Microsoft.PowerShell.Commands.Management/commands/management/RenamePropertyCommand.cs +++ b/src/Microsoft.PowerShell.Commands.Management/commands/management/RenamePropertyCommand.cs @@ -1,23 +1,21 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. using System.Management.Automation; -using Dbg = System.Management.Automation; namespace Microsoft.PowerShell.Commands { /// - /// A command to rename a property of an item at a specified path + /// A command to rename a property of an item at a specified path. /// [Cmdlet(VerbsCommon.Rename, "ItemProperty", DefaultParameterSetName = "Path", SupportsShouldProcess = true, SupportsTransactions = true, - HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113383")] + HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2097152")] public class RenameItemPropertyCommand : PassThroughItemPropertyCommandBase { #region Parameters /// - /// Gets or sets the path parameter to the command + /// Gets or sets the path parameter to the command. /// [Parameter(Position = 0, ParameterSetName = "Path", Mandatory = true, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true)] @@ -26,46 +24,44 @@ public string Path get { return _path; - } // get + } set { _path = value; - } // set - } // Path + } + } /// - /// Gets or sets the literal path parameter to the command + /// Gets or sets the literal path parameter to the command. /// [Parameter(ParameterSetName = "LiteralPath", Mandatory = true, ValueFromPipeline = false, ValueFromPipelineByPropertyName = true)] - [Alias("PSPath")] + [Alias("PSPath", "LP")] public string LiteralPath { get { return _path; - } // get + } set { base.SuppressWildcardExpansion = true; _path = value; - } // set - } // LiteralPath + } + } /// - /// The properties to be renamed on the item + /// The properties to be renamed on the item. /// - /// [Parameter(Mandatory = true, Position = 1, ValueFromPipelineByPropertyName = true)] [Alias("PSProperty")] public string Name { get; set; } /// - /// The new name of the property on the item + /// The new name of the property on the item. /// - /// [Parameter(Mandatory = true, Position = 2, ValueFromPipelineByPropertyName = true)] public string NewName { get; set; } @@ -74,24 +70,22 @@ public string LiteralPath /// that require dynamic parameters should override this method and return the /// dynamic parameter object. /// - /// /// /// The context under which the command is running. /// - /// /// /// An object representing the dynamic parameters for the cmdlet or null if there /// are none. /// - /// internal override object GetDynamicParameters(CmdletProviderContext context) { if (Path != null) { return InvokeProvider.Property.RenamePropertyDynamicParameters(Path, Name, NewName, context); } + return InvokeProvider.Property.RenamePropertyDynamicParameters(".", Name, NewName, context); - } // GetDynamicParameters + } #endregion Parameters @@ -146,9 +140,8 @@ protected override void ProcessRecord() pathNotFound.ErrorRecord, pathNotFound)); } - } // ProcessRecord + } #endregion Command code - - } // RenameItemPropertyCommand -} // namespace Microsoft.PowerShell.Commands + } +} diff --git a/src/Microsoft.PowerShell.Commands.Management/commands/management/ResolvePathCommand.cs b/src/Microsoft.PowerShell.Commands.Management/commands/management/ResolvePathCommand.cs index 5930dbab51e..d0f5fecf495 100644 --- a/src/Microsoft.PowerShell.Commands.Management/commands/management/ResolvePathCommand.cs +++ b/src/Microsoft.PowerShell.Commands.Management/commands/management/ResolvePathCommand.cs @@ -1,26 +1,24 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. using System; -using System.Management.Automation; -using Dbg = System.Management.Automation; using System.Collections.ObjectModel; +using System.Management.Automation; namespace Microsoft.PowerShell.Commands { /// - /// A command to resolve MSH paths containing glob characters to - /// MSH paths that match the glob strings. + /// A command to resolve PowerShell paths containing glob characters to + /// PowerShell paths that match the glob strings. /// [Cmdlet(VerbsDiagnostic.Resolve, "Path", DefaultParameterSetName = "Path", SupportsTransactions = true, - HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113384")] + HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2097143")] public class ResolvePathCommand : CoreCommandWithCredentialsBase { #region Parameters /// - /// Gets or sets the path parameter to the command + /// Gets or sets the path parameter to the command. /// [Parameter(Position = 0, ParameterSetName = "Path", Mandatory = true, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true)] @@ -29,69 +27,153 @@ public string[] Path get { return _paths; - } // get + } set { _paths = value; - } // set - } // Path + } + } /// - /// Gets or sets the literal path parameter to the command + /// Gets or sets the literal path parameter to the command. /// [Parameter(ParameterSetName = "LiteralPath", Mandatory = true, ValueFromPipeline = false, ValueFromPipelineByPropertyName = true)] - [Alias("PSPath")] + [Alias("PSPath", "LP")] public string[] LiteralPath { get { return _paths; - } // get + } set { base.SuppressWildcardExpansion = true; _paths = value; - } // set - } // LiteralPath + } + } /// /// Gets or sets the value that determines if the resolved path should /// be resolved to its relative version. /// - [Parameter()] + [Parameter(ParameterSetName = "Path")] + [Parameter(ParameterSetName = "LiteralPath")] public SwitchParameter Relative { get { return _relative; - } // get + } set { _relative = value; - } // set - } // Relative + } + } + private SwitchParameter _relative; + /// + /// Gets or sets the path the resolved relative path should be based off. + /// + [Parameter] + public string RelativeBasePath + { + get + { + return _relativeBasePath; + } + + set + { + _relativeBasePath = value; + } + } + + /// + /// Gets or sets the force property. + /// + [Parameter] + public override SwitchParameter Force + { + get => base.Force; + set => base.Force = value; + } #endregion Parameters #region parameter data /// - /// The path to resolve + /// The path to resolve. /// private string[] _paths; + private PSDriveInfo _relativeDrive; + private string _relativeBasePath; + #endregion parameter data #region Command code /// - /// Resolves the path containing glob characters to the MSH paths that it + /// Finds the path and drive that should be used for relative path resolution + /// represents. + /// + protected override void BeginProcessing() + { + if (!string.IsNullOrEmpty(RelativeBasePath)) + { + try + { + _relativeBasePath = SessionState.Internal.Globber.GetProviderPath(RelativeBasePath, CmdletProviderContext, out _, out _relativeDrive); + } + catch (ProviderNotFoundException providerNotFound) + { + ThrowTerminatingError( + new ErrorRecord( + providerNotFound.ErrorRecord, + providerNotFound)); + } + catch (DriveNotFoundException driveNotFound) + { + ThrowTerminatingError( + new ErrorRecord( + driveNotFound.ErrorRecord, + driveNotFound)); + } + catch (ProviderInvocationException providerInvocation) + { + ThrowTerminatingError( + new ErrorRecord( + providerInvocation.ErrorRecord, + providerInvocation)); + } + catch (NotSupportedException notSupported) + { + ThrowTerminatingError( + new ErrorRecord(notSupported, "ProviderIsNotNavigationCmdletProvider", ErrorCategory.InvalidArgument, RelativeBasePath)); + } + catch (InvalidOperationException invalidOperation) + { + ThrowTerminatingError( + new ErrorRecord(invalidOperation, "InvalidHomeLocation", ErrorCategory.InvalidOperation, RelativeBasePath)); + } + + return; + } + else if (_relative) + { + _relativeDrive = SessionState.Path.CurrentLocation.Drive; + _relativeBasePath = SessionState.Path.CurrentLocation.ProviderPath; + } + } + + /// + /// Resolves the path containing glob characters to the PowerShell paths that it /// represents. /// protected override void ProcessRecord() @@ -101,19 +183,65 @@ protected override void ProcessRecord() Collection result = null; try { - result = SessionState.Path.GetResolvedPSPathFromPSPath(path, CmdletProviderContext); + if (MyInvocation.BoundParameters.ContainsKey("RelativeBasePath")) + { + // Pushing and popping the location is done because GetResolvedPSPathFromPSPath uses the current path to resolve relative paths. + // It's important that we pop the location before writing an object to the pipeline to avoid affecting downstream commands. + try + { + SessionState.Path.PushCurrentLocation(string.Empty); + _ = SessionState.Path.SetLocation(_relativeBasePath); + result = SessionState.Path.GetResolvedPSPathFromPSPath(path, CmdletProviderContext); + } + finally + { + _ = SessionState.Path.PopLocation(string.Empty); + } + } + else + { + result = SessionState.Path.GetResolvedPSPathFromPSPath(path, CmdletProviderContext); + } if (_relative) { + ReadOnlySpan baseCache = null; + ReadOnlySpan adjustedBaseCache = null; foreach (PathInfo currentPath in result) { - string adjustedPath = SessionState.Path.NormalizeRelativePath(currentPath.Path, - SessionState.Path.CurrentLocation.ProviderPath); - if (!adjustedPath.StartsWith(".", StringComparison.OrdinalIgnoreCase)) + // When result path and base path is on different PSDrive + // (../)*path should not go beyond the root of base path + if (currentPath.Drive != _relativeDrive && + _relativeDrive != null && + !currentPath.ProviderPath.StartsWith(_relativeDrive.Root, StringComparison.OrdinalIgnoreCase)) + { + WriteObject(currentPath.Path, enumerateCollection: false); + continue; + } + + int leafIndex = currentPath.Path.LastIndexOf(currentPath.Provider.ItemSeparator); + var basePath = currentPath.Path.AsSpan(0, leafIndex); + if (basePath == baseCache) + { + WriteObject(string.Concat(adjustedBaseCache, currentPath.Path.AsSpan(leafIndex + 1)), enumerateCollection: false); + continue; + } + + baseCache = basePath; + string adjustedPath = SessionState.Path.NormalizeRelativePath(currentPath.Path, _relativeBasePath); + + // Do not insert './' if result path is not relative + if (!adjustedPath.StartsWith( + currentPath.Drive?.Root ?? currentPath.Path, StringComparison.OrdinalIgnoreCase) && + !adjustedPath.StartsWith('.')) { adjustedPath = SessionState.Path.Combine(".", adjustedPath); } - WriteObject(adjustedPath, false); + + leafIndex = adjustedPath.LastIndexOf(currentPath.Provider.ItemSeparator); + adjustedBaseCache = adjustedPath.AsSpan(0, leafIndex + 1); + + WriteObject(adjustedPath, enumerateCollection: false); } } } @@ -152,13 +280,11 @@ protected override void ProcessRecord() if (!_relative) { - WriteObject(result, true); + WriteObject(result, enumerateCollection: true); } } - } // ProcessRecord + } #endregion Command code - - } // ResolvePathCommand -} // namespace Microsoft.PowerShell.Commands - + } +} diff --git a/src/Microsoft.PowerShell.Commands.Management/commands/management/RollbackTransactionCommand.cs b/src/Microsoft.PowerShell.Commands.Management/commands/management/RollbackTransactionCommand.cs index 79b48c496b6..3d6fc27cc3c 100644 --- a/src/Microsoft.PowerShell.Commands.Management/commands/management/RollbackTransactionCommand.cs +++ b/src/Microsoft.PowerShell.Commands.Management/commands/management/RollbackTransactionCommand.cs @@ -1,8 +1,8 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. using System.Management.Automation; + using Dbg = System.Management.Automation; namespace Microsoft.PowerShell.Commands @@ -26,6 +26,5 @@ protected override void EndProcessing() this.Context.TransactionManager.Rollback(); } } - } // RollbackTransactionCommand -} // namespace Microsoft.PowerShell.Commands - + } +} diff --git a/src/Microsoft.PowerShell.Commands.Management/commands/management/Service.cs b/src/Microsoft.PowerShell.Commands.Management/commands/management/Service.cs index 67a6da5df81..739baa15bab 100644 --- a/src/Microsoft.PowerShell.Commands.Management/commands/management/Service.cs +++ b/src/Microsoft.PowerShell.Commands.Management/commands/management/Service.cs @@ -1,38 +1,38 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + #if !UNIX // Not built on Unix using System; using System.Collections.Generic; -using System.ServiceProcess; +using System.ComponentModel; // Win32Exception using System.Diagnostics.CodeAnalysis; -using Dbg = System.Management.Automation.Diagnostics; using System.Management.Automation; using System.Management.Automation.Internal; -using System.ComponentModel; // Win32Exception -using System.Runtime.Serialization; using System.Runtime.InteropServices; // Marshal, DllImport -using System.Security.Permissions; -using NakedWin32Handle = System.IntPtr; +using System.Runtime.Serialization; +using System.Security.AccessControl; +using System.ServiceProcess; +using Dbg = System.Management.Automation.Diagnostics; using DWORD = System.UInt32; +using NakedWin32Handle = System.IntPtr; namespace Microsoft.PowerShell.Commands { #region ServiceBaseCommand /// - /// This class implements the base for service commands + /// This class implements the base for service commands. /// public abstract class ServiceBaseCommand : Cmdlet { #region Internal /// - /// Confirm that the operation should proceed + /// Confirm that the operation should proceed. /// - /// service object to be acted on - /// true if operation should continue, false otherwise + /// Service object to be acted on. + /// True if operation should continue, false otherwise. protected bool ShouldProcessServiceOperation(ServiceController service) { return ShouldProcessServiceOperation( @@ -41,11 +41,11 @@ protected bool ShouldProcessServiceOperation(ServiceController service) } /// - /// Confirm that the operation should proceed + /// Confirm that the operation should proceed. /// - /// display name of service to be acted on - /// service name of service to be acted on - /// true if operation should continue, false otherwise + /// Display name of service to be acted on. + /// Service name of service to be acted on. + /// True if operation should continue, false otherwise. protected bool ShouldProcessServiceOperation( string displayName, string serviceName) { @@ -103,86 +103,83 @@ internal void WriteNonTerminatingError( string message = StringUtil.Format(errorMessage, serviceName, displayName, - (null == innerException) ? "" : innerException.Message - ); - ServiceCommandException exception = - new ServiceCommandException(message, innerException); - exception.ServiceName = serviceName; + (innerException == null) ? category.ToString() : innerException.Message); - if (innerException != null) - { - } - else - { - } + var exception = new ServiceCommandException(message, innerException); + exception.ServiceName = serviceName; WriteError(new ErrorRecord(exception, errorId, category, targetObject)); } - - /// - /// Writes a non-terminating error on computer name. - /// - /// - /// - /// - /// - /// - /// - internal void WriteNonTerminatingError( + internal void SetServiceSecurityDescriptor( ServiceController service, - string computername, - Exception innerException, - string errorId, - string errorMessage, - ErrorCategory category) + string securityDescriptorSddl, + NakedWin32Handle hService) { - WriteNonTerminatingError( - service.ServiceName, - computername, - service, - innerException, - errorId, - errorMessage, - category); + var rawSecurityDescriptor = new RawSecurityDescriptor(securityDescriptorSddl); + RawAcl rawDiscretionaryAcl = rawSecurityDescriptor.DiscretionaryAcl; + var discretionaryAcl = new DiscretionaryAcl(false, false, rawDiscretionaryAcl); + + byte[] rawDacl = new byte[discretionaryAcl.BinaryLength]; + discretionaryAcl.GetBinaryForm(rawDacl, 0); + rawSecurityDescriptor.DiscretionaryAcl = new RawAcl(rawDacl, 0); + byte[] securityDescriptorByte = new byte[rawSecurityDescriptor.BinaryLength]; + rawSecurityDescriptor.GetBinaryForm(securityDescriptorByte, 0); + + bool status = NativeMethods.SetServiceObjectSecurity( + hService, + SecurityInfos.DiscretionaryAcl, + securityDescriptorByte); + + if (!status) + { + int lastError = Marshal.GetLastWin32Error(); + Win32Exception exception = new(lastError); + bool accessDenied = exception.NativeErrorCode == NativeMethods.ERROR_ACCESS_DENIED; + WriteNonTerminatingError( + service, + exception, + nameof(ServiceResources.CouldNotSetServiceSecurityDescriptorSddl), + StringUtil.Format(ServiceResources.CouldNotSetServiceSecurityDescriptorSddl, service.ServiceName, exception.Message), + accessDenied ? ErrorCategory.PermissionDenied : ErrorCategory.InvalidOperation); + } } - #endregion Internal - } // class ServiceBaseCommand + } #endregion ServiceBaseCommand #region MultipleServiceCommandBase /// /// This class implements the base for service commands which can - /// operate on multiple services + /// operate on multiple services. /// public abstract class MultipleServiceCommandBase : ServiceBaseCommand { #region Parameters /// - /// The various process selection modes + /// The various process selection modes. /// internal enum SelectionMode { /// - /// Select all services + /// Select all services. /// Default = 0, /// - /// Select services matching the supplied names + /// Select services matching the supplied names. /// DisplayName = 1, /// - /// Select services based on pipeline input + /// Select services based on pipeline input. /// InputObject = 2, /// - /// Select services by Service name + /// Select services by Service name. /// ServiceName = 3 - }; + } /// /// Holds the selection mode setting. /// @@ -195,7 +192,7 @@ internal enum SelectionMode internal string[] serviceNames = null; /// - /// gets/sets an array of display names for services + /// Gets/sets an array of display names for services. /// [Parameter(ParameterSetName = "DisplayName", Mandatory = true)] public string[] DisplayName @@ -204,12 +201,14 @@ public string[] DisplayName { return displayNames; } + set { displayNames = value; selectionMode = SelectionMode.DisplayName; } } + internal string[] displayNames = null; /// @@ -226,11 +225,13 @@ public string[] Include { return include; } + set { include = value; } } + internal string[] include = null; /// @@ -247,11 +248,13 @@ public string[] Exclude { return exclude; } + set { exclude = value; } } + internal string[] exclude = null; // 1054295-2004/12/01-JonN This also works around 1054295. @@ -273,97 +276,53 @@ public ServiceController[] InputObject { return _inputObject; } + set { _inputObject = value; selectionMode = SelectionMode.InputObject; } } + private ServiceController[] _inputObject = null; #endregion Parameters #region Internal /// - /// Retrieve the master list of all services + /// Gets an array of all services. /// - /// + /// + /// An array of components that represents all the service resources. + /// /// /// MSDN does not document the list of exceptions, /// but it is reasonable to expect that SecurityException is /// among them. Errors here will terminate the cmdlet. /// - internal ServiceController[] AllServices - { - get - { - if (null == _allServices) - { - List services = new List(); - - if (SuppliedComputerName.Length > 0) - { - foreach (string computerName in SuppliedComputerName) - { - services.AddRange(ServiceController.GetServices(computerName)); - } - } - else - { - services.AddRange(ServiceController.GetServices()); - } + internal ServiceController[] AllServices => _allServices ??= ServiceController.GetServices(); - _allServices = services.ToArray(); - } - return _allServices; - } - } - private ServiceController[] _allServices = null; - - private void AddIfValidService(IList listOfValidServices, string nameOfService, string computerName) - { - try - { - ServiceController sc = new ServiceController(nameOfService, computerName); - ServiceControllerStatus tmp = sc.Status; // this will throw if the service doesn't exist - - // no exceptions = this is an existing, valid service = add - listOfValidServices.Add(sc); - } - catch (InvalidOperationException) - { - } - catch (ArgumentException) - { - } - } + private ServiceController[] _allServices; - internal List OneService(string nameOfService) + internal ServiceController GetOneService(string nameOfService) { Dbg.Assert(!WildcardPattern.ContainsWildcardCharacters(nameOfService), "Caller should verify that nameOfService doesn't contain wildcard characters"); - List services = new List(); - if (SuppliedComputerName.Length > 0) - { - foreach (string computerName in SuppliedComputerName) - { - AddIfValidService(services, nameOfService, computerName); - } - } - else + try { - AddIfValidService(services, nameOfService, "."); + var sc = new ServiceController(nameOfService); + // This will throw if the service doesn't exist + var unused = sc.Status; + + // No exception, then this is an existing, valid service. Return it. + return sc; } + catch (InvalidOperationException) { } + catch (ArgumentException) { } - return services; + return null; } - /// - /// The computer from which to retrieve processes. - /// - [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] - protected string[] SuppliedComputerName { get; set; } = Utils.EmptyArray(); - /// /// Retrieve the list of all services matching the ServiceName, /// DisplayName, Include and Exclude parameters, sorted by ServiceName. @@ -388,12 +347,12 @@ internal List MatchingServices() // before being stopped. JimTru confirms that this is OK. matchingServices.Sort(ServiceComparison); return matchingServices; - } // MatchingServices + } // sort by servicename private static int ServiceComparison(ServiceController x, ServiceController y) { - return String.Compare(x.ServiceName, y.ServiceName, StringComparison.CurrentCultureIgnoreCase); + return string.Compare(x.ServiceName, y.ServiceName, StringComparison.OrdinalIgnoreCase); } /// @@ -412,14 +371,15 @@ private static int ServiceComparison(ServiceController x, ServiceController y) /// private List MatchingServicesByServiceName() { - List matchingServices = new List(); + List matchingServices = new(); - if (null == serviceNames) + if (serviceNames == null) { foreach (ServiceController service in AllServices) { IncludeExcludeAdd(matchingServices, service, false); } + return matchingServices; } @@ -440,7 +400,8 @@ private List MatchingServicesByServiceName() } else { - foreach (ServiceController service in OneService(pattern)) + ServiceController service = GetOneService(pattern); + if (service != null) { found = true; IncludeExcludeAdd(matchingServices, service, true); @@ -451,7 +412,7 @@ private List MatchingServicesByServiceName() { WriteNonTerminatingError( pattern, - "", + string.Empty, pattern, null, "NoServiceFoundForGivenName", @@ -459,8 +420,9 @@ private List MatchingServicesByServiceName() ErrorCategory.ObjectNotFound); } } + return matchingServices; - } // MatchingServicesByServiceName + } /// /// Retrieves the list of all services matching the DisplayName, @@ -472,12 +434,13 @@ private List MatchingServicesByServiceName() /// private List MatchingServicesByDisplayName() { - List matchingServices = new List(); - if (null == DisplayName) + List matchingServices = new(); + if (DisplayName == null) { Diagnostics.Assert(false, "null DisplayName"); throw PSTraceSource.NewInvalidOperationException(); } + foreach (string pattern in DisplayName) { WildcardPattern wildcard = @@ -490,10 +453,11 @@ private List MatchingServicesByDisplayName() found = true; IncludeExcludeAdd(matchingServices, service, true); } + if (!found && !WildcardPattern.ContainsWildcardCharacters(pattern)) { WriteNonTerminatingError( - "", + string.Empty, pattern, pattern, null, @@ -502,8 +466,9 @@ private List MatchingServicesByDisplayName() ErrorCategory.ObjectNotFound); } } + return matchingServices; - } // MatchingServicesByDisplayName + } /// /// Retrieves the list of all services matching the InputObject, @@ -512,19 +477,21 @@ private List MatchingServicesByDisplayName() /// private List MatchingServicesByInput() { - List matchingServices = new List(); - if (null == InputObject) + List matchingServices = new(); + if (InputObject == null) { Diagnostics.Assert(false, "null InputObject"); throw PSTraceSource.NewInvalidOperationException(); } + foreach (ServiceController service in InputObject) { service.Refresh(); IncludeExcludeAdd(matchingServices, service, false); } + return matchingServices; - } // MatchingServicesByInput + } /// /// Add to , @@ -532,17 +499,17 @@ private List MatchingServicesByInput() /// and (if ) if it is not /// already on . /// - /// list of services - /// service to add to list - /// check list for duplicates + /// List of services. + /// Service to add to list. + /// Check list for duplicates. private void IncludeExcludeAdd( List list, ServiceController service, bool checkDuplicates) { - if (null != include && !Matches(service, include)) + if (include != null && !Matches(service, include)) return; - if (null != exclude && Matches(service, exclude)) + if (exclude != null && Matches(service, exclude)) return; if (checkDuplicates) { @@ -555,6 +522,7 @@ private void IncludeExcludeAdd( } } } + list.Add(service); } @@ -567,8 +535,8 @@ private void IncludeExcludeAdd( /// private bool Matches(ServiceController service, string[] matchList) { - if (null == matchList) - throw PSTraceSource.NewArgumentNullException("matchList"); + if (matchList == null) + throw PSTraceSource.NewArgumentNullException(nameof(matchList)); string serviceID = (selectionMode == SelectionMode.DisplayName) ? service.DisplayName : service.ServiceName; @@ -578,25 +546,26 @@ private bool Matches(ServiceController service, string[] matchList) if (wildcard.IsMatch(serviceID)) return true; } + return false; } #endregion Internal - } // class MultipleServiceCommandBase + } #endregion MultipleServiceCommandBase #region GetServiceCommand /// - /// This class implements the get-service command + /// This class implements the get-service command. /// [Cmdlet(VerbsCommon.Get, "Service", DefaultParameterSetName = "Default", - HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113332", RemotingCapability = RemotingCapability.SupportedByCommand)] + HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2096496", RemotingCapability = RemotingCapability.SupportedByCommand)] [OutputType(typeof(ServiceController))] public sealed class GetServiceCommand : MultipleServiceCommandBase { #region Parameters /// - /// gets/sets an array of service names + /// Gets/sets an array of service names. /// /// /// The ServiceName parameter is declared in subclasses, @@ -611,6 +580,7 @@ public string[] Name { return serviceNames; } + set { serviceNames = value; @@ -618,27 +588,6 @@ public string[] Name } } - /// - /// gets/sets the destination computer name - /// - [Parameter( - Mandatory = false, - ValueFromPipelineByPropertyName = true)] - [ValidateNotNullOrEmpty()] - [Alias("Cn")] - [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] - public string[] ComputerName - { - get - { - return SuppliedComputerName; - } - set - { - SuppliedComputerName = value; - } - } - /// /// This returns the DependentServices of the specified service. /// @@ -646,7 +595,6 @@ public string[] ComputerName [Alias("DS")] public SwitchParameter DependentServices { get; set; } - /// /// This returns the ServicesDependedOn of the specified service. /// @@ -658,38 +606,167 @@ public string[] ComputerName #region Overrides /// - /// Write the service objects + /// Write the service objects. /// protected override void ProcessRecord() { - foreach (ServiceController service in MatchingServices()) - { - if (!DependentServices.IsPresent && !RequiredServices.IsPresent) + nint scManagerHandle = nint.Zero; + if (!DependentServices && !RequiredServices) + { + // As Get-Service only works on local services we get this once + // to retrieve extra properties added by PowerShell. + scManagerHandle = NativeMethods.OpenSCManagerW( + lpMachineName: null, + lpDatabaseName: null, + dwDesiredAccess: NativeMethods.SC_MANAGER_CONNECT); + if (scManagerHandle == nint.Zero) { - WriteObject(service); + Win32Exception exception = new(); + string message = StringUtil.Format(ServiceResources.FailToOpenServiceControlManager, exception.Message); + ServiceCommandException serviceException = new ServiceCommandException(message, exception); + ErrorRecord err = new ErrorRecord( + serviceException, + "FailToOpenServiceControlManager", + ErrorCategory.PermissionDenied, + null); + ThrowTerminatingError(err); } - else + } + + try + { + foreach (ServiceController service in MatchingServices()) { - if (DependentServices.IsPresent) + if (!DependentServices.IsPresent && !RequiredServices.IsPresent) { - foreach (ServiceController dependantserv in service.DependentServices) - { - WriteObject(dependantserv); - } + WriteObject(AddProperties(scManagerHandle, service)); } - if (RequiredServices.IsPresent) + else { - foreach (ServiceController servicedependedon in service.ServicesDependedOn) + if (DependentServices.IsPresent) + { + foreach (ServiceController dependantserv in service.DependentServices) + { + WriteObject(dependantserv); + } + } + + if (RequiredServices.IsPresent) { - WriteObject(servicedependedon); + foreach (ServiceController servicedependedon in service.ServicesDependedOn) + { + WriteObject(servicedependedon); + } } } } } + finally + { + if (scManagerHandle != nint.Zero) + { + bool succeeded = NativeMethods.CloseServiceHandle(scManagerHandle); + Diagnostics.Assert(succeeded, "SCManager handle close failed"); + } + } } #endregion Overrides + +#nullable enable + /// + /// Adds UserName, Description, BinaryPathName, DelayedAutoStart and StartupType to a ServiceController object. + /// + /// Handle to the local SCManager instance. + /// + /// ServiceController as PSObject with UserName, Description and StartupType added. + private static PSObject AddProperties(nint scManagerHandle, ServiceController service) + { + NakedWin32Handle hService = nint.Zero; + + // As these are optional values, a failure due to permissions or + // other problem is ignored and the properties are set to null. + bool? isDelayedAutoStart = null; + string? binPath = null; + string? description = null; + string? startName = null; + ServiceStartupType startupType = ServiceStartupType.InvalidValue; + try + { + // We don't use service.ServiceHandle as that requests + // SERVICE_ALL_ACCESS when we only need SERVICE_QUERY_CONFIG. + hService = NativeMethods.OpenServiceW( + scManagerHandle, + service.ServiceName, + NativeMethods.SERVICE_QUERY_CONFIG + ); + if (hService != nint.Zero) + { + if (NativeMethods.QueryServiceConfig2( + hService, + NativeMethods.SERVICE_CONFIG_DESCRIPTION, + out NativeMethods.SERVICE_DESCRIPTIONW descriptionInfo)) + { + description = descriptionInfo.lpDescription; + } + + if (NativeMethods.QueryServiceConfig2( + hService, + NativeMethods.SERVICE_CONFIG_DELAYED_AUTO_START_INFO, + out NativeMethods.SERVICE_DELAYED_AUTO_START_INFO autostartInfo)) + { + isDelayedAutoStart = autostartInfo.fDelayedAutostart; + } + + if (NativeMethods.QueryServiceConfig( + hService, + out NativeMethods.QUERY_SERVICE_CONFIG serviceInfo)) + { + binPath = serviceInfo.lpBinaryPathName; + startName = serviceInfo.lpServiceStartName; + if (isDelayedAutoStart.HasValue) + { + startupType = NativeMethods.GetServiceStartupType( + (ServiceStartMode)serviceInfo.dwStartType, + isDelayedAutoStart.Value); + } + } + } + } + finally + { + if (hService != IntPtr.Zero) + { + bool succeeded = NativeMethods.CloseServiceHandle(hService); + Diagnostics.Assert(succeeded, "Failed to close service handle"); + } + } + + PSObject serviceAsPSObj = PSObject.AsPSObject(service); + PSNoteProperty noteProperty = new("UserName", startName); + serviceAsPSObj.Properties.Add(noteProperty, true); + serviceAsPSObj.TypeNames.Insert(0, "System.Service.ServiceController#UserName"); + + noteProperty = new PSNoteProperty("Description", description); + serviceAsPSObj.Properties.Add(noteProperty, true); + serviceAsPSObj.TypeNames.Insert(0, "System.Service.ServiceController#Description"); + + noteProperty = new PSNoteProperty("DelayedAutoStart", isDelayedAutoStart); + serviceAsPSObj.Properties.Add(noteProperty, true); + serviceAsPSObj.TypeNames.Insert(0, "System.Service.ServiceController#DelayedAutoStart"); + + noteProperty = new PSNoteProperty("BinaryPathName", binPath); + serviceAsPSObj.Properties.Add(noteProperty, true); + serviceAsPSObj.TypeNames.Insert(0, "System.Service.ServiceController#BinaryPathName"); + + noteProperty = new PSNoteProperty("StartupType", startupType); + serviceAsPSObj.Properties.Add(noteProperty, true); + serviceAsPSObj.TypeNames.Insert(0, "System.Service.ServiceController#StartupType"); + + return serviceAsPSObj; + } } +#nullable disable #endregion GetServiceCommand #region ServiceOperationBaseCommand @@ -701,7 +778,7 @@ public abstract class ServiceOperationBaseCommand : MultipleServiceCommandBase { #region Parameters /// - /// gets/sets an array of service names + /// Gets/sets an array of service names. /// /// /// The ServiceName parameter is declared in subclasses, @@ -715,6 +792,7 @@ public string[] Name { return serviceNames; } + set { serviceNames = value; @@ -723,7 +801,7 @@ public string[] Name } /// - /// Service controller objects + /// Service controller objects. /// [Parameter(Position = 0, Mandatory = true, ParameterSetName = "InputObject", ValueFromPipeline = true)] [ValidateNotNullOrEmpty] @@ -734,6 +812,7 @@ public string[] Name { return base.InputObject; } + set { base.InputObject = value; @@ -754,8 +833,8 @@ public string[] Name /// Waits forever for the service to reach the desired status, but /// writes a string to WriteWarning every 2 seconds. /// - /// service on which to operate - /// desired status + /// Service on which to operate. + /// Desired status. /// /// This is the expected status while the operation is incomplete. /// If the service is in some other state, this means that the @@ -771,7 +850,7 @@ public string[] Name /// /// errorMessage for a nonterminating error if operation fails /// - /// true if action succeeded + /// True if action succeeded. /// /// WriteWarning will throw this if the pipeline has been stopped. /// This means that the delay between hitting CTRL-C and stopping @@ -785,7 +864,7 @@ internal bool DoWaitForStatus( string errorId, string errorMessage) { - do + while (true) { try { @@ -811,6 +890,7 @@ internal bool DoWaitForStatus( ErrorCategory.OpenError); return false; } + string message = StringUtil.Format(resourceIdPending, serviceController.ServiceName, serviceController.DisplayName @@ -818,14 +898,14 @@ internal bool DoWaitForStatus( // will throw PipelineStoppedException if user hit CTRL-C WriteWarning(message); } - } while (true); + } } /// /// This will start the service. /// - /// service to start - /// true iff the service was started + /// Service to start. + /// True if-and-only-if the service was started. internal bool DoStartService(ServiceController serviceController) { Exception exception = null; @@ -835,19 +915,19 @@ internal bool DoStartService(ServiceController serviceController) } catch (Win32Exception e) { - if (NativeMethods.ERROR_SERVICE_ALREADY_RUNNING != e.NativeErrorCode) + if (e.NativeErrorCode != NativeMethods.ERROR_SERVICE_ALREADY_RUNNING) exception = e; } catch (InvalidOperationException e) { - Win32Exception eInner = e.InnerException as Win32Exception; - if (null == eInner - || NativeMethods.ERROR_SERVICE_ALREADY_RUNNING != eInner.NativeErrorCode) + if (e.InnerException is not Win32Exception eInner + || eInner.NativeErrorCode != NativeMethods.ERROR_SERVICE_ALREADY_RUNNING) { exception = e; } } - if (null != exception) + + if (exception != null) { // This service refused to accept the start command, // so write a non-terminating error. @@ -871,22 +951,23 @@ internal bool DoStartService(ServiceController serviceController) { return false; } + return true; } /// /// This will stop the service. /// - /// service to stop - /// stop dependent services + /// Service to stop. + /// Stop dependent services. /// - /// true iff the service was stopped + /// True if-and-only-if the service was stopped. internal List DoStopService(ServiceController serviceController, bool force, bool waitForServiceToStop) { // Ignore ServiceController.CanStop. CanStop will be set false // if the service is not running, but this is not an error. - List stoppedServices = new List(); + List stoppedServices = new(); ServiceController[] dependentServices = null; try @@ -950,20 +1031,19 @@ internal List DoStopService(ServiceController serviceControll } catch (Win32Exception e) { - if (NativeMethods.ERROR_SERVICE_NOT_ACTIVE != e.NativeErrorCode) + if (e.NativeErrorCode != NativeMethods.ERROR_SERVICE_NOT_ACTIVE) exception = e; } catch (InvalidOperationException e) { - Win32Exception eInner = - e.InnerException as Win32Exception; - if (null == eInner - || NativeMethods.ERROR_SERVICE_NOT_ACTIVE != eInner.NativeErrorCode) + if (e.InnerException is not Win32Exception eInner + || eInner.NativeErrorCode != NativeMethods.ERROR_SERVICE_NOT_ACTIVE) { exception = e; } } - if (null != exception) + + if (exception != null) { // This service refused to accept the stop command, // so write a non-terminating error. @@ -999,51 +1079,39 @@ internal List DoStopService(ServiceController serviceControll stoppedServices.Add(serviceController); } - return stoppedServices; } /// - /// Check if all dependent services are stopped + /// Check if all dependent services are stopped. /// /// /// /// True if all dependent services are stopped /// False if not all dependent services are stopped /// - private bool HaveAllDependentServicesStopped(ICollection dependentServices) + private static bool HaveAllDependentServicesStopped(ServiceController[] dependentServices) { - foreach (ServiceController service in dependentServices) - { - if (service.Status != ServiceControllerStatus.Stopped) - { - return false; - } - } - return true; + return Array.TrueForAll(dependentServices, static service => service.Status == ServiceControllerStatus.Stopped); } /// - /// This removes all services that are not stopped from a list of services + /// This removes all services that are not stopped from a list of services. /// - /// a list of services + /// A list of services. internal void RemoveNotStoppedServices(List services) { - foreach (ServiceController service in services) - { - if (service.Status != ServiceControllerStatus.Stopped && - service.Status != ServiceControllerStatus.StopPending) - { - services.Remove(service); - } - } + // You shall not modify a collection during enumeration. + services.RemoveAll(service => + service.Status != ServiceControllerStatus.Stopped && + service.Status != ServiceControllerStatus.StopPending); } /// /// This will pause the service. /// - /// service to pause - /// true iff the service was paused + /// Service to pause. + /// True if-and-only-if the service was paused. internal bool DoPauseService(ServiceController serviceController) { Exception exception = null; @@ -1054,23 +1122,25 @@ internal bool DoPauseService(ServiceController serviceController) } catch (Win32Exception e) { - if (NativeMethods.ERROR_SERVICE_NOT_ACTIVE == e.NativeErrorCode) + if (e.NativeErrorCode == NativeMethods.ERROR_SERVICE_NOT_ACTIVE) { serviceNotRunning = true; } + exception = e; } catch (InvalidOperationException e) { - Win32Exception eInner = e.InnerException as Win32Exception; - if (null != eInner - && NativeMethods.ERROR_SERVICE_NOT_ACTIVE == eInner.NativeErrorCode) + if (e.InnerException is Win32Exception eInner + && eInner.NativeErrorCode == NativeMethods.ERROR_SERVICE_NOT_ACTIVE) { serviceNotRunning = true; } + exception = e; } - if (null != exception) + + if (exception != null) { // This service refused to accept the pause command, // so write a non-terminating error. @@ -1120,8 +1190,8 @@ internal bool DoPauseService(ServiceController serviceController) /// /// This will resume the service. /// - /// service to resume - /// true iff the service was resumed + /// Service to resume. + /// True if-and-only-if the service was resumed. internal bool DoResumeService(ServiceController serviceController) { Exception exception = null; @@ -1132,23 +1202,25 @@ internal bool DoResumeService(ServiceController serviceController) } catch (Win32Exception e) { - if (NativeMethods.ERROR_SERVICE_NOT_ACTIVE == e.NativeErrorCode) + if (e.NativeErrorCode == NativeMethods.ERROR_SERVICE_NOT_ACTIVE) { serviceNotRunning = true; } + exception = e; } catch (InvalidOperationException e) { - Win32Exception eInner = e.InnerException as Win32Exception; - if (null != eInner - && NativeMethods.ERROR_SERVICE_NOT_ACTIVE == eInner.NativeErrorCode) + if (e.InnerException is Win32Exception eInner + && eInner.NativeErrorCode == NativeMethods.ERROR_SERVICE_NOT_ACTIVE) { serviceNotRunning = true; } + exception = e; } - if (null != exception) + + if (exception != null) { // This service refused to accept the continue command, // so write a non-terminating error. @@ -1200,13 +1272,13 @@ internal bool DoResumeService(ServiceController serviceController) #region StopServiceCommand /// - /// This class implements the stop-service command + /// This class implements the stop-service command. /// /// /// Note that the services will be sorted before being stopped. /// PM confirms that this is OK. /// - [Cmdlet(VerbsLifecycle.Stop, "Service", DefaultParameterSetName = "InputObject", SupportsShouldProcess = true, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113414")] + [Cmdlet(VerbsLifecycle.Stop, "Service", DefaultParameterSetName = "InputObject", SupportsShouldProcess = true, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2097052")] [OutputType(typeof(ServiceController))] public sealed class StopServiceCommand : ServiceOperationBaseCommand { @@ -1218,7 +1290,7 @@ public sealed class StopServiceCommand : ServiceOperationBaseCommand public SwitchParameter Force { get; set; } /// - /// Specifies whether to wait for a service to reach the stopped state before returning + /// Specifies whether to wait for a service to reach the stopped state before returning. /// [Parameter] public SwitchParameter NoWait { get; set; } @@ -1251,20 +1323,20 @@ protected override void ProcessRecord() } } } - } // ProcessRecord - } // StopServiceCommand + } + } #endregion StopServiceCommand #region StartServiceCommand /// - /// This class implements the start-service command + /// This class implements the start-service command. /// - [Cmdlet(VerbsLifecycle.Start, "Service", DefaultParameterSetName = "InputObject", SupportsShouldProcess = true, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113406")] + [Cmdlet(VerbsLifecycle.Start, "Service", DefaultParameterSetName = "InputObject", SupportsShouldProcess = true, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2097051")] [OutputType(typeof(ServiceController))] public sealed class StartServiceCommand : ServiceOperationBaseCommand { /// - /// Start the services + /// Start the services. /// protected override void ProcessRecord() { @@ -1283,20 +1355,20 @@ protected override void ProcessRecord() WriteObject(serviceController); } } - } // ProcessRecord - } // class StartServiceCommand + } + } #endregion StartServiceCommand #region SuspendServiceCommand /// - /// This class implements the suspend-service command + /// This class implements the suspend-service command. /// - [Cmdlet(VerbsLifecycle.Suspend, "Service", DefaultParameterSetName = "InputObject", SupportsShouldProcess = true, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113416")] + [Cmdlet(VerbsLifecycle.Suspend, "Service", DefaultParameterSetName = "InputObject", SupportsShouldProcess = true, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2097053")] [OutputType(typeof(ServiceController))] public sealed class SuspendServiceCommand : ServiceOperationBaseCommand { /// - /// Start the services + /// Start the services. /// protected override void ProcessRecord() { @@ -1315,21 +1387,21 @@ protected override void ProcessRecord() WriteObject(serviceController); } } - } // ProcessRecord - } // class SuspendServiceCommand + } + } #endregion SuspendServiceCommand #region ResumeServiceCommand /// - /// This class implements the resume-service command + /// This class implements the resume-service command. /// [Cmdlet(VerbsLifecycle.Resume, "Service", DefaultParameterSetName = "InputObject", SupportsShouldProcess = true, - HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113386")] + HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2097150")] [OutputType(typeof(ServiceController))] public sealed class ResumeServiceCommand : ServiceOperationBaseCommand { /// - /// Start the services + /// Start the services. /// protected override void ProcessRecord() { @@ -1341,23 +1413,24 @@ protected override void ProcessRecord() { continue; } + if (DoResumeService(serviceController)) { if (PassThru) WriteObject(serviceController); } } - } // ProcessRecord - } // class ResumeServiceCommand + } + } #endregion ResumeServiceCommand #region RestartServiceCommand /// - /// This class implements the restart-service command + /// This class implements the restart-service command. /// [Cmdlet(VerbsLifecycle.Restart, "Service", DefaultParameterSetName = "InputObject", SupportsShouldProcess = true, - HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113385")] + HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2097059")] [OutputType(typeof(ServiceController))] public sealed class RestartServiceCommand : ServiceOperationBaseCommand { @@ -1386,7 +1459,7 @@ protected override void ProcessRecord() continue; } - //Set the NoWait parameter to false since we are not adding this switch to this cmdlet. + // Set the NoWait parameter to false since we are not adding this switch to this cmdlet. List stoppedServices = DoStopService(serviceController, Force, true); if (stoppedServices.Count > 0) @@ -1401,53 +1474,51 @@ protected override void ProcessRecord() } } } - } // ProcessRecord - } // RestartServiceCommand + } + } #endregion RestartServiceCommand #region SetServiceCommand /// - /// This class implements the set-service command + /// This class implements the set-service command. /// [Cmdlet(VerbsCommon.Set, "Service", SupportsShouldProcess = true, DefaultParameterSetName = "Name", - HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113399", RemotingCapability = RemotingCapability.SupportedByCommand)] + HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2097148", RemotingCapability = RemotingCapability.SupportedByCommand)] [OutputType(typeof(ServiceController))] public class SetServiceCommand : ServiceOperationBaseCommand { #region Parameters /// - /// The following is the definition of the input parameter "ComputerName". - /// Set the properties of service running on the list of computer names - /// specified. The default is the local computer. - /// Type the NETBIOS name, an IP address, or a fully-qualified domain name of - /// one or more remote computers. To indicate the local computer, use the - /// computer name, "localhost" or a dot (.). When the computer is in a different - /// domain than the user, the fully-qualified domain name is required. - /// - [Parameter(ValueFromPipelineByPropertyName = true)] - [ValidateNotNullOrEmpty] - [Alias("cn")] - [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] - public String[] ComputerName { get; set; } = new string[] { "." }; - - /// - /// service name + /// Service name. /// /// - [Parameter(Position = 0, Mandatory = true, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true, ParameterSetName = "Name")] + [Parameter(Mandatory = true, ParameterSetName = "Name", Position = 0, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true)] [Alias("ServiceName", "SN")] - public new String Name + public new string Name { - get { return serviceName; } + get + { + return serviceName; + } + set { serviceName = value; } } - internal String serviceName = null; + internal string serviceName = null; + + /// + /// The following is the definition of the input parameter "InputObject". + /// Specifies a ServiceController object that represents the service to change. + /// Enter a variable that contains the objects or type a command or expression + /// that gets the objects. + /// + [Parameter(Mandatory = true, ParameterSetName = "InputObject", Position = 0, ValueFromPipeline = true)] + public new ServiceController InputObject { get; set; } /// /// The following is the definition of the input parameter "DisplayName". @@ -1458,95 +1529,129 @@ public class SetServiceCommand : ServiceOperationBaseCommand [Alias("DN")] public new string DisplayName { - get { return displayName; } + get + { + return displayName; + } + set { displayName = value; } } - internal string displayName = null; + internal string displayName = null; + /// + /// Account under which the service should run. + /// + /// + [Parameter] + [Credential()] + public PSCredential Credential { get; set; } /// /// The following is the definition of the input parameter "Description". /// Specifies a new description for the service. /// The service description appears in Services in Computer Management. /// Description is not a property of the ServiceController object that - /// Get-Service retrieve + /// Get-Service retrieve. /// [Parameter] [ValidateNotNullOrEmpty] public string Description { - get { return description; } + get + { + return description; + } + set { description = value; } } - internal string description = null; + internal string description = null; /// /// The following is the definition of the input parameter "StartupType". + /// "Set-Service -StartType" sets ServiceController.InputObject.StartType. /// Changes the starting mode of the service. Valid values for StartupType are: /// -- Automatic: Start when the system starts. /// -- Manual : Starts only when started by a user or program. - /// -- Disabled : Can + /// -- Disabled : Can. /// [Parameter] - [Alias("StartMode", "SM", "ST")] + [Alias("StartMode", "SM", "ST", "StartType")] [ValidateNotNullOrEmpty] - public ServiceStartMode StartupType + public ServiceStartupType StartupType { - get { return startupType; } + get + { + return startupType; + } + set { startupType = value; } } + // We set the initial value to an invalid value so that we can // distinguish when this is and is not set. - internal ServiceStartMode startupType = (ServiceStartMode)(-1); - - - + internal ServiceStartupType startupType = ServiceStartupType.InvalidValue; /// - /// The following is the definition of the input parameter "Status". - /// This specifies what state the service should be in (e.g. Running, Stopped, - /// Paused). If it is already in that state, do nothing. If it is not, do the + /// Sets the SecurityDescriptorSddl of the service using a SDDL string. + /// + [Parameter] + [Alias("sd")] + [ValidateNotNullOrEmpty] + public string SecurityDescriptorSddl + { + get; + set; + } + + /// + /// The following is the definition of the input parameter "Status". + /// This specifies what state the service should be in (e.g. Running, Stopped, + /// Paused). If it is already in that state, do nothing. If it is not, do the /// appropriate action to bring about the desired result (start/stop/suspend the /// service) and issue an error if this cannot be achieved. - /// Status can be Paused , Running and Stopped + /// Status can be Paused , Running and Stopped. /// [Parameter] [ValidateSetAttribute(new string[] { "Running", "Stopped", "Paused" })] public string Status { - get { return serviceStatus; } + get + { + return serviceStatus; + } + set { serviceStatus = value; } } + internal string serviceStatus = null; /// - /// The following is the definition of the input parameter "InputObject". - /// Specifies ServiceController object representing the services to be stopped. - /// Enter a variable that contains the objects or type a command or expression - /// that gets the objects. + /// The following is the definition of the input parameter "Force". + /// This parameter is useful only when parameter "Stop" is enabled. + /// If "Force" is enabled, it will also stop the dependent services. + /// If not, it will send an error when this service has dependent ones. /// - [Parameter(ValueFromPipeline = true, - ParameterSetName = "InputObject")] - public new ServiceController InputObject { get; set; } + [Parameter] + public SwitchParameter Force { get; set; } /// /// This is not a parameter for this cmdlet. /// - //This has been shadowed from base class and removed parameter tag to fix gcm "Set-Service" -syntax + // This has been shadowed from base class and removed parameter tag to fix gcm "Set-Service" -syntax [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] public new string[] Include { @@ -1554,17 +1659,19 @@ public string Status { return include; } + set { include = null; } } + internal new string[] include = null; /// /// This is not a parameter for this cmdlet. /// - //This has been shadowed from base class and removed parameter tag to fix gcm "Set-Service" -syntax + // This has been shadowed from base class and removed parameter tag to fix gcm "Set-Service" -syntax [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] public new string[] Exclude { @@ -1572,305 +1679,335 @@ public string Status { return exclude; } + set { exclude = null; } } + internal new string[] exclude = null; #endregion Parameters #region Overrides /// - /// /// - [ArchitectureSensitive] protected override void ProcessRecord() { ServiceController service = null; - string ServiceComputerName = null; - foreach (string computer in ComputerName) + IntPtr password = IntPtr.Zero; + bool objServiceShouldBeDisposed = false; + + try { - bool objServiceShouldBeDisposed = false; - try + if (InputObject != null) { - if (_ParameterSetName.Equals("InputObject", StringComparison.OrdinalIgnoreCase) && InputObject != null) - { - service = InputObject; - Name = service.ServiceName; - ServiceComputerName = service.MachineName; - //computer = service.MachineName; - objServiceShouldBeDisposed = false; - } - else - { - ServiceComputerName = computer; - service = new ServiceController(serviceName, ServiceComputerName); - objServiceShouldBeDisposed = true; - } - Diagnostics.Assert(!String.IsNullOrEmpty(Name), "null ServiceName"); - // "new ServiceController" will succeed even if - // there is no such service. This checks whether - // the service actually exists. - - string unusedByDesign = service.DisplayName; + service = InputObject; + Name = service.ServiceName; + objServiceShouldBeDisposed = false; } - catch (ArgumentException ex) + else { - //cannot use WriteNonterminatingError as service is null - ErrorRecord er = new ErrorRecord(ex, "ArgumentException", ErrorCategory.ObjectNotFound, computer); - WriteError(er); - continue; + service = new ServiceController(serviceName); + objServiceShouldBeDisposed = true; } - catch (InvalidOperationException ex) + + Diagnostics.Assert(!string.IsNullOrEmpty(Name), "null ServiceName"); + + // "new ServiceController" will succeed even if + // there is no such service. This checks whether + // the service actually exists. + string unusedByDesign = service.DisplayName; + } + catch (ArgumentException ex) + { + // cannot use WriteNonterminatingError as service is null + ErrorRecord er = new(ex, "ArgumentException", ErrorCategory.ObjectNotFound, Name); + WriteError(er); + return; + } + catch (InvalidOperationException ex) + { + // cannot use WriteNonterminatingError as service is null + ErrorRecord er = new(ex, "InvalidOperationException", ErrorCategory.ObjectNotFound, Name); + WriteError(er); + return; + } + + try // In finally we ensure dispose, if object not pipelined. + { + // confirm the operation first + // this is always false if WhatIf is set + if (!ShouldProcessServiceOperation(service)) { - //cannot use WriteNonterminatingError as service is null - ErrorRecord er = new ErrorRecord(ex, "InvalidOperationException", ErrorCategory.ObjectNotFound, computer); - WriteError(er); - continue; + return; } - try // In finally we ensure dispose, if object not pipelined. + NakedWin32Handle hScManager = IntPtr.Zero; + NakedWin32Handle hService = IntPtr.Zero; + IntPtr delayedAutoStartInfoBuffer = IntPtr.Zero; + try { - // confirm the operation first - // this is always false if WhatIf is set - if (!ShouldProcessServiceOperation(service)) + hScManager = NativeMethods.OpenSCManagerW( + string.Empty, + null, + NativeMethods.SC_MANAGER_CONNECT + ); + + if (hScManager == IntPtr.Zero) { - continue; + int lastError = Marshal.GetLastWin32Error(); + Win32Exception exception = new(lastError); + WriteNonTerminatingError( + service, + exception, + "FailToOpenServiceControlManager", + ServiceResources.FailToOpenServiceControlManager, + ErrorCategory.PermissionDenied); + return; } - NakedWin32Handle hScManager = IntPtr.Zero; - NakedWin32Handle hService = IntPtr.Zero; - try + var access = NativeMethods.SERVICE_CHANGE_CONFIG; + if (!string.IsNullOrEmpty(SecurityDescriptorSddl)) + access |= NativeMethods.WRITE_DAC | NativeMethods.WRITE_OWNER; + + hService = NativeMethods.OpenServiceW( + hScManager, + Name, + access + ); + + if (hService == IntPtr.Zero) { - hScManager = NativeMethods.OpenSCManagerW( - ServiceComputerName, - null, - NativeMethods.SC_MANAGER_CONNECT - ); - if (IntPtr.Zero == hScManager) + int lastError = Marshal.GetLastWin32Error(); + Win32Exception exception = new(lastError); + WriteNonTerminatingError( + service, + exception, + "CouldNotSetService", + ServiceResources.CouldNotSetService, + ErrorCategory.PermissionDenied); + return; + } + // Modify startup type or display name or credential + if (!string.IsNullOrEmpty(DisplayName) + || StartupType != ServiceStartupType.InvalidValue || Credential != null) + { + DWORD dwStartType = NativeMethods.SERVICE_NO_CHANGE; + if (!NativeMethods.TryGetNativeStartupType(StartupType, out dwStartType)) { - int lastError = Marshal.GetLastWin32Error(); - Win32Exception exception = new Win32Exception(lastError); - WriteNonTerminatingError( - service, - ServiceComputerName, - exception, - "ComputerAccessDenied", - ServiceResources.ComputerAccessDenied, - ErrorCategory.PermissionDenied); - continue; + WriteNonTerminatingError(StartupType.ToString(), "Set-Service", Name, + new ArgumentException(), "CouldNotSetService", + ServiceResources.UnsupportedStartupType, + ErrorCategory.InvalidArgument); + return; } - hService = NativeMethods.OpenServiceW( - hScManager, - Name, - NativeMethods.SERVICE_CHANGE_CONFIG + + string username = null; + if (Credential != null) + { + username = Credential.UserName; + password = Marshal.SecureStringToCoTaskMemUnicode(Credential.Password); + } + + bool succeeded = NativeMethods.ChangeServiceConfigW( + hService, + NativeMethods.SERVICE_NO_CHANGE, + dwStartType, + NativeMethods.SERVICE_NO_CHANGE, + null, + null, + IntPtr.Zero, + null, + username, + password, + DisplayName ); - if (IntPtr.Zero == hService) + if (!succeeded) { int lastError = Marshal.GetLastWin32Error(); - Win32Exception exception = new Win32Exception(lastError); + Win32Exception exception = new(lastError); WriteNonTerminatingError( service, exception, "CouldNotSetService", ServiceResources.CouldNotSetService, ErrorCategory.PermissionDenied); - continue; + return; } + } - // modify startup type or display name - if (!String.IsNullOrEmpty(DisplayName) - || (ServiceStartMode)(-1) != StartupType) - { - DWORD dwStartType = NativeMethods.SERVICE_NO_CHANGE; - switch (StartupType) - { - case ServiceStartMode.Automatic: - dwStartType = NativeMethods.SERVICE_AUTO_START; - break; - case ServiceStartMode.Manual: - dwStartType = NativeMethods.SERVICE_DEMAND_START; - break; - case ServiceStartMode.Disabled: - dwStartType = NativeMethods.SERVICE_DISABLED; - break; - default: - Diagnostics.Assert( - ((ServiceStartMode)(-1)) == StartupType, - "bad StartupType"); - break; - } - bool succeeded = NativeMethods.ChangeServiceConfigW( - hService, - NativeMethods.SERVICE_NO_CHANGE, - dwStartType, - NativeMethods.SERVICE_NO_CHANGE, - null, - null, - IntPtr.Zero, - null, - null, - IntPtr.Zero, - DisplayName - ); - if (!succeeded) - { - int lastError = Marshal.GetLastWin32Error(); - Win32Exception exception = new Win32Exception(lastError); - WriteNonTerminatingError( - service, - exception, - "CouldNotSetService", - ServiceResources.CouldNotSetService, - ErrorCategory.PermissionDenied); - continue; - } - } // modify startup type or display name - - NativeMethods.SERVICE_DESCRIPTIONW sd = new NativeMethods.SERVICE_DESCRIPTIONW(); - sd.lpDescription = Description; - int size = Marshal.SizeOf(sd); - IntPtr buffer = Marshal.AllocCoTaskMem(size); - Marshal.StructureToPtr(sd, buffer, false); + NativeMethods.SERVICE_DESCRIPTIONW sd = new(); + sd.lpDescription = Description; + int size = Marshal.SizeOf(sd); + IntPtr buffer = Marshal.AllocCoTaskMem(size); + Marshal.StructureToPtr(sd, buffer, false); - bool status = NativeMethods.ChangeServiceConfig2W( - hService, - NativeMethods.SERVICE_CONFIG_DESCRIPTION, - buffer); + bool status = NativeMethods.ChangeServiceConfig2W( + hService, + NativeMethods.SERVICE_CONFIG_DESCRIPTION, + buffer); - if (!status) - { - int lastError = Marshal.GetLastWin32Error(); - Win32Exception exception = new Win32Exception(lastError); - WriteNonTerminatingError( - service, - exception, - "CouldNotSetServiceDescription", - ServiceResources.CouldNotSetServiceDescription, - ErrorCategory.PermissionDenied); - } + if (!status) + { + int lastError = Marshal.GetLastWin32Error(); + Win32Exception exception = new(lastError); + WriteNonTerminatingError( + service, + exception, + "CouldNotSetServiceDescription", + ServiceResources.CouldNotSetServiceDescription, + ErrorCategory.PermissionDenied); + } + // Set the delayed auto start + NativeMethods.SERVICE_DELAYED_AUTO_START_INFO ds = new(); + ds.fDelayedAutostart = StartupType == ServiceStartupType.AutomaticDelayedStart; + size = Marshal.SizeOf(ds); + delayedAutoStartInfoBuffer = Marshal.AllocCoTaskMem(size); + Marshal.StructureToPtr(ds, delayedAutoStartInfoBuffer, false); + status = NativeMethods.ChangeServiceConfig2W( + hService, + NativeMethods.SERVICE_CONFIG_DELAYED_AUTO_START_INFO, + delayedAutoStartInfoBuffer); - //Addition by v-ramch Mar 11 2008 - //if Status parameter specified do the necessary action - //to bring about the desired result + if (!status) + { + int lastError = Marshal.GetLastWin32Error(); + Win32Exception exception = new(lastError); + WriteNonTerminatingError( + Name, + DisplayName, + Name, + exception, + "CouldNotSetServiceDelayedAutoStart", + ServiceResources.CouldNotSetServiceDelayedAutoStart, + ErrorCategory.PermissionDenied); + } - if (!string.IsNullOrEmpty(Status)) + // Handle the '-Status' parameter + if (!string.IsNullOrEmpty(Status)) + { + if (Status.Equals("Running", StringComparison.OrdinalIgnoreCase)) { - if (Status.Equals("Running", StringComparison.OrdinalIgnoreCase)) + if (!service.Status.Equals(ServiceControllerStatus.Running)) { - if (!service.Status.Equals(ServiceControllerStatus.Running)) - { - if (service.Status.Equals(ServiceControllerStatus.Paused)) - //resume service - DoResumeService(service); - else - //start service - DoStartService(service); - } + if (service.Status.Equals(ServiceControllerStatus.Paused)) + // resume service + DoResumeService(service); + else + // start service + DoStartService(service); } - else if (Status.Equals("Stopped", StringComparison.CurrentCultureIgnoreCase)) + } + else if (Status.Equals("Stopped", StringComparison.OrdinalIgnoreCase)) + { + if (!service.Status.Equals(ServiceControllerStatus.Stopped)) { - if (!service.Status.Equals(ServiceControllerStatus.Stopped)) + // Check for the dependent services as set-service dont have force parameter + ServiceController[] dependentServices = service.DependentServices; + + if ((!Force) && (dependentServices != null) && (dependentServices.Length > 0)) { - //check for the dependent services as set-service dont have force parameter - ServiceController[] dependentServices = service.DependentServices; - - if ((dependentServices != null) && (dependentServices.Length > 0)) - { - WriteNonTerminatingError(service, null, "ServiceHasDependentServicesNoForce", ServiceResources.ServiceHasDependentServicesNoForce, ErrorCategory.InvalidOperation); - continue; - } - - ServiceController[] servicedependedon = service.ServicesDependedOn; - - if ((servicedependedon != null) && (servicedependedon.Length > 0)) - { - WriteNonTerminatingError(service, null, "ServiceIsDependentOnNoForce", ServiceResources.ServiceIsDependentOnNoForce, ErrorCategory.InvalidOperation); - continue; - } - //stop service,give the force parameter always true as we have already checked for the dependent services - //Specify NoWait parameter as always false since we are not adding this switch to this cmdlet - DoStopService(service, true, true); + WriteNonTerminatingError(service, null, "ServiceHasDependentServicesNoForce", ServiceResources.ServiceHasDependentServicesNoForce, ErrorCategory.InvalidOperation); + return; } - } - else if (Status.Equals("Paused", StringComparison.CurrentCultureIgnoreCase)) - { - if (!service.Status.Equals(ServiceControllerStatus.Paused)) - //pause service - DoPauseService(service); + + // Stop service, pass 'true' to the force parameter as we have already checked for the dependent services. + DoStopService(service, Force, waitForServiceToStop: true); } } - if (PassThru.IsPresent) + else if (Status.Equals("Paused", StringComparison.OrdinalIgnoreCase)) { - //to display the service,refreshing the service would not show the display name after updating - ServiceController displayservice = new ServiceController(Name, ServiceComputerName); - WriteObject(displayservice); + if (!service.Status.Equals(ServiceControllerStatus.Paused)) + { + DoPauseService(service); + } } } - finally + + if (!string.IsNullOrEmpty(SecurityDescriptorSddl)) { - if (IntPtr.Zero != hService) + SetServiceSecurityDescriptor(service, SecurityDescriptorSddl, hService); + } + + if (PassThru.IsPresent) + { + // To display the service, refreshing the service would not show the display name after updating + ServiceController displayservice = new(Name); + WriteObject(displayservice); + } + } + finally + { + if (delayedAutoStartInfoBuffer != IntPtr.Zero) + { + Marshal.FreeCoTaskMem(delayedAutoStartInfoBuffer); + } + + if (hService != IntPtr.Zero) + { + bool succeeded = NativeMethods.CloseServiceHandle(hService); + if (!succeeded) { - bool succeeded = NativeMethods.CloseServiceHandle(hService); - if (!succeeded) - { - int lastError = Marshal.GetLastWin32Error(); - Win32Exception exception = new Win32Exception(lastError); - WriteNonTerminatingError( - service, - exception, - "CouldNotSetServiceDescription", - ServiceResources.CouldNotSetServiceDescription, - ErrorCategory.PermissionDenied); - } + int lastError = Marshal.GetLastWin32Error(); + Win32Exception exception = new(lastError); + WriteNonTerminatingError( + service, + exception, + "CouldNotSetServiceDescription", + ServiceResources.CouldNotSetServiceDescription, + ErrorCategory.PermissionDenied); } + } - if (IntPtr.Zero != hScManager) + if (hScManager != IntPtr.Zero) + { + bool succeeded = NativeMethods.CloseServiceHandle(hScManager); + if (!succeeded) { - bool succeeded = NativeMethods.CloseServiceHandle(hScManager); - if (!succeeded) - { - int lastError = Marshal.GetLastWin32Error(); - Win32Exception exception = new Win32Exception(lastError); - WriteNonTerminatingError( - service, - exception, - "CouldNotSetServiceDescription", - ServiceResources.CouldNotSetServiceDescription, - ErrorCategory.PermissionDenied); - } + int lastError = Marshal.GetLastWin32Error(); + Win32Exception exception = new(lastError); + WriteNonTerminatingError( + service, + exception, + "CouldNotSetServiceDescription", + ServiceResources.CouldNotSetServiceDescription, + ErrorCategory.PermissionDenied); } - } // finally - } //End try - finally - { - if (objServiceShouldBeDisposed) - { - service.Dispose(); } } - }//end for + } + finally + { + if (password != IntPtr.Zero) + { + Marshal.ZeroFreeCoTaskMemUnicode(password); + } + + if (objServiceShouldBeDisposed) + { + service.Dispose(); + } + } } #endregion Overrides - - } // class SetServiceCommand + } #endregion SetServiceCommand #region NewServiceCommand /// - /// This class implements the set-service command + /// This class implements the New-Service command. /// - [Cmdlet(VerbsCommon.New, "Service", SupportsShouldProcess = true, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113359")] + [Cmdlet(VerbsCommon.New, "Service", SupportsShouldProcess = true, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2096905")] [OutputType(typeof(ServiceController))] public class NewServiceCommand : ServiceBaseCommand { #region Parameters /// - /// Name of the service to create + /// Name of the service to create. /// /// [Parameter(Position = 0, Mandatory = true)] @@ -1878,24 +2015,29 @@ public class NewServiceCommand : ServiceBaseCommand public string Name { get { return serviceName; } + set { serviceName = value; } } + internal string serviceName = null; /// - /// The executable which implements this service + /// The executable which implements this service. /// /// [Parameter(Position = 1, Mandatory = true)] + [Alias("Path")] public string BinaryPathName { get { return binaryPathName; } + set { binaryPathName = value; } } + internal string binaryPathName = null; /// - /// DisplayName of the service to create + /// DisplayName of the service to create. /// /// [Parameter] @@ -1903,12 +2045,14 @@ public string BinaryPathName public string DisplayName { get { return displayName; } + set { displayName = value; } } + internal string displayName = null; /// - /// Description of the service to create + /// Description of the service to create. /// /// [Parameter] @@ -1916,8 +2060,10 @@ public string DisplayName public string Description { get { return description; } + set { description = value; } } + internal string description = null; /// @@ -1925,15 +2071,17 @@ public string Description /// /// [Parameter] - public ServiceStartMode StartupType + public ServiceStartupType StartupType { get { return startupType; } + set { startupType = value; } } - internal ServiceStartMode startupType = ServiceStartMode.Automatic; + + internal ServiceStartupType startupType = ServiceStartupType.Automatic; /// - /// Account under which the service should run + /// Account under which the service should run. /// /// [Parameter] @@ -1941,38 +2089,54 @@ public ServiceStartMode StartupType public PSCredential Credential { get { return credential; } + set { credential = value; } } + internal PSCredential credential = null; /// - /// Other services on which the new service depends + /// Sets the SecurityDescriptorSddl of the service using a SDDL string. + /// + [Parameter] + [Alias("sd")] + [ValidateNotNullOrEmpty] + public string SecurityDescriptorSddl + { + get; + set; + } + + /// + /// Other services on which the new service depends. /// /// [Parameter] public string[] DependsOn { get { return dependsOn; } + set { dependsOn = value; } } + internal string[] dependsOn = null; #endregion Parameters #region Overrides /// - /// Create the service + /// Create the service. /// - [ArchitectureSensitive] protected override void BeginProcessing() { - Diagnostics.Assert(!String.IsNullOrEmpty(Name), + ServiceController service = null; + Diagnostics.Assert(!string.IsNullOrEmpty(Name), "null ServiceName"); - Diagnostics.Assert(!String.IsNullOrEmpty(BinaryPathName), + Diagnostics.Assert(!string.IsNullOrEmpty(BinaryPathName), "null BinaryPathName"); // confirm the operation first // this is always false if WhatIf is set - if (!ShouldProcessServiceOperation(DisplayName ?? "", Name)) + if (!ShouldProcessServiceOperation(DisplayName ?? string.Empty, Name)) { return; } @@ -1981,6 +2145,7 @@ protected override void BeginProcessing() NakedWin32Handle hScManager = IntPtr.Zero; NakedWin32Handle hService = IntPtr.Zero; IntPtr password = IntPtr.Zero; + IntPtr delayedAutoStartInfoBuffer = IntPtr.Zero; try { hScManager = NativeMethods.OpenSCManagerW( @@ -1988,10 +2153,10 @@ protected override void BeginProcessing() null, NativeMethods.SC_MANAGER_CONNECT | NativeMethods.SC_MANAGER_CREATE_SERVICE ); - if (IntPtr.Zero == hScManager) + if (hScManager == IntPtr.Zero) { int lastError = Marshal.GetLastWin32Error(); - Win32Exception exception = new Win32Exception(lastError); + Win32Exception exception = new(lastError); WriteNonTerminatingError( Name, DisplayName, @@ -2002,34 +2167,25 @@ protected override void BeginProcessing() ErrorCategory.PermissionDenied); return; } - DWORD dwStartType = NativeMethods.SERVICE_AUTO_START; - switch (StartupType) + + if (!NativeMethods.TryGetNativeStartupType(StartupType, out DWORD dwStartType)) { - case ServiceStartMode.Automatic: - dwStartType = NativeMethods.SERVICE_AUTO_START; - break; - case ServiceStartMode.Manual: - dwStartType = NativeMethods.SERVICE_DEMAND_START; - break; - case ServiceStartMode.Disabled: - dwStartType = NativeMethods.SERVICE_DISABLED; - break; - default: - Diagnostics.Assert( - ((ServiceStartMode)(-1)) == StartupType, - "bad StartupType"); - break; + WriteNonTerminatingError(StartupType.ToString(), "New-Service", Name, + new ArgumentException(), "CouldNotNewService", + ServiceResources.UnsupportedStartupType, + ErrorCategory.InvalidArgument); + return; } - // set up the double-null-terminated lpDependencies parameter IntPtr lpDependencies = IntPtr.Zero; - if (null != DependsOn) + if (DependsOn != null) { int numchars = 1; // final null foreach (string dependedOn in DependsOn) { numchars += dependedOn.Length + 1; } + char[] doubleNullArray = new char[numchars]; int pos = 0; foreach (string dependedOn in DependsOn) @@ -2042,6 +2198,7 @@ protected override void BeginProcessing() pos += dependedOn.Length; doubleNullArray[pos++] = (char)0; // null terminator } + doubleNullArray[pos++] = (char)0; // double-null terminator Diagnostics.Assert(pos == numchars, "lpDependencies build error"); lpDependencies = Marshal.AllocHGlobal( @@ -2051,7 +2208,7 @@ protected override void BeginProcessing() // set up the Credential parameter string username = null; - if (null != Credential) + if (Credential != null) { username = Credential.UserName; password = Marshal.SecureStringToCoTaskMemUnicode(Credential.Password); @@ -2062,7 +2219,7 @@ protected override void BeginProcessing() hScManager, Name, DisplayName, - NativeMethods.SERVICE_CHANGE_CONFIG, + NativeMethods.SERVICE_CHANGE_CONFIG | NativeMethods.WRITE_DAC | NativeMethods.WRITE_OWNER, NativeMethods.SERVICE_WIN32_OWN_PROCESS, dwStartType, NativeMethods.SERVICE_ERROR_NORMAL, @@ -2073,10 +2230,10 @@ protected override void BeginProcessing() username, password ); - if (IntPtr.Zero == hService) + if (hService == IntPtr.Zero) { int lastError = Marshal.GetLastWin32Error(); - Win32Exception exception = new Win32Exception(lastError); + Win32Exception exception = new(lastError); WriteNonTerminatingError( Name, DisplayName, @@ -2089,7 +2246,7 @@ protected override void BeginProcessing() } // Set the service description - NativeMethods.SERVICE_DESCRIPTIONW sd = new NativeMethods.SERVICE_DESCRIPTIONW(); + NativeMethods.SERVICE_DESCRIPTIONW sd = new(); sd.lpDescription = Description; int size = Marshal.SizeOf(sd); IntPtr buffer = Marshal.AllocCoTaskMem(size); @@ -2103,7 +2260,7 @@ protected override void BeginProcessing() if (!succeeded) { int lastError = Marshal.GetLastWin32Error(); - Win32Exception exception = new Win32Exception(lastError); + Win32Exception exception = new(lastError); WriteNonTerminatingError( Name, DisplayName, @@ -2114,27 +2271,64 @@ protected override void BeginProcessing() ErrorCategory.PermissionDenied); } + // Set the delayed auto start + if (StartupType == ServiceStartupType.AutomaticDelayedStart) + { + NativeMethods.SERVICE_DELAYED_AUTO_START_INFO ds = new(); + ds.fDelayedAutostart = true; + size = Marshal.SizeOf(ds); + delayedAutoStartInfoBuffer = Marshal.AllocCoTaskMem(size); + Marshal.StructureToPtr(ds, delayedAutoStartInfoBuffer, false); + + succeeded = NativeMethods.ChangeServiceConfig2W( + hService, + NativeMethods.SERVICE_CONFIG_DELAYED_AUTO_START_INFO, + delayedAutoStartInfoBuffer); + + if (!succeeded) + { + int lastError = Marshal.GetLastWin32Error(); + Win32Exception exception = new(lastError); + WriteNonTerminatingError( + Name, + DisplayName, + Name, + exception, + "CouldNotNewServiceDelayedAutoStart", + ServiceResources.CouldNotNewServiceDelayedAutoStart, + ErrorCategory.PermissionDenied); + } + } + // write the ServiceController for the new service - using (ServiceController service = - new ServiceController(Name)) // ensure dispose + service = new ServiceController(Name); + + if (!string.IsNullOrEmpty(SecurityDescriptorSddl)) { - WriteObject(service); + SetServiceSecurityDescriptor(service, SecurityDescriptorSddl, hService); } + + WriteObject(service); } finally { - if (IntPtr.Zero != password) + if (delayedAutoStartInfoBuffer != IntPtr.Zero) + { + Marshal.FreeCoTaskMem(delayedAutoStartInfoBuffer); + } + + if (password != IntPtr.Zero) { Marshal.ZeroFreeCoTaskMemUnicode(password); } - if (IntPtr.Zero != hService) + if (hService != IntPtr.Zero) { bool succeeded = NativeMethods.CloseServiceHandle(hService); if (!succeeded) { int lastError = Marshal.GetLastWin32Error(); - Win32Exception exception = new Win32Exception(lastError); + Win32Exception exception = new(lastError); WriteNonTerminatingError( Name, DisplayName, @@ -2146,13 +2340,13 @@ protected override void BeginProcessing() } } - if (IntPtr.Zero != hScManager) + if (hScManager != IntPtr.Zero) { bool succeeded = NativeMethods.CloseServiceHandle(hScManager); if (!succeeded) { int lastError = Marshal.GetLastWin32Error(); - Win32Exception exception = new Win32Exception(lastError); + Win32Exception exception = new(lastError); WriteNonTerminatingError( Name, DisplayName, @@ -2166,21 +2360,189 @@ protected override void BeginProcessing() } } #endregion Overrides - } // class NewServiceCommand + } #endregion NewServiceCommand + #region RemoveServiceCommand + /// + /// This class implements the Remove-Service command. + /// + [Cmdlet(VerbsCommon.Remove, "Service", SupportsShouldProcess = true, DefaultParameterSetName = "Name", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2248980")] + public class RemoveServiceCommand : ServiceBaseCommand + { + #region Parameters + + /// + /// Name of the service to remove. + /// + [Parameter(Position = 0, Mandatory = true, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true, ParameterSetName = "Name")] + [Alias("ServiceName", "SN")] + public string Name { get; set; } + + /// + /// The following is the definition of the input parameter "InputObject". + /// Specifies ServiceController object representing the services to be removed. + /// Enter a variable that contains the objects or type a command or expression + /// that gets the objects. + /// + [Parameter(ValueFromPipeline = true, ParameterSetName = "InputObject")] + public ServiceController InputObject { get; set; } + + #endregion Parameters + + #region Overrides + /// + /// Remove the service. + /// + protected override void ProcessRecord() + { + ServiceController service = null; + bool objServiceShouldBeDisposed = false; + try + { + if (InputObject != null) + { + service = InputObject; + Name = service.ServiceName; + objServiceShouldBeDisposed = false; + } + else + { + service = new ServiceController(Name); + objServiceShouldBeDisposed = true; + } + + Diagnostics.Assert(!string.IsNullOrEmpty(Name), "null ServiceName"); + + // "new ServiceController" will succeed even if there is no such service. + // This checks whether the service actually exists. + string unusedByDesign = service.DisplayName; + } + catch (ArgumentException ex) + { + // Cannot use WriteNonterminatingError as service is null + ErrorRecord er = new(ex, "ArgumentException", ErrorCategory.ObjectNotFound, Name); + WriteError(er); + return; + } + catch (InvalidOperationException ex) + { + // Cannot use WriteNonterminatingError as service is null + ErrorRecord er = new(ex, "InvalidOperationException", ErrorCategory.ObjectNotFound, Name); + WriteError(er); + return; + } + + try // In finally we ensure dispose, if object not pipelined. + { + // Confirm the operation first. + // This is always false if WhatIf is set. + if (!ShouldProcessServiceOperation(service)) + { + return; + } + + NakedWin32Handle hScManager = IntPtr.Zero; + NakedWin32Handle hService = IntPtr.Zero; + try + { + hScManager = NativeMethods.OpenSCManagerW( + lpMachineName: string.Empty, + lpDatabaseName: null, + dwDesiredAccess: NativeMethods.SC_MANAGER_ALL_ACCESS + ); + if (hScManager == IntPtr.Zero) + { + int lastError = Marshal.GetLastWin32Error(); + Win32Exception exception = new(lastError); + WriteObject(exception); + WriteNonTerminatingError( + service, + exception, + "FailToOpenServiceControlManager", + ServiceResources.FailToOpenServiceControlManager, + ErrorCategory.PermissionDenied); + return; + } + + hService = NativeMethods.OpenServiceW( + hScManager, + Name, + NativeMethods.SERVICE_DELETE + ); + if (hService == IntPtr.Zero) + { + int lastError = Marshal.GetLastWin32Error(); + Win32Exception exception = new(lastError); + WriteNonTerminatingError( + service, + exception, + "CouldNotRemoveService", + ServiceResources.CouldNotRemoveService, + ErrorCategory.PermissionDenied); + return; + } + + bool status = NativeMethods.DeleteService(hService); + + if (!status) + { + int lastError = Marshal.GetLastWin32Error(); + Win32Exception exception = new(lastError); + WriteNonTerminatingError( + service, + exception, + "CouldNotRemoveService", + ServiceResources.CouldNotRemoveService, + ErrorCategory.PermissionDenied); + } + } + finally + { + if (hService != IntPtr.Zero) + { + bool succeeded = NativeMethods.CloseServiceHandle(hService); + if (!succeeded) + { + int lastError = Marshal.GetLastWin32Error(); + Diagnostics.Assert(lastError != 0, "ErrorCode not success"); + } + } + + if (hScManager != IntPtr.Zero) + { + bool succeeded = NativeMethods.CloseServiceHandle(hScManager); + if (!succeeded) + { + int lastError = Marshal.GetLastWin32Error(); + Diagnostics.Assert(lastError != 0, "ErrorCode not success"); + } + } + } + } + finally + { + if (objServiceShouldBeDisposed) + { + service.Dispose(); + } + } + } + #endregion Overrides + } + #endregion RemoveServiceCommand + #region ServiceCommandException /// - /// Non-terminating errors occurring in the service noun commands + /// Non-terminating errors occurring in the service noun commands. /// - [Serializable] public class ServiceCommandException : SystemException { #region ctors /// - /// unimplemented standard constructor + /// Unimplemented standard constructor. /// - /// doesn't return + /// Doesn't return. public ServiceCommandException() : base() { @@ -2188,17 +2550,17 @@ public ServiceCommandException() } /// - /// standard constructor + /// Standard constructor. /// /// - /// constructed object + /// Constructed object. public ServiceCommandException(string message) : base(message) { } /// - /// standard constructor + /// Standard constructor. /// /// /// @@ -2210,52 +2572,34 @@ public ServiceCommandException(string message, Exception innerException) #region Serialization /// - /// serialization constructor + /// Serialization constructor. /// /// /// - /// constructed object + /// Constructed object. + [Obsolete("Legacy serialization support is deprecated since .NET 8, hence this method is now marked as obsolete", DiagnosticId = "SYSLIB0051")] protected ServiceCommandException(SerializationInfo info, StreamingContext context) - : base(info, context) { - if (info == null) - { - throw new ArgumentNullException("info"); - } - - _serviceName = info.GetString("ServiceName"); + throw new NotSupportedException(); } - /// - /// Serializer - /// - /// serialization information - /// streaming context - [SecurityPermissionAttribute(SecurityAction.Demand, SerializationFormatter = true)] - public override void GetObjectData(SerializationInfo info, StreamingContext context) - { - if (info == null) - { - throw new ArgumentNullException("info"); - } - base.GetObjectData(info, context); - info.AddValue("ServiceName", _serviceName); - } #endregion Serialization #region Properties /// - /// Name of the service which could not be found or operated upon + /// Name of the service which could not be found or operated upon. /// /// public string ServiceName { get { return _serviceName; } + set { _serviceName = value; } } - private string _serviceName = String.Empty; + + private string _serviceName = string.Empty; #endregion Properties - } // class ServiceCommandException + } #endregion ServiceCommandException #region NativeMethods @@ -2264,15 +2608,23 @@ internal static class NativeMethods // from winuser.h internal const int ERROR_SERVICE_ALREADY_RUNNING = 1056; internal const int ERROR_SERVICE_NOT_ACTIVE = 1062; + internal const int ERROR_INSUFFICIENT_BUFFER = 122; + internal const DWORD ERROR_ACCESS_DENIED = 0x5; internal const DWORD SC_MANAGER_CONNECT = 1; internal const DWORD SC_MANAGER_CREATE_SERVICE = 2; + internal const DWORD SC_MANAGER_ALL_ACCESS = 0xf003f; internal const DWORD SERVICE_QUERY_CONFIG = 1; internal const DWORD SERVICE_CHANGE_CONFIG = 2; + internal const DWORD SERVICE_DELETE = 0x10000; internal const DWORD SERVICE_NO_CHANGE = 0xffffffff; internal const DWORD SERVICE_AUTO_START = 0x2; internal const DWORD SERVICE_DEMAND_START = 0x3; internal const DWORD SERVICE_DISABLED = 0x4; internal const DWORD SERVICE_CONFIG_DESCRIPTION = 1; + internal const DWORD SERVICE_CONFIG_DELAYED_AUTO_START_INFO = 3; + internal const DWORD SERVICE_CONFIG_SERVICE_SID_INFO = 5; + internal const DWORD WRITE_DAC = 262144; + internal const DWORD WRITE_OWNER = 524288; internal const DWORD SERVICE_WIN32_OWN_PROCESS = 0x10; internal const DWORD SERVICE_ERROR_NORMAL = 1; @@ -2293,12 +2645,37 @@ NakedWin32Handle OpenServiceW( DWORD dwDesiredAccess ); + [DllImport(PinvokeDllNames.QueryServiceConfigDllName, CharSet = CharSet.Unicode, SetLastError = true)] + internal static extern + bool QueryServiceConfigW( + NakedWin32Handle hSCManager, + IntPtr lpServiceConfig, + DWORD cbBufSize, + out DWORD pcbBytesNeeded + ); + + [DllImport(PinvokeDllNames.QueryServiceConfig2DllName, CharSet = CharSet.Unicode, SetLastError = true)] + internal static extern + bool QueryServiceConfig2W( + NakedWin32Handle hService, + DWORD dwInfoLevel, + IntPtr lpBuffer, + DWORD cbBufSize, + out DWORD pcbBytesNeeded + ); + [DllImport(PinvokeDllNames.CloseServiceHandleDllName, CharSet = CharSet.Unicode, SetLastError = true)] internal static extern bool CloseServiceHandle( NakedWin32Handle hSCManagerOrService ); + [DllImport(PinvokeDllNames.DeleteServiceDllName, CharSet = CharSet.Unicode, SetLastError = true)] + internal static extern + bool DeleteService( + NakedWin32Handle hService + ); + [DllImport(PinvokeDllNames.ChangeServiceConfigWDllName, CharSet = CharSet.Unicode, SetLastError = true)] internal static extern bool ChangeServiceConfigW( @@ -2328,7 +2705,27 @@ internal struct SERVICE_DESCRIPTIONW { [MarshalAs(UnmanagedType.LPWStr)] internal string lpDescription; - }; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct QUERY_SERVICE_CONFIG + { + internal uint dwServiceType; + internal uint dwStartType; + internal uint dwErrorControl; + [MarshalAs(UnmanagedType.LPWStr)] internal string lpBinaryPathName; + [MarshalAs(UnmanagedType.LPWStr)] internal string lpLoadOrderGroup; + internal uint dwTagId; + [MarshalAs(UnmanagedType.LPWStr)] internal string lpDependencies; + [MarshalAs(UnmanagedType.LPWStr)] internal string lpServiceStartName; + [MarshalAs(UnmanagedType.LPWStr)] internal string lpDisplayName; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct SERVICE_DELAYED_AUTO_START_INFO + { + internal bool fDelayedAutostart; + } [DllImport(PinvokeDllNames.CreateServiceWDllName, CharSet = CharSet.Unicode, SetLastError = true)] internal static extern @@ -2348,70 +2745,168 @@ NakedWin32Handle CreateServiceW( [In] IntPtr lpPassword ); + [DllImport(PinvokeDllNames.SetServiceObjectSecurityDllName, CharSet = CharSet.Unicode, SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] + internal static extern + bool SetServiceObjectSecurity( + NakedWin32Handle hSCManager, + System.Security.AccessControl.SecurityInfos dwSecurityInformation, + byte[] lpSecurityDescriptor + ); + + internal static bool QueryServiceConfig(NakedWin32Handle hService, out NativeMethods.QUERY_SERVICE_CONFIG configStructure) + { + IntPtr lpBuffer = IntPtr.Zero; + configStructure = default(NativeMethods.QUERY_SERVICE_CONFIG); + DWORD bufferSize, bufferSizeNeeded = 0; + bool status = NativeMethods.QueryServiceConfigW( + hSCManager: hService, + lpServiceConfig: lpBuffer, + cbBufSize: 0, + pcbBytesNeeded: out bufferSizeNeeded); + + if (!status && Marshal.GetLastWin32Error() != ERROR_INSUFFICIENT_BUFFER) + { + return status; + } + + try + { + lpBuffer = Marshal.AllocCoTaskMem((int)bufferSizeNeeded); + bufferSize = bufferSizeNeeded; + + status = NativeMethods.QueryServiceConfigW( + hService, + lpBuffer, + bufferSize, + out bufferSizeNeeded); + configStructure = (NativeMethods.QUERY_SERVICE_CONFIG)Marshal.PtrToStructure(lpBuffer, typeof(NativeMethods.QUERY_SERVICE_CONFIG)); + } + finally + { + Marshal.FreeCoTaskMem(lpBuffer); + } + + return status; + } + + internal static bool QueryServiceConfig2(NakedWin32Handle hService, DWORD infolevel, out T configStructure) + { + IntPtr lpBuffer = IntPtr.Zero; + configStructure = default(T); + DWORD bufferSize, bufferSizeNeeded = 0; + + bool status = NativeMethods.QueryServiceConfig2W( + hService: hService, + dwInfoLevel: infolevel, + lpBuffer: lpBuffer, + cbBufSize: 0, + pcbBytesNeeded: out bufferSizeNeeded); + + if (!status && Marshal.GetLastWin32Error() != ERROR_INSUFFICIENT_BUFFER) + { + return status; + } + + try + { + lpBuffer = Marshal.AllocCoTaskMem((int)bufferSizeNeeded); + bufferSize = bufferSizeNeeded; + + status = NativeMethods.QueryServiceConfig2W( + hService, + infolevel, + lpBuffer, + bufferSize, + out bufferSizeNeeded); + configStructure = (T)Marshal.PtrToStructure(lpBuffer, typeof(T)); + } + finally + { + Marshal.FreeCoTaskMem(lpBuffer); + } + + return status; + } + /// - /// CreateJobObject API creates or opens a job object. + /// Get appropriate win32 StartupType. /// - /// - /// A pointer to a SECURITY_ATTRIBUTES structure that specifies the security descriptor for the - /// job object and determines whether child processes can inherit the returned handle. - /// If lpJobAttributes is NULL, the job object gets a default security descriptor - /// and the handle cannot be inherited. + /// + /// StartupType provided by the user. /// - /// - /// The name of the job. + /// + /// Out parameter of the native win32 StartupType /// /// - /// If the function succeeds, the return value is a handle to the job object. - /// If the object existed before the function call, the function - /// returns a handle to the existing job object. + /// If a supported StartupType is provided, funciton returns true, otherwise false. /// - [DllImport(PinvokeDllNames.CreateJobObjectDllName, CharSet = CharSet.Unicode)] - internal static extern IntPtr CreateJobObject(IntPtr lpJobAttributes, string lpName); + internal static bool TryGetNativeStartupType(ServiceStartupType StartupType, out DWORD dwStartType) + { + bool success = true; + dwStartType = NativeMethods.SERVICE_NO_CHANGE; + switch (StartupType) + { + case ServiceStartupType.Automatic: + case ServiceStartupType.AutomaticDelayedStart: + dwStartType = NativeMethods.SERVICE_AUTO_START; + break; + case ServiceStartupType.Manual: + dwStartType = NativeMethods.SERVICE_DEMAND_START; + break; + case ServiceStartupType.Disabled: + dwStartType = NativeMethods.SERVICE_DISABLED; + break; + case ServiceStartupType.InvalidValue: + dwStartType = NativeMethods.SERVICE_NO_CHANGE; + break; + default: + success = false; + break; + } - /// - /// AssignProcessToJobObject API is used to assign a process to an existing job object. - /// - /// - /// A handle to the job object to which the process will be associated. - /// - /// - /// A handle to the process to associate with the job object. - /// - /// If the function succeeds, the return value is nonzero. - /// If the function fails, the return value is zero. - /// - [DllImport(PinvokeDllNames.AssignProcessToJobObjectDllName, CharSet = CharSet.Unicode)] - [return: MarshalAs(UnmanagedType.Bool)] - internal static extern bool AssignProcessToJobObject(SafeHandle hJob, IntPtr hProcess); + return success; + } - /// - /// Retrieves job state information from the job object. - /// - /// - /// A handle to the job whose information is being queried. - /// - /// - /// The information class for the limits to be queried. - /// - /// - /// The limit or job state information. - /// - /// - /// The count of the job information being queried, in bytes. - /// - /// - /// A pointer to a variable that receives the length of - /// data written to the structure pointed to by the lpJobObjectInfo parameter. - /// - /// If the function succeeds, the return value is nonzero. - /// If the function fails, the return value is zero. - /// - [DllImport(PinvokeDllNames.QueryInformationJobObjectDllName, EntryPoint = "QueryInformationJobObject", SetLastError = true, CharSet = CharSet.Unicode)] - public static extern bool QueryInformationJobObject(SafeHandle hJob, int JobObjectInfoClass, - ref JOBOBJECT_BASIC_PROCESS_ID_LIST lpJobObjectInfo, - int cbJobObjectLength, IntPtr lpReturnLength); + internal static ServiceStartupType GetServiceStartupType(ServiceStartMode startMode, bool delayedAutoStart) + { + ServiceStartupType result = ServiceStartupType.Disabled; + switch (startMode) + { + case ServiceStartMode.Automatic: + result = delayedAutoStart ? ServiceStartupType.AutomaticDelayedStart : ServiceStartupType.Automatic; + break; + case ServiceStartMode.Manual: + result = ServiceStartupType.Manual; + break; + case ServiceStartMode.Disabled: + result = ServiceStartupType.Disabled; + break; + } + + return result; + } } #endregion NativeMethods + + #region ServiceStartupType + /// + /// Enum for usage with StartupType. Automatic, Manual and Disabled index matched from System.ServiceProcess.ServiceStartMode + /// + public enum ServiceStartupType + { + /// Invalid service + InvalidValue = -1, + /// Automatic service + Automatic = 2, + /// Manual service + Manual = 3, + /// Disabled service + Disabled = 4, + /// Automatic (Delayed Start) service + AutomaticDelayedStart = 10 + } + #endregion ServiceStartupType } #endif // Not built on Unix diff --git a/src/Microsoft.PowerShell.Commands.Management/commands/management/SetClipboardCommand.cs b/src/Microsoft.PowerShell.Commands.Management/commands/management/SetClipboardCommand.cs index a8e917ea73e..49ab5d768f6 100644 --- a/src/Microsoft.PowerShell.Commands.Management/commands/management/SetClipboardCommand.cs +++ b/src/Microsoft.PowerShell.Commands.Management/commands/management/SetClipboardCommand.cs @@ -1,16 +1,13 @@ -using System; -using System.Management.Automation; +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Globalization; -using System.IO; +using System.Management.Automation; using System.Text; -using System.Windows.Forms; -using System.Collections.Specialized; -using System.Linq; -using System.Collections.ObjectModel; -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; -using System.Text.RegularExpressions; +using Microsoft.PowerShell.Commands.Internal; namespace Microsoft.PowerShell.Commands { @@ -18,20 +15,18 @@ namespace Microsoft.PowerShell.Commands /// Defines the implementation of the 'Set-Clipboard' cmdlet. /// This cmdlet gets the content from system clipboard. /// - [Cmdlet(VerbsCommon.Set, "Clipboard", DefaultParameterSetName = "String", SupportsShouldProcess = true, HelpUri = "https://go.microsoft.com/fwlink/?LinkId=526220")] + [Cmdlet(VerbsCommon.Set, "Clipboard", SupportsShouldProcess = true, ConfirmImpact = ConfirmImpact.Medium, HelpUri = "https://go.microsoft.com/fwlink/?LinkId=2109826")] [Alias("scb")] + [OutputType(typeof(string))] public class SetClipboardCommand : PSCmdlet { - private List _contentList = new List(); - private const string ValueParameterSet = "Value"; - private const string PathParameterSet = "Path"; - private const string LiteralPathParameterSet = "LiteralPath"; + private readonly List _contentList = new(); /// /// Property that sets clipboard content. /// - [Parameter(ParameterSetName = ValueParameterSet, Position = 0, Mandatory = true, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true)] - [AllowNull] + [Parameter(Position = 0, Mandatory = true, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true)] + [System.Management.Automation.AllowNull] [AllowEmptyCollection] [AllowEmptyString] [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] @@ -44,40 +39,20 @@ public class SetClipboardCommand : PSCmdlet public SwitchParameter Append { get; set; } /// - /// Property that sets Path parameter. This will allow to set file formats to Clipboard. - /// - [Parameter(ParameterSetName = PathParameterSet, Mandatory = true, ValueFromPipelineByPropertyName = true)] - [ValidateNotNullOrEmpty] - [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] - public string[] Path { get; set; } - - /// - /// Property that sets LiteralPath parameter. This will allow to set file formats to Clipboard. + /// Gets or sets if the values sent down the pipeline. /// - [Parameter(ParameterSetName = LiteralPathParameterSet, Mandatory = true, ValueFromPipelineByPropertyName = true)] - [Alias("PSPath")] - [ValidateNotNullOrEmpty] - [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] - public string[] LiteralPath { get; set; } + [Parameter] + public SwitchParameter PassThru { get; set; } /// - /// Property that sets html parameter. This will allow html content rendered as html to clipboard. + /// Gets or sets whether to use OSC52 escape sequence to set the clipboard of host instead of target. /// [Parameter] - public SwitchParameter AsHtml - { - get { return _asHtml; } - set - { - _isHtmlSet = true; - _asHtml = value; - } - } - private bool _asHtml; - private bool _isHtmlSet = false; + [Alias("ToLocalhost")] + public SwitchParameter AsOSC52 { get; set; } /// - /// This method implements the BeginProcessing method for Set-Clipboard command + /// This method implements the BeginProcessing method for Set-Clipboard command. /// protected override void BeginProcessing() { @@ -85,83 +60,53 @@ protected override void BeginProcessing() } /// - /// This method implements the ProcessRecord method for Set-Clipboard command + /// This method implements the ProcessRecord method for Set-Clipboard command. /// protected override void ProcessRecord() { - // Html should only combine with Text content. - if (Value == null && _isHtmlSet) - { - ThrowTerminatingError(new ErrorRecord(new InvalidOperationException( - String.Format(CultureInfo.InvariantCulture, ClipboardResources.InvalidHtmlCombine)), - "FailedToSetClipboard", ErrorCategory.InvalidOperation, "Clipboard")); - } - if (Value != null) { _contentList.AddRange(Value); - } - else if (Path != null) - { - _contentList.AddRange(Path); - } - else if (LiteralPath != null) - { - _contentList.AddRange(LiteralPath); + + if (PassThru) + { + WriteObject(Value); + } } } /// - /// This method implements the EndProcessing method for Set-Clipboard command + /// This method implements the EndProcessing method for Set-Clipboard command. /// protected override void EndProcessing() { - if (LiteralPath != null) - { - CopyFilesToClipboard(_contentList, Append, true); - } - else if (Path != null) - { - CopyFilesToClipboard(_contentList, Append, false); - } - else - { - SetClipboardContent(_contentList, Append, _asHtml); - } + SetClipboardContent(_contentList, Append); } /// /// Set the clipboard content. /// - /// - /// - /// - private void SetClipboardContent(List contentList, bool append, bool asHtml) + /// The content to store into the clipboard. + /// If true, appends to clipboard instead of overwriting. + private void SetClipboardContent(List contentList, bool append) { string setClipboardShouldProcessTarget; if ((contentList == null || contentList.Count == 0) && !append) { - setClipboardShouldProcessTarget = String.Format(CultureInfo.InvariantCulture, ClipboardResources.ClipboardCleared); + setClipboardShouldProcessTarget = string.Format(CultureInfo.InvariantCulture, ClipboardResources.ClipboardCleared); if (ShouldProcess(setClipboardShouldProcessTarget, "Set-Clipboard")) { - Clipboard.Clear(); + Clipboard.SetText(string.Empty); } + return; } - StringBuilder content = new StringBuilder(); + StringBuilder content = new(); if (append) { - if (!Clipboard.ContainsText()) - { - WriteVerbose(String.Format(CultureInfo.InvariantCulture, ClipboardResources.NoAppendableClipboardContent)); - append = false; - } - else - { - content.AppendLine(Clipboard.GetText()); - } + content.AppendLine(Clipboard.GetText()); } if (contentList != null) @@ -169,7 +114,6 @@ private void SetClipboardContent(List contentList, bool append, bool asH content.Append(string.Join(Environment.NewLine, contentList.ToArray(), 0, contentList.Count)); } - // Verbose output string verboseString = null; if (contentList != null) { @@ -177,244 +121,43 @@ private void SetClipboardContent(List contentList, bool append, bool asH if (verboseString.Length >= 20) { verboseString = verboseString.Substring(0, 20); - verboseString = verboseString + " ..."; + verboseString += " ..."; } } if (append) { - setClipboardShouldProcessTarget = String.Format(CultureInfo.InvariantCulture, ClipboardResources.AppendClipboardContent, verboseString); + setClipboardShouldProcessTarget = string.Format(CultureInfo.InvariantCulture, ClipboardResources.AppendClipboardContent, verboseString); } else { - setClipboardShouldProcessTarget = String.Format(CultureInfo.InvariantCulture, ClipboardResources.SetClipboardContent, verboseString); + setClipboardShouldProcessTarget = string.Format(CultureInfo.InvariantCulture, ClipboardResources.SetClipboardContent, verboseString); } if (ShouldProcess(setClipboardShouldProcessTarget, "Set-Clipboard")) { - // Set the text data - Clipboard.Clear(); - if (asHtml) - Clipboard.SetText(GetHtmlDataString(content.ToString()), TextDataFormat.Html); - else - Clipboard.SetText(content.ToString()); + SetClipboardContent(content.ToString()); } } /// - /// Copy the file format to clipboard. + /// Set the clipboard content. /// - /// - /// - /// - private void CopyFilesToClipboard(List fileList, bool append, bool isLiteralPath) + /// The content to store into the clipboard. + private void SetClipboardContent(string content) { - int clipBoardContentLength = 0; - HashSet dropFiles = new HashSet(StringComparer.OrdinalIgnoreCase); - - // Append the new file list after the file list exists in the clipboard. - if (append) + if (!AsOSC52) { - if (!Clipboard.ContainsFileDropList()) - { - WriteVerbose(String.Format(CultureInfo.InvariantCulture, ClipboardResources.NoAppendableClipboardContent)); - append = false; - } - else - { - StringCollection clipBoardContent = Clipboard.GetFileDropList(); - dropFiles = new HashSet(clipBoardContent.Cast().ToList(), StringComparer.OrdinalIgnoreCase); - - //we need the count of original files so we can get the accurate files number that has been appended. - clipBoardContentLength = clipBoardContent.Count; - } - } - - ProviderInfo provider = null; - for (int i = 0; i < fileList.Count; i++) - { - Collection newPaths = new Collection(); - - try - { - if (isLiteralPath) - { - newPaths.Add(Context.SessionState.Path.GetUnresolvedProviderPathFromPSPath(fileList[i])); - } - else - { - newPaths = Context.SessionState.Path.GetResolvedProviderPathFromPSPath(fileList[i], out provider); - } - } - catch (ItemNotFoundException exception) - { - WriteError(new ErrorRecord(exception, "FailedToSetClipboard", ErrorCategory.InvalidOperation, "Clipboard")); - } - - foreach (string fileName in newPaths) - { - // Avoid adding duplicated files. - if (!dropFiles.Contains(fileName)) - { - dropFiles.Add(fileName); - } - } - } - - if (dropFiles.Count == 0) + Clipboard.SetText(content); return; - - // Verbose output - string setClipboardShouldProcessTarget; - if ((dropFiles.Count - clipBoardContentLength) == 1) - { - if (append) - { - setClipboardShouldProcessTarget = String.Format(CultureInfo.InvariantCulture, ClipboardResources.AppendSingleFileToClipboard, dropFiles.ElementAt(dropFiles.Count - 1)); - } - else - { - setClipboardShouldProcessTarget = String.Format(CultureInfo.InvariantCulture, ClipboardResources.SetSingleFileToClipboard, dropFiles.ElementAt(0)); - } - } - else - { - if (append) - { - setClipboardShouldProcessTarget = String.Format(CultureInfo.InvariantCulture, ClipboardResources.AppendMultipleFilesToClipboard, (dropFiles.Count - clipBoardContentLength)); - } - else - { - setClipboardShouldProcessTarget = String.Format(CultureInfo.InvariantCulture, ClipboardResources.SetMultipleFilesToClipboard, dropFiles.Count); - } - } - - if (ShouldProcess(setClipboardShouldProcessTarget, "Set-Clipboard")) - { - // Set file list formats to clipboard. - Clipboard.Clear(); - StringCollection fileDropList = new StringCollection(); - fileDropList.AddRange(dropFiles.ToArray()); - Clipboard.SetFileDropList(fileDropList); - } - } - - /// - /// Generate HTML fragment data string with header that is required for the clipboard. - /// - /// the html to generate for - /// the resulted string - private static string GetHtmlDataString(string html) - { - // The string contains index references to other spots in the string, so we need placeholders so we can compute the offsets. - // The "<<<<<<<<1,<<<<<<<<2, etc" strings are just placeholders. We'll back-patch them actual values within the header location afterwards. - const string Header = @"Version:0.9 -StartHTML:<<<<<<<<1 -EndHTML:<<<<<<<<2 -StartFragment:<<<<<<<<3 -EndFragment:<<<<<<<<4 -StartSelection:<<<<<<<<3 -EndSelection:<<<<<<<<4"; - - const string StartFragment = ""; - const string EndFragment = @""; - - var sb = new StringBuilder(); - sb.AppendLine(Header); - sb.AppendLine(@""); - - // if given html already provided the fragments we won't add them - int fragmentStart, fragmentEnd; - int fragmentStartIdx = html.IndexOf(StartFragment, StringComparison.OrdinalIgnoreCase); - int fragmentEndIdx = html.LastIndexOf(EndFragment, StringComparison.OrdinalIgnoreCase); - - // if html tag is missing add it surrounding the given html - //find the index of " 0 ? html.IndexOf('>', htmlOpenIdx) + 1 : -1; - //find the index of " 0 ? html.IndexOf('>', bodyOpenIdx) + 1 : -1; - - if (htmlOpenEndIdx < 0 && bodyOpenEndIdx < 0) - { - // the given html doesn't contain html or body tags so we need to add them and place start/end fragments around the given html only - sb.Append(""); - sb.Append(StartFragment); - fragmentStart = GetByteCount(sb); - sb.Append(html); - fragmentEnd = GetByteCount(sb); - sb.Append(EndFragment); - sb.Append(""); - } - else - { - // insert start/end fragments in the proper place (related to html/body tags if exists) so the paste will work correctly - //find the index of ""); - else - sb.Append(html, 0, htmlOpenEndIdx); - - if (bodyOpenEndIdx > -1) - sb.Append(html, htmlOpenEndIdx > -1 ? htmlOpenEndIdx : 0, bodyOpenEndIdx - (htmlOpenEndIdx > -1 ? htmlOpenEndIdx : 0)); - - sb.Append(StartFragment); - fragmentStart = GetByteCount(sb); - - var innerHtmlStart = bodyOpenEndIdx > -1 ? bodyOpenEndIdx : (htmlOpenEndIdx > -1 ? htmlOpenEndIdx : 0); - var innerHtmlEnd = bodyCloseIdx > 0 ? bodyCloseIdx : (htmlCloseIdx > 0 ? htmlCloseIdx : html.Length); - sb.Append(html, innerHtmlStart, innerHtmlEnd - innerHtmlStart); - - fragmentEnd = GetByteCount(sb); - sb.Append(EndFragment); - - if (innerHtmlEnd < html.Length) - sb.Append(html, innerHtmlEnd, html.Length - innerHtmlEnd); - - if (htmlCloseIdx <= 0) - sb.Append(""); - } - } - else - { - // directly return the cf_html - return html; } - // Back-patch offsets, the replace text area is restricted to header only from index 0 to header.Length - sb.Replace("<<<<<<<<1", Header.Length.ToString("D9", CultureInfo.CreateSpecificCulture("en-US")), 0, Header.Length); - sb.Replace("<<<<<<<<2", GetByteCount(sb).ToString("D9", CultureInfo.CreateSpecificCulture("en-US")), 0, Header.Length); - sb.Replace("<<<<<<<<3", fragmentStart.ToString("D9", CultureInfo.CreateSpecificCulture("en-US")), 0, Header.Length); - sb.Replace("<<<<<<<<4", fragmentEnd.ToString("D9", CultureInfo.CreateSpecificCulture("en-US")), 0, Header.Length); - return sb.ToString(); - } + var bytes = System.Text.Encoding.UTF8.GetBytes(content); + var encoded = System.Convert.ToBase64String(bytes); + var osc = $"\u001B]52;;{encoded}\u0007"; - /// - /// Calculates the number of bytes produced by encoding the string in the string builder in UTF-8 and not .NET default string encoding. - /// - /// the string builder to count its string - /// optional: the start index to calculate from (default - start of string) - /// optional: the end index to calculate to (default - end of string) - /// the number of bytes required to encode the string in UTF-8 - private static int GetByteCount(StringBuilder sb, int start = 0, int end = -1) - { - char[] _byteCount = new char[1]; - int count = 0; - end = end > -1 ? end : sb.Length; - for (int i = start; i < end; i++) - { - _byteCount[0] = sb[i]; - count += Encoding.UTF8.GetByteCount(_byteCount); - } - return count; + var message = new HostInformationMessage { Message = osc, NoNewLine = true }; + WriteInformation(message, new string[] { "PSHOST" }); } } } diff --git a/src/Microsoft.PowerShell.Commands.Management/commands/management/SetContentCommand.cs b/src/Microsoft.PowerShell.Commands.Management/commands/management/SetContentCommand.cs index e8bde3599c2..44f4d904d33 100644 --- a/src/Microsoft.PowerShell.Commands.Management/commands/management/SetContentCommand.cs +++ b/src/Microsoft.PowerShell.Commands.Management/commands/management/SetContentCommand.cs @@ -1,18 +1,16 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. using System.Management.Automation; using System.Management.Automation.Internal; -using Dbg = System.Management.Automation; namespace Microsoft.PowerShell.Commands { /// - /// A command to set the content of an item at a specified path + /// A command to set the content of an item at a specified path. /// [Cmdlet(VerbsCommon.Set, "Content", DefaultParameterSetName = "Path", SupportsShouldProcess = true, SupportsTransactions = true, - HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113392")] + HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2097142")] public class SetContentCommand : WriteContentCommandBase { #region protected members @@ -21,20 +19,17 @@ public class SetContentCommand : WriteContentCommandBase /// Called by the base class before the streams are open for the path. /// This override clears the content from the item. /// - /// /// /// The path to the items that will be opened for writing content. /// - /// internal override void BeforeOpenStreams(string[] paths) { - if (paths == null || - (paths != null && paths.Length == 0)) + if (paths == null || paths.Length == 0) { - throw PSTraceSource.NewArgumentNullException("paths"); + throw PSTraceSource.NewArgumentNullException(nameof(paths)); } - CmdletProviderContext context = new CmdletProviderContext(GetCurrentContext()); + CmdletProviderContext context = new(GetCurrentContext()); foreach (string path in paths) { @@ -67,24 +62,21 @@ internal override void BeforeOpenStreams(string[] paths) } catch (ItemNotFoundException) { - //If the item is not found then there is nothing to clear so ignore this exception. + // If the item is not found then there is nothing to clear so ignore this exception. continue; } } - } // BeforeOpenStreams + } /// /// Makes the call to ShouldProcess with appropriate action and target strings. /// - /// /// /// The path to the item on which the content will be set. /// - /// /// /// True if the action should continue or false otherwise. /// - /// internal override bool CallShouldProcess(string path) { string action = NavigationResources.SetContentAction; @@ -94,6 +86,5 @@ internal override bool CallShouldProcess(string path) return ShouldProcess(target, action); } #endregion protected members - } // SetContentCommand -} // namespace Microsoft.PowerShell.Commands - + } +} diff --git a/src/Microsoft.PowerShell.Commands.Management/commands/management/SetPropertyCommand.cs b/src/Microsoft.PowerShell.Commands.Management/commands/management/SetPropertyCommand.cs index fa9cbb78d0d..7c5f43aad2b 100644 --- a/src/Microsoft.PowerShell.Commands.Management/commands/management/SetPropertyCommand.cs +++ b/src/Microsoft.PowerShell.Commands.Management/commands/management/SetPropertyCommand.cs @@ -1,18 +1,15 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. -using System; using System.Management.Automation; -using Dbg = System.Management.Automation; namespace Microsoft.PowerShell.Commands { /// - /// A command to set the property of an item at a specified path + /// A command to set the property of an item at a specified path. /// [Cmdlet(VerbsCommon.Set, "ItemProperty", DefaultParameterSetName = "propertyValuePathSet", SupportsShouldProcess = true, SupportsTransactions = true, - HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113396")] + HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2097147")] public class SetItemPropertyCommand : PassThroughItemPropertyCommandBase { private const string propertyValuePathSet = "propertyValuePathSet"; @@ -23,7 +20,7 @@ public class SetItemPropertyCommand : PassThroughItemPropertyCommandBase #region Parameters /// - /// Gets or sets the path parameter to the command + /// Gets or sets the path parameter to the command. /// [Parameter(Position = 0, ParameterSetName = propertyPSObjectPathSet, Mandatory = true, ValueFromPipelineByPropertyName = true)] @@ -32,20 +29,25 @@ public class SetItemPropertyCommand : PassThroughItemPropertyCommandBase public string[] Path { get { return paths; } + set { paths = value; } } /// - /// Gets or sets the literal path parameter to the command + /// Gets or sets the literal path parameter to the command. /// [Parameter(ParameterSetName = propertyValueLiteralPathSet, Mandatory = true, ValueFromPipeline = false, ValueFromPipelineByPropertyName = true)] [Parameter(ParameterSetName = propertyPSObjectLiteralPathSet, Mandatory = true, ValueFromPipeline = false, ValueFromPipelineByPropertyName = true)] - [Alias("PSPath")] + [Alias("PSPath", "LP")] public string[] LiteralPath { - get { return paths; } + get + { + return paths; + } + set { base.SuppressWildcardExpansion = true; @@ -58,26 +60,22 @@ public string[] LiteralPath /// /// The name of the property to set. /// - /// /// /// This value type is determined by the InvokeProvider. /// - /// [Parameter(Position = 1, ParameterSetName = propertyValuePathSet, Mandatory = true, ValueFromPipelineByPropertyName = true)] [Parameter(Position = 1, ParameterSetName = propertyValueLiteralPathSet, Mandatory = true, ValueFromPipelineByPropertyName = true)] [Alias("PSProperty")] - public string Name { get; set; } = String.Empty; + public string Name { get; set; } = string.Empty; /// /// The value of the property to set. /// - /// /// /// This value type is determined by the InvokeProvider. /// - /// [Parameter(Position = 2, ParameterSetName = propertyValuePathSet, Mandatory = true, ValueFromPipelineByPropertyName = true)] [Parameter(Position = 2, ParameterSetName = propertyValueLiteralPathSet, @@ -87,7 +85,7 @@ public string[] LiteralPath #endregion Property Value set - #region Shell Object set + #region Shell object set /// /// A PSObject that contains the properties and values to be set. @@ -101,23 +99,20 @@ public string[] LiteralPath ValueFromPipeline = true)] public PSObject InputObject { get; set; } - #endregion Shell Object set + #endregion Shell object set /// /// A virtual method for retrieving the dynamic parameters for a cmdlet. Derived cmdlets /// that require dynamic parameters should override this method and return the /// dynamic parameter object. /// - /// /// /// The context under which the command is running. /// - /// /// /// An object representing the dynamic parameters for the cmdlet or null if there /// are none. /// - /// internal override object GetDynamicParameters(CmdletProviderContext context) { PSObject mshObject = null; @@ -126,24 +121,26 @@ internal override object GetDynamicParameters(CmdletProviderContext context) { case propertyValuePathSet: case propertyValueLiteralPathSet: - if (!String.IsNullOrEmpty(Name)) + if (!string.IsNullOrEmpty(Name)) { mshObject = new PSObject(); mshObject.Properties.Add(new PSNoteProperty(Name, Value)); } + break; default: mshObject = InputObject; break; - } // switch + } if (Path != null && Path.Length > 0) { return InvokeProvider.Property.SetPropertyDynamicParameters(Path[0], mshObject, context); } + return InvokeProvider.Property.SetPropertyDynamicParameters(".", mshObject, context); - } // GetDynamicParameters + } #endregion Parameters @@ -154,7 +151,7 @@ internal override object GetDynamicParameters(CmdletProviderContext context) #region Command code /// - /// Sets the content of the item at the specified path + /// Sets the content of the item at the specified path. /// protected override void ProcessRecord() { @@ -183,7 +180,7 @@ protected override void ProcessRecord() false, "One of the parameter sets should have been resolved or an error should have been thrown by the command processor"); break; - } // switch + } foreach (string path in Path) { @@ -224,9 +221,8 @@ protected override void ProcessRecord() continue; } } - } // ProcessRecord + } #endregion Command code - - } // SetItemPropertyCommand -} // namespace Microsoft.PowerShell.Commands + } +} diff --git a/src/Microsoft.PowerShell.Commands.Management/commands/management/SetWMIInstanceCommand.cs b/src/Microsoft.PowerShell.Commands.Management/commands/management/SetWMIInstanceCommand.cs index b507faae72e..4b2deac8bef 100644 --- a/src/Microsoft.PowerShell.Commands.Management/commands/management/SetWMIInstanceCommand.cs +++ b/src/Microsoft.PowerShell.Commands.Management/commands/management/SetWMIInstanceCommand.cs @@ -1,23 +1,22 @@ -// -// Copyright (C) Microsoft. All rights reserved. -// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. using System; -using System.Management.Automation; -using System.Management; -using System.Text; -using System.Management.Automation.Provider; -using System.ComponentModel; using System.Collections; using System.Collections.ObjectModel; -using System.Security.AccessControl; -using System.Runtime.InteropServices; +using System.ComponentModel; using System.Diagnostics.CodeAnalysis; +using System.Management; +using System.Management.Automation; +using System.Management.Automation.Provider; +using System.Runtime.InteropServices; +using System.Security.AccessControl; +using System.Text; namespace Microsoft.PowerShell.Commands { /// - /// A command to Set WMI Instance + /// A command to Set WMI Instance. /// [Cmdlet(VerbsCommon.Set, "WmiInstance", DefaultParameterSetName = "class", SupportsShouldProcess = true, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113402", RemotingCapability = RemotingCapability.OwnedByCommand)] @@ -25,26 +24,25 @@ public sealed class SetWmiInstance : WmiBaseCmdlet { #region Parameters /// - /// The WMI Object to use + /// The WMI Object to use. /// - /// [Parameter(ValueFromPipeline = true, Mandatory = true, ParameterSetName = "object")] public ManagementObject InputObject { get; set; } = null; /// - /// The WMI Path to use + /// The WMI Path to use. /// [Parameter(ParameterSetName = "path", Mandatory = true)] public string Path { get; set; } = null; /// - /// The WMI class to use + /// The WMI class to use. /// [Parameter(Position = 0, Mandatory = true, ParameterSetName = "class")] public string Class { get; set; } = null; /// - /// The property name /value pair + /// The property name /value pair. /// [Parameter(ParameterSetName = "path")] [Parameter(Position = 2, ParameterSetName = "class")] @@ -54,12 +52,13 @@ public sealed class SetWmiInstance : WmiBaseCmdlet public Hashtable Arguments { get; set; } = null; /// - /// The Flag to use + /// The Flag to use. /// [Parameter] public PutType PutType { get { return _putType; } + set { _putType = value; flagSpecified = true; } } @@ -81,6 +80,7 @@ protected override void ProcessRecord() RunAsJob("Set-WMIInstance"); return; } + if (InputObject != null) { object result = null; @@ -96,6 +96,7 @@ protected override void ProcessRecord() { return; } + mObj.Put(pOptions); } else @@ -103,6 +104,7 @@ protected override void ProcessRecord() InvalidOperationException exp = new InvalidOperationException(); throw exp; } + result = mObj; } catch (ManagementException e) @@ -115,14 +117,15 @@ protected override void ProcessRecord() ErrorRecord errorRecord = new ErrorRecord(e, "SetWMICOMException", ErrorCategory.InvalidOperation, null); WriteError(errorRecord); } + WriteObject(result); } else { ManagementPath mPath = null; - //If Class is specified only CreateOnly flag is supported + // If Class is specified only CreateOnly flag is supported mPath = this.SetWmiInstanceBuildManagementPath(); - //If server name is specified loop through it. + // If server name is specified loop through it. if (mPath != null) { if (!(mPath.Server == "." && serverNameSpecified)) @@ -131,6 +134,7 @@ protected override void ProcessRecord() ComputerName = serverName; } } + ConnectionOptions options = GetConnectionOption(); object result = null; ManagementObject mObject = null; @@ -148,6 +152,7 @@ protected override void ProcessRecord() { continue; } + mObject.Put(pOptions); } else @@ -155,6 +160,7 @@ protected override void ProcessRecord() InvalidOperationException exp = new InvalidOperationException(); throw exp; } + result = mObject; } catch (ManagementException e) @@ -167,6 +173,7 @@ protected override void ProcessRecord() ErrorRecord errorRecord = new ErrorRecord(e, "SetWMICOMException", ErrorCategory.InvalidOperation, null); WriteError(errorRecord); } + if (result != null) { WriteObject(result); diff --git a/src/Microsoft.PowerShell.Commands.Management/commands/management/StartTransactionCommand.cs b/src/Microsoft.PowerShell.Commands.Management/commands/management/StartTransactionCommand.cs index e758380ce5e..58dcfbd1daa 100644 --- a/src/Microsoft.PowerShell.Commands.Management/commands/management/StartTransactionCommand.cs +++ b/src/Microsoft.PowerShell.Commands.Management/commands/management/StartTransactionCommand.cs @@ -1,9 +1,9 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. using System; using System.Management.Automation; + using Dbg = System.Management.Automation; namespace Microsoft.PowerShell.Commands @@ -18,7 +18,7 @@ public class StartTransactionCommand : PSCmdlet /// The time, in minutes, before this transaction is rolled back /// automatically. /// - [Parameter()] + [Parameter] [Alias("TimeoutMins")] public int Timeout { @@ -26,6 +26,7 @@ public int Timeout { return (int)_timeout.TotalMinutes; } + set { // The transactions constructor treats a timeout of @@ -38,6 +39,7 @@ public int Timeout _timeoutSpecified = true; } } + private bool _timeoutSpecified = false; private TimeSpan _timeout = TimeSpan.MinValue; @@ -45,23 +47,27 @@ public int Timeout /// Gets or sets the flag to determine if this transaction can /// be committed or rolled back independently of other transactions. /// - [Parameter()] + [Parameter] public SwitchParameter Independent { get { return _independent; } + set { _independent = value; } } + private SwitchParameter _independent; /// /// Gets or sets the rollback preference for this transaction. /// - [Parameter()] + [Parameter] public RollbackSeverity RollbackPreference { get { return _rollbackPreference; } + set { _rollbackPreference = value; } } + private RollbackSeverity _rollbackPreference = RollbackSeverity.Error; /// @@ -99,6 +105,5 @@ protected override void EndProcessing() } } } - } // StartTransactionCommand -} // namespace Microsoft.PowerShell.Commands - + } +} diff --git a/src/Microsoft.PowerShell.Commands.Management/commands/management/TestConnectionCommand.cs b/src/Microsoft.PowerShell.Commands.Management/commands/management/TestConnectionCommand.cs new file mode 100644 index 00000000000..2a4a453f8f7 --- /dev/null +++ b/src/Microsoft.PowerShell.Commands.Management/commands/management/TestConnectionCommand.cs @@ -0,0 +1,1254 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#nullable enable + +using System; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Management.Automation; +using System.Management.Automation.Internal; +using System.Net; +using System.Net.NetworkInformation; +using System.Net.Sockets; +using System.Threading; +using System.Threading.Tasks; + +namespace Microsoft.PowerShell.Commands +{ + /// + /// The implementation of the "Test-Connection" cmdlet. + /// + [Cmdlet(VerbsDiagnostic.Test, "Connection", DefaultParameterSetName = DefaultPingParameterSet, + HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2097144")] + [OutputType(typeof(PingStatus), ParameterSetName = new string[] { DefaultPingParameterSet })] + [OutputType(typeof(PingStatus), ParameterSetName = new string[] { RepeatPingParameterSet, MtuSizeDetectParameterSet })] + [OutputType(typeof(bool), ParameterSetName = new string[] { DefaultPingParameterSet, RepeatPingParameterSet, TcpPortParameterSet })] + [OutputType(typeof(PingMtuStatus), ParameterSetName = new string[] { MtuSizeDetectParameterSet })] + [OutputType(typeof(int), ParameterSetName = new string[] { MtuSizeDetectParameterSet })] + [OutputType(typeof(TraceStatus), ParameterSetName = new string[] { TraceRouteParameterSet })] + [OutputType(typeof(TcpPortStatus), ParameterSetName = new string[] { TcpPortParameterSet })] + public class TestConnectionCommand : PSCmdlet, IDisposable + { + #region Parameter Set Names + private const string DefaultPingParameterSet = "DefaultPing"; + private const string RepeatPingParameterSet = "RepeatPing"; + private const string TraceRouteParameterSet = "TraceRoute"; + private const string TcpPortParameterSet = "TcpPort"; + private const string MtuSizeDetectParameterSet = "MtuSizeDetect"; + + #endregion + + #region Cmdlet Defaults + + // Count of pings sent to each trace route hop. Default mimics Windows' defaults. + // If this value changes, we need to update 'ConsoleTraceRouteReply' resource string. + private const uint DefaultTraceRoutePingCount = 3; + + // Default size for the send buffer. + private const int DefaultSendBufferSize = 32; + + private const int DefaultMaxHops = 128; + + private const string TestConnectionExceptionId = "TestConnectionException"; + + #endregion + + #region Private Fields + + private static readonly byte[] s_DefaultSendBuffer = Array.Empty(); + + private readonly CancellationTokenSource _dnsLookupCancel = new(); + + private bool _disposed; + + private Ping? _sender; + + #endregion + + #region Parameters + + /// + /// Gets or sets whether to do ping test. + /// Default is true. + /// + [Parameter(ParameterSetName = DefaultPingParameterSet)] + [Parameter(ParameterSetName = RepeatPingParameterSet)] + public SwitchParameter Ping { get; set; } = true; + + /// + /// Gets or sets whether to force use of IPv4 protocol. + /// + [Parameter(ParameterSetName = DefaultPingParameterSet)] + [Parameter(ParameterSetName = RepeatPingParameterSet)] + [Parameter(ParameterSetName = TraceRouteParameterSet)] + [Parameter(ParameterSetName = MtuSizeDetectParameterSet)] + [Parameter(ParameterSetName = TcpPortParameterSet)] + public SwitchParameter IPv4 { get; set; } + + /// + /// Gets or sets whether to force use of IPv6 protocol. + /// + [Parameter(ParameterSetName = DefaultPingParameterSet)] + [Parameter(ParameterSetName = RepeatPingParameterSet)] + [Parameter(ParameterSetName = TraceRouteParameterSet)] + [Parameter(ParameterSetName = MtuSizeDetectParameterSet)] + [Parameter(ParameterSetName = TcpPortParameterSet)] + public SwitchParameter IPv6 { get; set; } + + /// + /// Gets or sets whether to do reverse DNS lookup to get names for IP addresses. + /// + [Parameter(ParameterSetName = DefaultPingParameterSet)] + [Parameter(ParameterSetName = RepeatPingParameterSet)] + [Parameter(ParameterSetName = TraceRouteParameterSet)] + [Parameter(ParameterSetName = MtuSizeDetectParameterSet)] + [Parameter(ParameterSetName = TcpPortParameterSet)] + public SwitchParameter ResolveDestination { get; set; } + + /// + /// Gets the source from which to run the selected test. + /// The default is localhost. + /// Remoting is not yet implemented internally in the cmdlet. + /// + [Parameter(ParameterSetName = DefaultPingParameterSet)] + [Parameter(ParameterSetName = RepeatPingParameterSet)] + [Parameter(ParameterSetName = TraceRouteParameterSet)] + [Parameter(ParameterSetName = TcpPortParameterSet)] + public string Source { get; } = Dns.GetHostName(); + + /// + /// Gets or sets the number of times the Ping data packets can be forwarded by routers. + /// As gateways and routers transmit packets through a network, they decrement the Time-to-Live (TTL) + /// value found in the packet header. + /// The default (from Windows) is 128 hops. + /// + [Parameter(ParameterSetName = DefaultPingParameterSet)] + [Parameter(ParameterSetName = RepeatPingParameterSet)] + [Parameter(ParameterSetName = TraceRouteParameterSet)] + [ValidateRange(1, DefaultMaxHops)] + [Alias("Ttl", "TimeToLive", "Hops")] + public int MaxHops { get; set; } = DefaultMaxHops; + + /// + /// Gets or sets the number of ping attempts. + /// The default (from Windows) is 4 times. + /// + [Parameter(ParameterSetName = DefaultPingParameterSet)] + [Parameter(ParameterSetName = TcpPortParameterSet)] + [ValidateRange(ValidateRangeKind.Positive)] + public int Count { get; set; } = 4; + + /// + /// Gets or sets the delay between ping attempts. + /// The default (from Windows) is 1 second. + /// + [Parameter(ParameterSetName = DefaultPingParameterSet)] + [Parameter(ParameterSetName = RepeatPingParameterSet)] + [Parameter(ParameterSetName = TcpPortParameterSet)] + [ValidateRange(ValidateRangeKind.Positive)] + public int Delay { get; set; } = 1; + + /// + /// Gets or sets the buffer size to send with the ping packet. + /// The default (from Windows) is 32 bytes. + /// Max value is 65500 (limitation imposed by Windows API). + /// + [Parameter(ParameterSetName = DefaultPingParameterSet)] + [Parameter(ParameterSetName = RepeatPingParameterSet)] + [Alias("Size", "Bytes", "BS")] + [ValidateRange(0, 65500)] + public int BufferSize { get; set; } = DefaultSendBufferSize; + + /// + /// Gets or sets whether to prevent fragmentation of the ICMP packets. + /// Currently CoreFX not supports this on Unix. + /// + [Parameter(ParameterSetName = DefaultPingParameterSet)] + [Parameter(ParameterSetName = RepeatPingParameterSet)] + public SwitchParameter DontFragment { get; set; } + + /// + /// Gets or sets whether to continue pinging until user presses Ctrl-C (or Int.MaxValue threshold reached). + /// + [Parameter(Mandatory = true, ParameterSetName = RepeatPingParameterSet)] + [Parameter(ParameterSetName = TcpPortParameterSet)] + [Alias("Continuous")] + public SwitchParameter Repeat { get; set; } + + /// + /// Gets or sets whether to enable quiet output mode, reducing output to a single simple value only. + /// By default, PingStatus, PingMtuStatus, or TraceStatus objects are emitted. + /// With this switch, standard ping and -Traceroute returns only true / false, and -MtuSize returns an integer. + /// + [Parameter] + public SwitchParameter Quiet { get; set; } + + /// + /// Gets or sets whether to enable detailed output mode while running a TCP connection test. + /// Without this flag, the TCP test will return a boolean result. + /// + [Parameter(ParameterSetName = TcpPortParameterSet)] + public SwitchParameter Detailed; + + /// + /// Gets or sets the timeout value for an individual ping in seconds. + /// If a response is not received in this time, no response is assumed. + /// The default (from Windows) is 5 seconds. + /// + [Parameter] + [ValidateRange(ValidateRangeKind.Positive)] + public int TimeoutSeconds { get; set; } = 5; + + /// + /// Gets or sets the destination hostname or IP address. + /// + [Parameter( + Mandatory = true, + Position = 0, + ValueFromPipeline = true, + ValueFromPipelineByPropertyName = true)] + [ValidateNotNullOrEmpty] + [Alias("ComputerName")] + public string[]? TargetName { get; set; } + + /// + /// Gets or sets whether to detect Maximum Transmission Unit size. + /// When selected, only a single ping result is returned, indicating the maximum buffer size + /// the route to the destination can support without fragmenting the ICMP packets. + /// + [Parameter(Mandatory = true, ParameterSetName = MtuSizeDetectParameterSet)] + [Alias("MtuSizeDetect")] + public SwitchParameter MtuSize { get; set; } + + /// + /// Gets or sets whether to perform a traceroute test. + /// + [Parameter(Mandatory = true, ParameterSetName = TraceRouteParameterSet)] + public SwitchParameter Traceroute { get; set; } + + /// + /// Gets or sets whether to perform a TCP connection test. + /// + [ValidateRange(0, 65535)] + [Parameter(Mandatory = true, ParameterSetName = TcpPortParameterSet)] + public int TcpPort { get; set; } + + #endregion Parameters + + /// + /// BeginProcessing implementation for TestConnectionCommand. + /// Sets Count for different types of tests unless specified explicitly. + /// + protected override void BeginProcessing() + { + if (Repeat) + { + Count = int.MaxValue; + } + else if (ParameterSetName == TcpPortParameterSet) + { + SetCountForTcpTest(); + } + } + + /// + /// Process a connection test. + /// + protected override void ProcessRecord() + { + if (TargetName == null) + { + return; + } + + foreach (var targetName in TargetName) + { + if (MtuSize) + { + ProcessMTUSize(targetName); + } + else if (Traceroute) + { + ProcessTraceroute(targetName); + } + else if (ParameterSetName == TcpPortParameterSet) + { + ProcessConnectionByTCPPort(targetName); + } + else + { + // None of the switch parameters are true: handle default ping or -Repeat + ProcessPing(targetName); + } + } + } + + /// + /// On receiving the StopProcessing() request, the cmdlet will immediately cancel any in-progress ping request. + /// This allows a cancellation to occur during a ping request without having to wait for the timeout. + /// + protected override void StopProcessing() + { + _sender?.SendAsyncCancel(); + _dnsLookupCancel.Cancel(); + } + + #region ConnectionTest + + private void SetCountForTcpTest() + { + if (Repeat) + { + Count = int.MaxValue; + } + else if (!MyInvocation.BoundParameters.ContainsKey(nameof(Count))) + { + Count = 1; + } + } + + private void ProcessConnectionByTCPPort(string targetNameOrAddress) + { + if (!TryResolveNameOrAddress(targetNameOrAddress, out _, out IPAddress? targetAddress)) + { + if (Quiet.IsPresent) + { + WriteObject(false); + } + + return; + } + + int timeoutMilliseconds = TimeoutSeconds * 1000; + int delayMilliseconds = Delay * 1000; + + for (var i = 1; i <= Count; i++) + { + long latency = 0; + SocketError status = SocketError.SocketError; + + Stopwatch stopwatch = new Stopwatch(); + + using var client = new TcpClient(); + + try + { + stopwatch.Start(); + + if (client.ConnectAsync(targetAddress, TcpPort).Wait(timeoutMilliseconds, _dnsLookupCancel.Token)) + { + latency = stopwatch.ElapsedMilliseconds; + status = SocketError.Success; + } + else + { + status = SocketError.TimedOut; + } + } + catch (AggregateException ae) + { + ae.Handle((ex) => + { + if (ex is TaskCanceledException) + { + throw new PipelineStoppedException(); + } + if (ex is SocketException socketException) + { + status = socketException.SocketErrorCode; + return true; + } + else + { + return false; + } + }); + } + finally + { + stopwatch.Reset(); + } + + if (!Detailed.IsPresent) + { + WriteObject(status == SocketError.Success); + return; + } + else + { + WriteObject(new TcpPortStatus( + i, + Source, + targetNameOrAddress, + targetAddress, + TcpPort, + latency, + status == SocketError.Success, + status + )); + } + + if (i < Count) + { + Task.Delay(delayMilliseconds).Wait(_dnsLookupCancel.Token); + } + } + } + + #endregion ConnectionTest + + #region TracerouteTest + + private void ProcessTraceroute(string targetNameOrAddress) + { + byte[] buffer = GetSendBuffer(BufferSize); + + if (!TryResolveNameOrAddress(targetNameOrAddress, out string resolvedTargetName, out IPAddress? targetAddress)) + { + if (!Quiet.IsPresent) + { + WriteObject(false); + } + + return; + } + + int currentHop = 1; + PingOptions pingOptions = new(currentHop, DontFragment.IsPresent); + PingReply reply; + PingReply discoveryReply; + int timeout = TimeoutSeconds * 1000; + Stopwatch timer = new(); + + IPAddress hopAddress; + do + { + pingOptions.Ttl = currentHop; + +#if !UNIX + // Get intermediate hop target. This needs to be done first, so that we can target it properly + // and get useful responses. + var discoveryAttempts = 0; + bool addressIsValid = false; + do + { + discoveryReply = SendCancellablePing(targetAddress, timeout, buffer, pingOptions); + discoveryAttempts++; + addressIsValid = !(discoveryReply.Address.Equals(IPAddress.Any) + || discoveryReply.Address.Equals(IPAddress.IPv6Any)); + } + while (discoveryAttempts <= DefaultTraceRoutePingCount && addressIsValid); + + // If we aren't able to get a valid address, just re-target the final destination of the trace. + hopAddress = addressIsValid ? discoveryReply.Address : targetAddress; +#else + // Unix Ping API returns nonsense "TimedOut" for ALL intermediate hops. No way around this + // issue for traceroutes as we rely on information (intermediate addresses, etc.) that is + // simply not returned to us by the API. + // The only supported states on Unix seem to be Success and TimedOut. Workaround is to + // keep targeting the final address; at the very least we will be able to tell the user + // the required number of hops to reach the destination. + hopAddress = targetAddress; + discoveryReply = SendCancellablePing(targetAddress, timeout, buffer, pingOptions); +#endif + var hopAddressString = discoveryReply.Address.ToString(); + + string routerName = hopAddressString; + try + { + if (!TryResolveNameOrAddress(hopAddressString, out routerName, out _)) + { + routerName = hopAddressString; + } + } + catch + { + // Swallow hostname resolve exceptions and continue with traceroute + } + + // In traceroutes we don't use 'Count' parameter. + // If we change 'DefaultTraceRoutePingCount' we should change 'ConsoleTraceRouteReply' resource string. + for (uint i = 1; i <= DefaultTraceRoutePingCount; i++) + { + try + { + reply = SendCancellablePing(hopAddress, timeout, buffer, pingOptions, timer); + + if (!Quiet.IsPresent) + { + var status = new PingStatus( + Source, + routerName, + reply, + reply.Status == IPStatus.Success + ? reply.RoundtripTime + : timer.ElapsedMilliseconds, + + // If we use the empty buffer, then .NET actually uses a 32 byte buffer so we want to show + // as the result object the actual buffer size used instead of 0. + buffer.Length == 0 ? DefaultSendBufferSize : buffer.Length, + pingNum: i); + WriteObject(new TraceStatus( + currentHop, + status, + Source, + resolvedTargetName, + targetAddress)); + } + } + catch (PingException ex) + { + string message = StringUtil.Format( + TestConnectionResources.NoPingResult, + resolvedTargetName, + ex.Message); + Exception pingException = new PingException(message, ex.InnerException); + ErrorRecord errorRecord = new( + pingException, + TestConnectionExceptionId, + ErrorCategory.ResourceUnavailable, + resolvedTargetName); + WriteError(errorRecord); + + continue; + } + + // We use short delay because it is impossible DoS with trace route. + Thread.Sleep(50); + timer.Reset(); + } + + currentHop++; + } while (currentHop <= MaxHops + && (discoveryReply.Status == IPStatus.TtlExpired + || discoveryReply.Status == IPStatus.TimedOut)); + + if (Quiet.IsPresent) + { + WriteObject(currentHop <= MaxHops); + } + else if (currentHop > MaxHops) + { + var message = StringUtil.Format( + TestConnectionResources.MaxHopsExceeded, + resolvedTargetName, + MaxHops); + var pingException = new PingException(message); + WriteError(new ErrorRecord( + pingException, + TestConnectionExceptionId, + ErrorCategory.ConnectionError, + targetAddress)); + } + } + + #endregion TracerouteTest + + #region MTUSizeTest + private void ProcessMTUSize(string targetNameOrAddress) + { + PingReply? reply, replyResult = null; + if (!TryResolveNameOrAddress(targetNameOrAddress, out string resolvedTargetName, out IPAddress? targetAddress)) + { + if (Quiet.IsPresent) + { + WriteObject(-1); + } + + return; + } + + // Caution! Algorithm is sensitive to changing boundary values. + int HighMTUSize = 10000; + int CurrentMTUSize = 1473; + int LowMTUSize = targetAddress.AddressFamily == AddressFamily.InterNetworkV6 ? 1280 : 68; + int timeout = TimeoutSeconds * 1000; + + PingReply? timeoutReply = null; + + try + { + PingOptions pingOptions = new(MaxHops, true); + int retry = 1; + + while (LowMTUSize < (HighMTUSize - 1)) + { + byte[] buffer = GetSendBuffer(CurrentMTUSize); + + WriteDebug(StringUtil.Format( + "LowMTUSize: {0}, CurrentMTUSize: {1}, HighMTUSize: {2}", + LowMTUSize, + CurrentMTUSize, + HighMTUSize)); + + reply = SendCancellablePing(targetAddress, timeout, buffer, pingOptions); + + if (reply.Status == IPStatus.PacketTooBig || reply.Status == IPStatus.TimedOut) + { + HighMTUSize = CurrentMTUSize; + timeoutReply = reply; + retry = 1; + } + else if (reply.Status == IPStatus.Success) + { + LowMTUSize = CurrentMTUSize; + replyResult = reply; + retry = 1; + } + else + { + // If the host didn't reply, try again up to the 'Count' value. + if (retry >= Count) + { + string message = StringUtil.Format( + TestConnectionResources.NoPingResult, + targetAddress, + reply.Status.ToString()); + Exception pingException = new PingException(message); + ErrorRecord errorRecord = new( + pingException, + TestConnectionExceptionId, + ErrorCategory.ResourceUnavailable, + targetAddress); + WriteError(errorRecord); + return; + } + else + { + retry++; + continue; + } + } + + CurrentMTUSize = (LowMTUSize + HighMTUSize) / 2; + + // Prevent DoS attack. + Thread.Sleep(100); + } + } + catch (PingException ex) + { + string message = StringUtil.Format(TestConnectionResources.NoPingResult, targetAddress, ex.Message); + Exception pingException = new PingException(message, ex.InnerException); + ErrorRecord errorRecord = new( + pingException, + TestConnectionExceptionId, + ErrorCategory.ResourceUnavailable, + targetAddress); + WriteError(errorRecord); + return; + } + + if (Quiet.IsPresent) + { + WriteObject(CurrentMTUSize); + } + else + { + if (replyResult is null) + { + if (timeoutReply is not null) + { + Exception timeoutException = new TimeoutException(targetAddress.ToString()); + ErrorRecord errorRecord = new( + timeoutException, + TestConnectionExceptionId, + ErrorCategory.ResourceUnavailable, + timeoutReply); + WriteError(errorRecord); + } + else + { + ArgumentNullException.ThrowIfNull(replyResult); + } + } + else + { + WriteObject(new PingMtuStatus( + Source, + resolvedTargetName, + replyResult, + CurrentMTUSize)); + } + + } + } + + #endregion MTUSizeTest + + #region PingTest + + private void ProcessPing(string targetNameOrAddress) + { + if (!TryResolveNameOrAddress(targetNameOrAddress, out string resolvedTargetName, out IPAddress? targetAddress)) + { + if (Quiet.IsPresent) + { + WriteObject(false); + } + + return; + } + + bool quietResult = true; + byte[] buffer = GetSendBuffer(BufferSize); + + PingReply reply; + PingOptions pingOptions = new(MaxHops, DontFragment.IsPresent); + int timeout = TimeoutSeconds * 1000; + int delay = Delay * 1000; + + for (int i = 1; i <= Count; i++) + { + try + { + reply = SendCancellablePing(targetAddress, timeout, buffer, pingOptions); + } + catch (PingException ex) + { + string message = StringUtil.Format(TestConnectionResources.NoPingResult, resolvedTargetName, ex.Message); + Exception pingException = new PingException(message, ex.InnerException); + ErrorRecord errorRecord = new( + pingException, + TestConnectionExceptionId, + ErrorCategory.ResourceUnavailable, + resolvedTargetName); + WriteError(errorRecord); + + quietResult = false; + continue; + } + + if (Quiet.IsPresent) + { + // Return 'true' only if all pings have completed successfully. + quietResult &= reply.Status == IPStatus.Success; + } + else + { + WriteObject(new PingStatus( + Source, + resolvedTargetName, + reply, + reply.RoundtripTime, + buffer.Length == 0 ? DefaultSendBufferSize : buffer.Length, + pingNum: (uint)i)); + } + + // Delay between pings, but not after last ping. + if (i < Count && Delay > 0) + { + Thread.Sleep(delay); + } + } + + if (Quiet.IsPresent) + { + WriteObject(quietResult); + } + } + + #endregion PingTest + + private bool TryResolveNameOrAddress( + string targetNameOrAddress, + out string resolvedTargetName, + [NotNullWhen(true)] + out IPAddress? targetAddress) + { + resolvedTargetName = targetNameOrAddress; + + IPHostEntry hostEntry; + if (IPAddress.TryParse(targetNameOrAddress, out targetAddress)) + { + if ((IPv4 && targetAddress.AddressFamily != AddressFamily.InterNetwork) + || (IPv6 && targetAddress.AddressFamily != AddressFamily.InterNetworkV6)) + { + string message = StringUtil.Format( + TestConnectionResources.NoPingResult, + resolvedTargetName, + TestConnectionResources.TargetAddressAbsent); + Exception pingException = new PingException(message, null); + ErrorRecord errorRecord = new( + pingException, + TestConnectionExceptionId, + ErrorCategory.ResourceUnavailable, + resolvedTargetName); + WriteError(errorRecord); + return false; + } + + if (ResolveDestination) + { + hostEntry = GetCancellableHostEntry(targetNameOrAddress); + resolvedTargetName = hostEntry.HostName; + } + else + { + resolvedTargetName = targetAddress.ToString(); + } + } + else + { + try + { + hostEntry = GetCancellableHostEntry(targetNameOrAddress); + + if (ResolveDestination) + { + resolvedTargetName = hostEntry.HostName; + hostEntry = GetCancellableHostEntry(hostEntry.HostName); + } + } + catch (PipelineStoppedException) + { + throw; + } + catch (Exception ex) + { + if (!Quiet.IsPresent) + { + string message = StringUtil.Format( + TestConnectionResources.NoPingResult, + resolvedTargetName, + TestConnectionResources.CannotResolveTargetName); + Exception pingException = new PingException(message, ex); + ErrorRecord errorRecord = new( + pingException, + TestConnectionExceptionId, + ErrorCategory.ResourceUnavailable, + resolvedTargetName); + WriteError(errorRecord); + } + + return false; + } + + if (IPv6 || IPv4) + { + targetAddress = GetHostAddress(hostEntry); + + if (targetAddress == null) + { + string message = StringUtil.Format( + TestConnectionResources.NoPingResult, + resolvedTargetName, + TestConnectionResources.TargetAddressAbsent); + Exception pingException = new PingException(message, null); + ErrorRecord errorRecord = new( + pingException, + TestConnectionExceptionId, + ErrorCategory.ResourceUnavailable, + resolvedTargetName); + WriteError(errorRecord); + return false; + } + } + else + { + targetAddress = hostEntry.AddressList[0]; + } + } + + return true; + } + + private IPHostEntry GetCancellableHostEntry(string targetNameOrAddress) + { + var task = Dns.GetHostEntryAsync(targetNameOrAddress); + var waitHandles = new[] { ((IAsyncResult)task).AsyncWaitHandle, _dnsLookupCancel.Token.WaitHandle }; + + // WaitAny() returns the index of the first signal it gets; 1 is our cancellation token. + if (WaitHandle.WaitAny(waitHandles) == 1) + { + throw new PipelineStoppedException(); + } + + return task.GetAwaiter().GetResult(); + } + + private IPAddress? GetHostAddress(IPHostEntry hostEntry) + { + AddressFamily addressFamily = IPv6 ? AddressFamily.InterNetworkV6 : AddressFamily.InterNetwork; + + foreach (var address in hostEntry.AddressList) + { + if (address.AddressFamily == addressFamily) + { + return address; + } + } + + return null; + } + + // Users most often use the default buffer size so we cache the buffer. + // Creates and fills a send buffer. This follows the ping.exe and CoreFX model. + private static byte[] GetSendBuffer(int bufferSize) + { + if (bufferSize == DefaultSendBufferSize) + { + return s_DefaultSendBuffer; + } + + byte[] sendBuffer = new byte[bufferSize]; + + for (int i = 0; i < bufferSize; i++) + { + sendBuffer[i] = (byte)((int)'a' + i % 23); + } + + return sendBuffer; + } + + /// + /// IDisposable implementation, dispose of any disposable resources created by the cmdlet. + /// + public void Dispose() + { + Dispose(disposing: true); + GC.SuppressFinalize(this); + } + + /// + /// Implementation of IDisposable for both manual Dispose() and finalizer-called disposal of resources. + /// + /// + /// Specified as true when Dispose() was called, false if this is called from the finalizer. + /// + protected virtual void Dispose(bool disposing) + { + if (!_disposed) + { + if (disposing) + { + _sender?.Dispose(); + _dnsLookupCancel.Dispose(); + } + + _disposed = true; + } + } + + // Uses the SendAsync() method to send pings, so that Ctrl+C can halt the request early if needed. + private PingReply SendCancellablePing( + IPAddress targetAddress, + int timeout, + byte[] buffer, + PingOptions pingOptions, + Stopwatch? timer = null) + { + try + { + _sender = new Ping(); + + timer?.Start(); + // 'SendPingAsync' always uses the default synchronization context (threadpool). + // This is what we want to avoid the deadlock resulted by async work being scheduled back to the + // pipeline thread due to a change of the current synchronization context of the pipeline thread. + return _sender.SendPingAsync(targetAddress, timeout, buffer, pingOptions).GetAwaiter().GetResult(); + } + catch (PingException ex) when (ex.InnerException is TaskCanceledException) + { + // The only cancellation we have implemented is on pipeline stops via StopProcessing(). + throw new PipelineStoppedException(); + } + finally + { + timer?.Stop(); + _sender?.Dispose(); + _sender = null; + } + } + + /// + /// The class contains information about the TCP connection test. + /// + public class TcpPortStatus + { + /// + /// Initializes a new instance of the class. + /// + /// The number of this test. + /// The source machine name or IP of the test. + /// The target machine name or IP of the test. + /// The resolved IP from the target. + /// The port used for the connection. + /// The latency of the test. + /// If the test connection succeeded. + /// Status of the underlying socket. + internal TcpPortStatus(int id, string source, string target, IPAddress targetAddress, int port, long latency, bool connected, SocketError status) + { + Id = id; + Source = source; + Target = target; + TargetAddress = targetAddress; + Port = port; + Latency = latency; + Connected = connected; + Status = status; + } + + /// + /// Gets and sets the count of the test. + /// + public int Id { get; set; } + + /// + /// Gets the source from which the test was sent. + /// + public string Source { get; } + + /// + /// Gets the target name. + /// + public string Target { get; } + + /// + /// Gets the resolved address for the target. + /// + public IPAddress TargetAddress { get; } + + /// + /// Gets the port used for the test. + /// + public int Port { get; } + + /// + /// Gets or sets the latancy of the connection. + /// + public long Latency { get; set; } + + /// + /// Gets or sets the result of the test. + /// + public bool Connected { get; set; } + + /// + /// Gets or sets the state of the socket after the test. + /// + public SocketError Status { get; set; } + } + + /// + /// The class contains information about the source, the destination and ping results. + /// + public class PingStatus + { + /// + /// Initializes a new instance of the class. + /// This constructor allows manually specifying the initial values for the cases where the PingReply + /// object may be missing some information, specifically in the instances where PingReply objects are + /// utilised to perform a traceroute. + /// + /// The source machine name or IP of the ping. + /// The destination machine name of the ping. + /// The response from the ping attempt. + /// The latency of the ping. + /// The buffer size. + /// The sequence number in the sequence of pings to the hop point. + internal PingStatus( + string source, + string destination, + PingReply reply, + long latency, + int bufferSize, + uint pingNum) + : this(source, destination, reply, pingNum) + { + _bufferSize = bufferSize; + _latency = latency; + } + + /// + /// Initializes a new instance of the class. + /// + /// The source machine name or IP of the ping. + /// The destination machine name of the ping. + /// The response from the ping attempt. + /// The sequence number of the ping in the sequence of pings to the target. + internal PingStatus(string source, string destination, PingReply reply, uint pingNum) + { + Ping = pingNum; + Reply = reply; + Source = source; + Destination = destination; + } + + // These values can be set manually to skirt issues with the Ping API on Unix platforms + // so that we can return meaningful known data that is discarded by the API. + private readonly int _bufferSize = -1; + + private readonly long _latency = -1; + + /// + /// Gets the sequence number of this ping in the sequence of pings to the + /// + public uint Ping { get; } + + /// + /// Gets the source from which the ping was sent. + /// + public string Source { get; } + + /// + /// Gets the destination which was pinged. + /// + public string Destination { get; } + + /// + /// Gets the target address of the ping. + /// + public IPAddress? Address { get => Reply.Status == IPStatus.Success ? Reply.Address : null; } + + /// + /// Gets the target address of the ping if one is available, or "*" if it is not. + /// + public string DisplayAddress { get => Address?.ToString() ?? "*"; } + + /// + /// Gets the roundtrip time of the ping in milliseconds. + /// + public long Latency { get => _latency >= 0 ? _latency : Reply.RoundtripTime; } + + /// + /// Gets the returned status of the ping. + /// + public IPStatus Status { get => Reply.Status; } + + /// + /// Gets the size in bytes of the buffer data sent in the ping. + /// + public int BufferSize { get => _bufferSize >= 0 ? _bufferSize : Reply.Buffer.Length; } + + /// + /// Gets the reply object from this ping. + /// + public PingReply Reply { get; } + } + + /// + /// The class contains information about the source, the destination and ping results. + /// + public class PingMtuStatus : PingStatus + { + /// + /// Initializes a new instance of the class. + /// + /// The source machine name or IP of the ping. + /// The destination machine name of the ping. + /// The response from the ping attempt. + /// The buffer size from the successful ping attempt. + internal PingMtuStatus(string source, string destination, PingReply reply, int bufferSize) + : base(source, destination, reply, 1) + { + MtuSize = bufferSize; + } + + /// + /// Gets the maximum transmission unit size on the network path between the source and destination. + /// + public int MtuSize { get; } + } + + /// + /// The class contains an information about a trace route attempt. + /// + public class TraceStatus + { + /// + /// Initializes a new instance of the class. + /// + /// The hop number of this trace hop. + /// The PingStatus response from this trace hop. + /// The source computer name or IP address of the traceroute. + /// The target destination of the traceroute. + /// The target IPAddress of the overall traceroute. + internal TraceStatus( + int hop, + PingStatus status, + string source, + string destination, + IPAddress destinationAddress) + { + _status = status; + Hop = hop; + Source = source; + Target = destination; + TargetAddress = destinationAddress; + + if (_status.Address == IPAddress.Any + || _status.Address == IPAddress.IPv6Any) + { + Hostname = null; + } + else + { + Hostname = _status.Destination; + } + } + + private readonly PingStatus _status; + + /// + /// Gets the number of the current hop / router. + /// + public int Hop { get; } + + /// + /// Gets the hostname of the current hop point. + /// + /// + public string? Hostname { get; } + + /// + /// Gets the sequence number of the ping in the sequence of pings to the hop point. + /// + public uint Ping { get => _status.Ping; } + + /// + /// Gets the IP address of the current hop point. + /// + public IPAddress? HopAddress { get => _status.Address; } + + /// + /// Gets the latency values of each ping to the current hop point. + /// + public long Latency { get => _status.Latency; } + + /// + /// Gets the status of the traceroute hop. + /// + public IPStatus Status { get => _status.Status; } + + /// + /// Gets the source address of the traceroute command. + /// + public string Source { get; } + + /// + /// Gets the final destination hostname of the trace. + /// + public string Target { get; } + + /// + /// Gets the final destination IP address of the trace. + /// + public IPAddress TargetAddress { get; } + + /// + /// Gets the raw PingReply object received from the ping to this hop point. + /// + public PingReply Reply { get => _status.Reply; } + } + + /// + /// Finalizes an instance of the class. + /// + ~TestConnectionCommand() + { + Dispose(disposing: false); + } + } +} diff --git a/src/Microsoft.PowerShell.Commands.Management/commands/management/TestPathCommand.cs b/src/Microsoft.PowerShell.Commands.Management/commands/management/TestPathCommand.cs new file mode 100644 index 00000000000..50765c0e0ae --- /dev/null +++ b/src/Microsoft.PowerShell.Commands.Management/commands/management/TestPathCommand.cs @@ -0,0 +1,247 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Management.Automation; + +namespace Microsoft.PowerShell.Commands +{ + /// + /// The valid values for the -PathType parameter for test-path. + /// + public enum TestPathType + { + /// + /// If the item at the path exists, true will be returned. + /// + Any, + + /// + /// If the item at the path exists and is a container, true will be returned. + /// + Container, + + /// + /// If the item at the path exists and is not a container, true will be returned. + /// + Leaf + } + + /// + /// A command to determine if an item exists at a specified path. + /// + [Cmdlet(VerbsDiagnostic.Test, "Path", DefaultParameterSetName = "Path", SupportsTransactions = true, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2097057")] + [OutputType(typeof(bool))] + public class TestPathCommand : CoreCommandWithCredentialsBase + { + #region Parameters + + /// + /// Gets or sets the path parameter to the command. + /// + [Parameter(Position = 0, ParameterSetName = "Path", + Mandatory = true, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true)] + [AllowNull] + [AllowEmptyCollection] + [AllowEmptyString] + public string[] Path + { + get { return _paths; } + + set { _paths = value; } + } + + /// + /// Gets or sets the literal path parameter to the command. + /// + [Parameter(ParameterSetName = "LiteralPath", + Mandatory = true, ValueFromPipeline = false, ValueFromPipelineByPropertyName = true)] + [Alias("PSPath", "LP")] + [AllowNull] + [AllowEmptyCollection] + [AllowEmptyString] + public string[] LiteralPath + { + get + { + return _paths; + } + + set + { + base.SuppressWildcardExpansion = true; + _paths = value; + } + } + + /// + /// Gets or sets the filter property. + /// + [Parameter] + public override string Filter + { + get { return base.Filter; } + + set { base.Filter = value; } + } + + /// + /// Gets or sets the include property. + /// + [Parameter] + public override string[] Include + { + get { return base.Include; } + + set { base.Include = value; } + } + + /// + /// Gets or sets the exclude property. + /// + [Parameter] + public override string[] Exclude + { + get { return base.Exclude; } + + set { base.Exclude = value; } + } + + /// + /// Gets or sets the isContainer property. + /// + [Parameter] + [Alias("Type")] + public TestPathType PathType { get; set; } = TestPathType.Any; + + /// + /// Gets or sets the IsValid parameter. + /// + [Parameter] + public SwitchParameter IsValid { get; set; } = new SwitchParameter(); + + /// + /// A virtual method for retrieving the dynamic parameters for a cmdlet. Derived cmdlets + /// that require dynamic parameters should override this method and return the + /// dynamic parameter object. + /// + /// + /// The context under which the command is running. + /// + /// + /// An object representing the dynamic parameters for the cmdlet or null if there + /// are none. + /// + internal override object GetDynamicParameters(CmdletProviderContext context) + { + object result = null; + + if (!IsValid) + { + if (Path != null && Path.Length > 0 && Path[0] != null) + { + result = InvokeProvider.Item.ItemExistsDynamicParameters(Path[0], context); + } + else + { + result = InvokeProvider.Item.ItemExistsDynamicParameters(".", context); + } + } + + return result; + } + + #endregion Parameters + + #region parameter data + + /// + /// The path to the item to ping. + /// + private string[] _paths; + + #endregion parameter data + + #region Command code + + /// + /// Determines if an item at the specified path exists. + /// + protected override void ProcessRecord() + { + if (_paths == null || _paths.Length == 0) + { + WriteError(new ErrorRecord( + new ArgumentNullException(TestPathResources.PathIsNullOrEmptyCollection), + "NullPathNotPermitted", + ErrorCategory.InvalidArgument, + Path)); + + return; + } + + CmdletProviderContext currentContext = CmdletProviderContext; + + foreach (string path in _paths) + { + bool result = false; + + if (string.IsNullOrWhiteSpace(path)) + { + if (path is null) + { + WriteError(new ErrorRecord( + new ArgumentNullException(TestPathResources.PathIsNullOrEmptyCollection), + "NullPathNotPermitted", + ErrorCategory.InvalidArgument, + Path)); + } + else + { + WriteObject(result); + } + + continue; + } + + try + { + if (IsValid) + { + result = SessionState.Path.IsValid(path, currentContext); + } + else + { + result = InvokeProvider.Item.Exists(path, currentContext); + + if (this.PathType == TestPathType.Container) + { + result &= InvokeProvider.Item.IsContainer(path, currentContext); + } + else if (this.PathType == TestPathType.Leaf) + { + result &= !InvokeProvider.Item.IsContainer(path, currentContext); + } + } + } + + // Any of the known exceptions means the path does not exist. + catch (PSNotSupportedException) + { + } + catch (DriveNotFoundException) + { + } + catch (ProviderNotFoundException) + { + } + catch (ItemNotFoundException) + { + } + + WriteObject(result); + } + } + #endregion Command code + } +} diff --git a/src/Microsoft.PowerShell.Commands.Management/commands/management/TimeZoneCommands.cs b/src/Microsoft.PowerShell.Commands.Management/commands/management/TimeZoneCommands.cs index 75b41682981..22a50e41176 100644 --- a/src/Microsoft.PowerShell.Commands.Management/commands/management/TimeZoneCommands.cs +++ b/src/Microsoft.PowerShell.Commands.Management/commands/management/TimeZoneCommands.cs @@ -1,11 +1,14 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + using System; using System.Collections.Generic; using System.Collections.ObjectModel; -using System.Management.Automation; using System.ComponentModel; -using System.Runtime.InteropServices; -using System.Globalization; using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Management.Automation; +using System.Runtime.InteropServices; namespace Microsoft.PowerShell.Commands { @@ -13,11 +16,12 @@ namespace Microsoft.PowerShell.Commands /// A cmdlet to retrieve time zone information. /// [Cmdlet(VerbsCommon.Get, "TimeZone", DefaultParameterSetName = "Name", - HelpUri = "https://go.microsoft.com/fwlink/?LinkId=799468")] + HelpUri = "https://go.microsoft.com/fwlink/?LinkId=2096904")] + [OutputType(typeof(TimeZoneInfo))] [Alias("gtz")] public class GetTimeZoneCommand : PSCmdlet { -#region Parameters + #region Parameters /// /// A list of the local time zone ids that the cmdlet should look up. @@ -40,17 +44,17 @@ public class GetTimeZoneCommand : PSCmdlet [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] public string[] Name { get; set; } -#endregion Parameters + #endregion Parameters /// - /// Implementation of the ProcessRecord method for Get-TimeZone + /// Implementation of the ProcessRecord method for Get-TimeZone. /// protected override void ProcessRecord() { // make sure we've got the latest time zone settings TimeZoneInfo.ClearCachedData(); - if (this.ParameterSetName.Equals("ListAvailable", StringComparison.OrdinalIgnoreCase)) + if (ListAvailable) { // output the list of all available time zones WriteObject(TimeZoneInfo.GetSystemTimeZones(), true); @@ -73,13 +77,13 @@ protected override void ProcessRecord() } else // ParameterSetName == "Name" { - if (null != Name) + if (Name != null) { // lookup each time zone name (or wildcard pattern) foreach (string tzname in Name) { TimeZoneInfo[] timeZones = TimeZoneHelper.LookupSystemTimeZoneInfoByName(tzname); - if (0 < timeZones.Length) + if (timeZones.Length > 0) { // manually process each object in the array, so if there is only a single // entry then the returned type is TimeZoneInfo and not TimeZoneInfo[], and @@ -117,17 +121,18 @@ protected override void ProcessRecord() [Cmdlet(VerbsCommon.Set, "TimeZone", SupportsShouldProcess = true, DefaultParameterSetName = "Name", - HelpUri = "https://go.microsoft.com/fwlink/?LinkId=799469")] + HelpUri = "https://go.microsoft.com/fwlink/?LinkId=2097056")] + [OutputType(typeof(TimeZoneInfo))] [Alias("stz")] public class SetTimeZoneCommand : PSCmdlet { -#region string constants + #region string constants private const string TimeZoneTarget = "Local System"; -#endregion string constants + #endregion string constants -#region Parameters + #region Parameters /// /// The name of the local time zone that the system should use. @@ -148,15 +153,15 @@ public class SetTimeZoneCommand : PSCmdlet public string Name { get; set; } /// - /// Request return of the new local time zone as a TimeZoneInfo object + /// Request return of the new local time zone as a TimeZoneInfo object. /// [Parameter] public SwitchParameter PassThru { get; set; } -#endregion Parameters + #endregion Parameters /// - /// Implementation of the ProcessRecord method for Get-TimeZone + /// Implementation of the ProcessRecord method for Set-TimeZone. /// [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly", Justification = "Since Name is not a parameter of this method, it confuses FXCop. It is the appropriate value for the exception.")] protected override void ProcessRecord() @@ -185,7 +190,7 @@ protected override void ProcessRecord() { // lookup the time zone name and make sure we have one (and only one) match TimeZoneInfo[] timeZones = TimeZoneHelper.LookupSystemTimeZoneInfoByName(Name); - if (0 == timeZones.Length) + if (timeZones.Length == 0) { string message = string.Format(CultureInfo.InvariantCulture, TimeZoneResources.TimeZoneNameNotFound, Name); @@ -195,7 +200,7 @@ protected override void ProcessRecord() ErrorCategory.InvalidArgument, "Name")); } - else if (1 < timeZones.Length) + else if (timeZones.Length > 1) { string message = string.Format(CultureInfo.InvariantCulture, TimeZoneResources.MultipleMatchingTimeZones, Name); @@ -251,14 +256,14 @@ protected override void ProcessRecord() try { // construct and populate a new DYNAMIC_TIME_ZONE_INFORMATION structure - NativeMethods.DYNAMIC_TIME_ZONE_INFORMATION dtzi = new NativeMethods.DYNAMIC_TIME_ZONE_INFORMATION(); + NativeMethods.DYNAMIC_TIME_ZONE_INFORMATION dtzi = new(); dtzi.Bias -= (int)InputObject.BaseUtcOffset.TotalMinutes; dtzi.StandardName = InputObject.StandardName; dtzi.DaylightName = InputObject.DaylightName; dtzi.TimeZoneKeyName = InputObject.Id; // Request time zone transition information for the current year - NativeMethods.TIME_ZONE_INFORMATION tzi = new NativeMethods.TIME_ZONE_INFORMATION(); + NativeMethods.TIME_ZONE_INFORMATION tzi = new(); if (!NativeMethods.GetTimeZoneInformationForYear((ushort)DateTime.Now.Year, ref dtzi, ref tzi)) { ThrowWin32Error(); @@ -317,7 +322,7 @@ protected override void ProcessRecord() } } -#region Helper functions + #region Helper functions /// /// True if the current process has access to change the time zone setting. @@ -340,7 +345,7 @@ protected bool HasAccess try { // setup the privileges being checked - NativeMethods.PRIVILEGE_SET ps = new NativeMethods.PRIVILEGE_SET() + NativeMethods.PRIVILEGE_SET ps = new() { PrivilegeCount = 1, Control = 1, @@ -370,7 +375,7 @@ protected bool HasAccess } /// - /// Set the SeTimeZonePrivilege, which controls access to the SetDynamicTimeZoneInformation API + /// Set the SeTimeZonePrivilege, which controls access to the SetDynamicTimeZoneInformation API. /// /// Set to true to enable (or false to disable) the privilege. protected void SetAccessToken(bool enable) @@ -387,7 +392,7 @@ protected void SetAccessToken(bool enable) try { // setup the privileges being requested - NativeMethods.TOKEN_PRIVILEGES tp = new NativeMethods.TOKEN_PRIVILEGES() + NativeMethods.TOKEN_PRIVILEGES tp = new() { PrivilegeCount = 1, Luid = 0, @@ -421,20 +426,13 @@ protected void ThrowWin32Error() throw new Win32Exception(error); } -#endregion Helper functions + #endregion Helper functions -#region Win32 interop helper + #region Win32 interop helper - internal class NativeMethods + internal static class NativeMethods { - /// - /// Private constructor to prevent instantiation - /// - private NativeMethods() - { - } - -#region Native DLL locations + #region Native DLL locations private const string SetDynamicTimeZoneApiDllName = "api-ms-win-core-timezone-l1-1-0.dll"; private const string GetTimeZoneInformationForYearApiDllName = "api-ms-win-core-timezone-l1-1-0.dll"; @@ -446,12 +444,12 @@ private NativeMethods() private const string CloseHandleApiDllName = "api-ms-win-downlevel-kernel32-l1-1-0.dll"; private const string SendMessageTimeoutApiDllName = "ext-ms-win-rtcore-ntuser-window-ext-l1-1-0.dll"; -#endregion Native DLL locations + #endregion Native DLL locations -#region Win32 SetDynamicTimeZoneInformation imports + #region Win32 SetDynamicTimeZoneInformation imports /// - /// Used to marshal win32 SystemTime structure to managed code layer + /// Used to marshal win32 SystemTime structure to managed code layer. /// [StructLayout(LayoutKind.Sequential)] public struct SystemTime @@ -499,7 +497,7 @@ public struct SystemTime } /// - /// Used to marshal win32 DYNAMIC_TIME_ZONE_INFORMATION structure to managed code layer + /// Used to marshal win32 DYNAMIC_TIME_ZONE_INFORMATION structure to managed code layer. /// [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] public struct DYNAMIC_TIME_ZONE_INFORMATION @@ -550,7 +548,7 @@ public struct DYNAMIC_TIME_ZONE_INFORMATION } /// - /// Used to marshal win32 TIME_ZONE_INFORMATION structure to managed code layer + /// Used to marshal win32 TIME_ZONE_INFORMATION structure to managed code layer. /// [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] public struct TIME_ZONE_INFORMATION @@ -591,49 +589,51 @@ public struct TIME_ZONE_INFORMATION } /// - /// PInvoke SetDynamicTimeZoneInformation API + /// PInvoke SetDynamicTimeZoneInformation API. /// /// A DYNAMIC_TIME_ZONE_INFORMATION structure representing the desired local time zone. /// [DllImport(SetDynamicTimeZoneApiDllName, SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] public static extern bool SetDynamicTimeZoneInformation([In] ref DYNAMIC_TIME_ZONE_INFORMATION lpTimeZoneInformation); [DllImport(GetTimeZoneInformationForYearApiDllName, SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] public static extern bool GetTimeZoneInformationForYear([In] ushort wYear, [In] ref DYNAMIC_TIME_ZONE_INFORMATION pdtzi, ref TIME_ZONE_INFORMATION ptzi); -#endregion Win32 SetDynamicTimeZoneInformation imports + #endregion Win32 SetDynamicTimeZoneInformation imports -#region Win32 AdjustTokenPrivilege imports + #region Win32 AdjustTokenPrivilege imports /// - /// Definition of TOKEN_QUERY constant from Win32 API + /// Definition of TOKEN_QUERY constant from Win32 API. /// public const int TOKEN_QUERY = 0x00000008; /// - /// Definition of TOKEN_ADJUST_PRIVILEGES constant from Win32 API + /// Definition of TOKEN_ADJUST_PRIVILEGES constant from Win32 API. /// public const int TOKEN_ADJUST_PRIVILEGES = 0x00000020; /// - /// Definition of SE_PRIVILEGE_ENABLED constant from Win32 API + /// Definition of SE_PRIVILEGE_ENABLED constant from Win32 API. /// public const int SE_PRIVILEGE_ENABLED = 0x00000002; /// - /// Definition of SE_TIME_ZONE_NAME constant from Win32 API + /// Definition of SE_TIME_ZONE_NAME constant from Win32 API. /// - public const string SE_TIME_ZONE_NAME = "SeTimeZonePrivilege"; //http://msdn.microsoft.com/en-us/library/bb530716(VS.85).aspx + public const string SE_TIME_ZONE_NAME = "SeTimeZonePrivilege"; // https://msdn.microsoft.com/library/bb530716(VS.85).aspx /// - /// PInvoke GetCurrentProcess API + /// PInvoke GetCurrentProcess API. /// /// [DllImport(GetCurrentProcessApiDllName, ExactSpelling = true)] public static extern IntPtr GetCurrentProcess(); /// - /// PInvoke OpenProcessToken API + /// PInvoke OpenProcessToken API. /// /// /// @@ -644,7 +644,7 @@ public struct TIME_ZONE_INFORMATION public static extern bool OpenProcessToken(IntPtr ProcessHandle, int DesiredAccess, ref IntPtr TokenHandle); /// - /// PInvoke LookupPrivilegeValue API + /// PInvoke LookupPrivilegeValue API. /// /// /// @@ -655,7 +655,7 @@ public struct TIME_ZONE_INFORMATION public static extern bool LookupPrivilegeValue(string lpSystemName, string lpName, ref long lpLuid); /// - /// PInvoke PrivilegeCheck API + /// PInvoke PrivilegeCheck API. /// /// /// @@ -665,9 +665,8 @@ public struct TIME_ZONE_INFORMATION [return: MarshalAs(UnmanagedType.Bool)] public static extern bool PrivilegeCheck(IntPtr ClientToken, ref PRIVILEGE_SET RequiredPrivileges, ref bool pfResult); - /// - /// PInvoke AdjustTokenPrivilege API + /// PInvoke AdjustTokenPrivilege API. /// /// /// @@ -682,7 +681,7 @@ public static extern bool AdjustTokenPrivileges(IntPtr TokenHandle, bool Disable ref TOKEN_PRIVILEGES NewState, int BufferLength, IntPtr PreviousState, IntPtr ReturnLength); /// - /// PInvoke CloseHandle API + /// PInvoke CloseHandle API. /// /// /// @@ -691,7 +690,7 @@ public static extern bool AdjustTokenPrivileges(IntPtr TokenHandle, bool Disable public static extern bool CloseHandle(IntPtr hObject); /// - /// Used to marshal win32 PRIVILEGE_SET structure to managed code layer + /// Used to marshal win32 PRIVILEGE_SET structure to managed code layer. /// [StructLayout(LayoutKind.Sequential, Pack = 1)] public struct PRIVILEGE_SET @@ -703,7 +702,7 @@ public struct PRIVILEGE_SET } /// - /// Used to marshal win32 TOKEN_PRIVILEGES structure to managed code layer + /// Used to marshal win32 TOKEN_PRIVILEGES structure to managed code layer. /// [StructLayout(LayoutKind.Sequential, Pack = 1)] public struct TOKEN_PRIVILEGES @@ -713,27 +712,27 @@ public struct TOKEN_PRIVILEGES public int Attributes; } -#endregion Win32 AdjustTokenPrivilege imports + #endregion Win32 AdjustTokenPrivilege imports -#region Win32 SendMessage imports + #region Win32 SendMessage imports /// - /// Definition of WM_SETTINGCHANGE constant from Win32 API + /// Definition of WM_SETTINGCHANGE constant from Win32 API. /// public const int WM_SETTINGCHANGE = 0x001A; /// - /// Definition of HWND_BROADCAST constant from Win32 API + /// Definition of HWND_BROADCAST constant from Win32 API. /// public const int HWND_BROADCAST = (-1); /// - /// Definition of SMTO_ABORTIFHUNG constant from Win32 API + /// Definition of SMTO_ABORTIFHUNG constant from Win32 API. /// public const int SMTO_ABORTIFHUNG = 0x0002; /// - /// PInvoke SendMessageTimeout API + /// PInvoke SendMessageTimeout API. /// /// /// @@ -746,26 +745,26 @@ public struct TOKEN_PRIVILEGES [DllImport(SendMessageTimeoutApiDllName, SetLastError = true, CharSet = CharSet.Unicode)] public static extern IntPtr SendMessageTimeout(IntPtr hWnd, int Msg, IntPtr wParam, string lParam, int fuFlags, int uTimeout, ref int lpdwResult); -#endregion Win32 SendMessage imports + #endregion Win32 SendMessage imports } -#endregion Win32 interop helper + #endregion Win32 interop helper } #endif /// - /// static Helper class for working with system time zones. + /// Static Helper class for working with system time zones. /// internal static class TimeZoneHelper { -#region Error Ids + #region Error Ids internal const string TimeZoneNotFoundError = "TimeZoneNotFound"; internal const string MultipleMatchingTimeZonesError = "MultipleMatchingTimeZones"; internal const string InsufficientPermissionsError = "InsufficientPermissions"; internal const string SetTimeZoneFailedError = "SetTimeZoneFailed"; -#endregion Error Ids + #endregion Error Ids /// /// Find the system time zone by checking first against StandardName and then, @@ -775,8 +774,8 @@ internal static class TimeZoneHelper /// A TimeZoneInfo object array containing information about the specified system time zones. internal static TimeZoneInfo[] LookupSystemTimeZoneInfoByName(string name) { - WildcardPattern namePattern = new WildcardPattern(name, WildcardOptions.IgnoreCase); - List tzi = new List(); + WildcardPattern namePattern = new(name, WildcardOptions.IgnoreCase); + List tzi = new(); // get the available system time zones ReadOnlyCollection zones = TimeZoneInfo.GetSystemTimeZones(); diff --git a/src/Microsoft.PowerShell.Commands.Management/commands/management/UseTransactionCommand.cs b/src/Microsoft.PowerShell.Commands.Management/commands/management/UseTransactionCommand.cs index b65f46412c7..67d3acee44a 100644 --- a/src/Microsoft.PowerShell.Commands.Management/commands/management/UseTransactionCommand.cs +++ b/src/Microsoft.PowerShell.Commands.Management/commands/management/UseTransactionCommand.cs @@ -1,10 +1,10 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. using System; using System.Management.Automation; using System.Management.Automation.Internal; + using Dbg = System.Management.Automation; namespace Microsoft.PowerShell.Commands @@ -17,7 +17,7 @@ public class UseTransactionCommand : PSCmdlet { /// /// This parameter specifies the script block to run in the current - /// PowerShell transaction + /// PowerShell transaction. /// [Parameter(Position = 0, Mandatory = true)] public ScriptBlock TransactedScript @@ -26,15 +26,17 @@ public ScriptBlock TransactedScript { return _transactedScript; } + set { _transactedScript = value; } } + private ScriptBlock _transactedScript; /// - /// Commits the current transaction + /// Commits the current transaction. /// protected override void EndProcessing() { @@ -42,7 +44,7 @@ protected override void EndProcessing() { try { - var emptyArray = Utils.EmptyArray(); + var emptyArray = Array.Empty(); _transactedScript.InvokeUsingCmdlet( contextCmdlet: this, useLocalScope: false, @@ -87,6 +89,5 @@ protected override void EndProcessing() } } } - } // CommitTransactionCommand -} // namespace Microsoft.PowerShell.Commands - + } +} diff --git a/src/Microsoft.PowerShell.Commands.Management/commands/management/WMIHelper.cs b/src/Microsoft.PowerShell.Commands.Management/commands/management/WMIHelper.cs index bef805293b0..31670a7d632 100644 --- a/src/Microsoft.PowerShell.Commands.Management/commands/management/WMIHelper.cs +++ b/src/Microsoft.PowerShell.Commands.Management/commands/management/WMIHelper.cs @@ -1,23 +1,24 @@ -// -// Copyright (C) Microsoft. All rights reserved. -// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. using System; +using System.Collections; +using System.Collections.ObjectModel; +using System.ComponentModel; +using System.Diagnostics.CodeAnalysis; using System.Globalization; -using System.Management.Automation; using System.Management; +using System.Management.Automation; using System.Management.Automation.Internal; -using System.Text; using System.Management.Automation.Provider; -using System.ComponentModel; -using System.Collections; -using System.Collections.ObjectModel; -using System.Security.AccessControl; +using System.Management.Automation.Remoting; using System.Runtime.InteropServices; +using System.Security.AccessControl; +using System.Text; using System.Threading; -using System.Management.Automation.Remoting; -using System.Diagnostics.CodeAnalysis; + using Microsoft.PowerShell.Commands.Internal; + using Dbg = System.Management.Automation; namespace Microsoft.PowerShell.Commands @@ -26,13 +27,13 @@ namespace Microsoft.PowerShell.Commands /// /// Base class for all WMI helper classes. This is an abstract class - /// and the helpers need to derive from this + /// and the helpers need to derive from this. /// internal abstract class AsyncCmdletHelper : IThrottleOperation { /// /// Exception raised internally when any method of this class - /// is executed + /// is executed. /// internal Exception InternalException { @@ -41,6 +42,7 @@ internal Exception InternalException return internalException; } } + protected Exception internalException = null; } @@ -52,12 +54,12 @@ internal Exception InternalException internal class WmiAsyncCmdletHelper : AsyncCmdletHelper { /// - /// Internal Constructor + /// Internal Constructor. /// - /// Job associated with this operation - /// object associated with this operation - /// computer on which the operation is invoked - /// sink to get wmi objects + /// Job associated with this operation. + /// Object associated with this operation. + /// Computer on which the operation is invoked. + /// Sink to get wmi objects. internal WmiAsyncCmdletHelper(PSWmiChildJob childJob, Cmdlet wmiObject, string computerName, ManagementOperationObserver results) { _wmiObject = wmiObject; @@ -71,11 +73,11 @@ internal WmiAsyncCmdletHelper(PSWmiChildJob childJob, Cmdlet wmiObject, string c /// Internal Constructor. This variant takes a count parameter that determines how many times /// the WMI command is executed. /// - /// Job associated with this operation - /// Object associated with this operation - /// Computer on which the operation is invoked - /// Sink to return wmi objects - /// Number of times the WMI command is executed + /// Job associated with this operation. + /// Object associated with this operation. + /// Computer on which the operation is invoked. + /// Sink to return wmi objects. + /// Number of times the WMI command is executed. internal WmiAsyncCmdletHelper(PSWmiChildJob childJob, Cmdlet wmiObject, string computerName, ManagementOperationObserver results, int count) : this(childJob, wmiObject, computerName, results) { @@ -89,18 +91,19 @@ internal WmiAsyncCmdletHelper(PSWmiChildJob childJob, Cmdlet wmiObject, string c private int _cmdCount = 1; private PSWmiChildJob _job; /// - /// current operation state + /// Current operation state. /// internal WmiState State { get { return _state; } + set { _state = value; } } private WmiState _state; /// - /// Cancel WMI connection + /// Cancel WMI connection. /// internal override void StopOperation() { @@ -109,20 +112,21 @@ internal override void StopOperation() RaiseOperationCompleteEvent(null, OperationState.StopComplete); } /// - /// Uses this.filter, this.wmiClass and this.property to retrieve the filter + /// Uses this.filter, this.wmiClass and this.property to retrieve the filter. /// private string GetWmiQueryString() { GetWmiObjectCommand getObject = (GetWmiObjectCommand)_wmiObject; StringBuilder returnValue = new StringBuilder("select "); - returnValue.Append(String.Join(", ", getObject.Property)); + returnValue.Append(string.Join(", ", getObject.Property)); returnValue.Append(" from "); returnValue.Append(getObject.Class); - if (!String.IsNullOrEmpty(getObject.Filter)) + if (!string.IsNullOrEmpty(getObject.Filter)) { returnValue.Append(" where "); returnValue.Append(getObject.Filter); } + return returnValue.ToString(); } @@ -156,31 +160,31 @@ internal override void StartOperation() RaiseOperationCompleteEvent(null, OperationState.StopComplete); return; } + thread.IsBackground = true; - //thread.SetApartmentState( ApartmentState.STA); + thread.SetApartmentState(ApartmentState.STA); thread.Start(); } /// - /// /// internal override event EventHandler OperationComplete; private Cmdlet _wmiObject; /// - /// Raise operation completion event + /// Raise operation completion event. /// internal void RaiseOperationCompleteEvent(EventArgs baseEventArgs, OperationState state) { OperationStateEventArgs operationStateEventArgs = new OperationStateEventArgs(); operationStateEventArgs.OperationState = state; OperationComplete.SafeInvoke(this, operationStateEventArgs); - } // RaiseOperationCompleteEvent + } - /// + /// /// Raise WMI state changed event - /// + /// internal void RaiseWmiOperationState(EventArgs baseEventArgs, WmiState state) { WmiJobStateEventArgs wmiJobStateEventArgs = new WmiJobStateEventArgs(); @@ -202,10 +206,10 @@ private void ConnectSetWmi() try { PutOptions pOptions = new PutOptions(); - //Extra check + // Extra check if (setObject.InputObject.GetType() == typeof(ManagementClass)) { - //Check if Flag specified is CreateOnly or not + // Check if Flag specified is CreateOnly or not if (setObject.flagSpecified && setObject.PutType != PutType.CreateOnly) { InvalidOperationException e = new InvalidOperationException("CreateOnlyFlagNotSpecifiedWithClassPath"); @@ -214,12 +218,13 @@ private void ConnectSetWmi() RaiseOperationCompleteEvent(null, OperationState.StopComplete); return; } + mObj = ((ManagementClass)setObject.InputObject).CreateInstance(); setObject.PutType = PutType.CreateOnly; } else { - //Check if Flag specified is Updateonly or UpdateOrCreateOnly or not + // Check if Flag specified is Updateonly or UpdateOrCreateOnly or not if (setObject.flagSpecified) { if (!(setObject.PutType == PutType.UpdateOnly || setObject.PutType == PutType.UpdateOrCreate)) @@ -238,6 +243,7 @@ private void ConnectSetWmi() mObj = (ManagementObject)setObject.InputObject.Clone(); } + if (setObject.Arguments != null) { IDictionaryEnumerator en = setObject.Arguments.GetEnumerator(); @@ -246,6 +252,7 @@ private void ConnectSetWmi() mObj[en.Key as string] = en.Value; } } + pOptions.Type = setObject.PutType; if (mObj != null) { @@ -281,7 +288,7 @@ private void ConnectSetWmi() else { ManagementPath mPath = null; - //If Class is specified only CreateOnly flag is supported + // If Class is specified only CreateOnly flag is supported if (setObject.Class != null) { if (setObject.flagSpecified && setObject.PutType != PutType.CreateOnly) @@ -292,12 +299,13 @@ private void ConnectSetWmi() RaiseOperationCompleteEvent(null, OperationState.StopComplete); return; } + setObject.PutType = PutType.CreateOnly; } else { mPath = new ManagementPath(setObject.Path); - if (String.IsNullOrEmpty(mPath.NamespacePath)) + if (string.IsNullOrEmpty(mPath.NamespacePath)) { mPath.NamespacePath = setObject.Namespace; } @@ -318,6 +326,7 @@ private void ConnectSetWmi() RaiseOperationCompleteEvent(null, OperationState.StopComplete); return; } + if (mPath.IsClass) { if (setObject.flagSpecified && setObject.PutType != PutType.CreateOnly) @@ -328,6 +337,7 @@ private void ConnectSetWmi() RaiseOperationCompleteEvent(null, OperationState.StopComplete); return; } + setObject.PutType = PutType.CreateOnly; } else @@ -349,7 +359,7 @@ private void ConnectSetWmi() } } } - //If server name is specified loop through it. + // If server name is specified loop through it. if (mPath != null) { if (!(mPath.Server == "." && setObject.serverNameSpecified)) @@ -357,6 +367,7 @@ private void ConnectSetWmi() _computerName = mPath.Server; } } + ConnectionOptions options = setObject.GetConnectionOption(); ManagementObject mObject = null; try @@ -373,7 +384,7 @@ private void ConnectSetWmi() } else { - //This can throw if path does not exist caller should catch it. + // This can throw if path does not exist caller should catch it. ManagementObject mInstance = new ManagementObject(mPath); mInstance.Scope = mScope; try @@ -389,6 +400,7 @@ private void ConnectSetWmi() RaiseOperationCompleteEvent(null, OperationState.StopComplete); return; } + int namespaceIndex = setObject.Path.IndexOf(':'); if (namespaceIndex == -1) { @@ -397,6 +409,7 @@ private void ConnectSetWmi() RaiseOperationCompleteEvent(null, OperationState.StopComplete); return; } + int classIndex = (setObject.Path.Substring(namespaceIndex)).IndexOf('.'); if (classIndex == -1) { @@ -405,13 +418,14 @@ private void ConnectSetWmi() RaiseOperationCompleteEvent(null, OperationState.StopComplete); return; } - //Get class object and create instance. + // Get class object and create instance. string newPath = setObject.Path.Substring(0, classIndex + namespaceIndex); ManagementPath classPath = new ManagementPath(newPath); ManagementClass mClass = new ManagementClass(classPath); mClass.Scope = mScope; mInstance = mClass.CreateInstance(); } + mObject = mInstance; } } @@ -422,6 +436,7 @@ private void ConnectSetWmi() mClass.Scope = scope; mObject = mClass.CreateInstance(); } + if (setObject.Arguments != null) { IDictionaryEnumerator en = setObject.Arguments.GetEnumerator(); @@ -430,6 +445,7 @@ private void ConnectSetWmi() mObject[en.Key as string] = en.Value; } } + PutOptions pOptions = new PutOptions(); pOptions.Type = setObject.PutType; if (mObject != null) @@ -491,6 +507,7 @@ private void ConnectInvokeWmi() inParamCount--; } } + invokeObject.InputObject.InvokeMethod(_results, invokeObject.Name, inputParameters, null); } catch (ManagementException e) @@ -511,6 +528,7 @@ private void ConnectInvokeWmi() _state = WmiState.Failed; RaiseOperationCompleteEvent(null, OperationState.StopComplete); } + return; } else @@ -521,7 +539,7 @@ private void ConnectInvokeWmi() if (invokeObject.Path != null) { mPath = new ManagementPath(invokeObject.Path); - if (String.IsNullOrEmpty(mPath.NamespacePath)) + if (string.IsNullOrEmpty(mPath.NamespacePath)) { mPath.NamespacePath = invokeObject.Namespace; } @@ -542,7 +560,7 @@ private void ConnectInvokeWmi() RaiseOperationCompleteEvent(null, OperationState.StopComplete); return; } - //If server name is specified loop through it. + // If server name is specified loop through it. if (!(mPath.Server == "." && invokeObject.serverNameSpecified)) { _computerName = mPath.Server; @@ -583,6 +601,7 @@ private void ConnectInvokeWmi() ManagementObject mInstance = new ManagementObject(mPath); mObject = mInstance; } + ManagementScope mScope = new ManagementScope(mPath, options); mObject.Scope = mScope; } @@ -660,7 +679,7 @@ private void ConnectInvokeWmi() } /// - /// Check if we need to enable the shutdown privilege + /// Check if we need to enable the shutdown privilege. /// /// /// @@ -673,11 +692,11 @@ private bool NeedToEnablePrivilege(string computer, string methodName, ref bool { result = true; - // CLR 4.0 Port note - use https://msdn.microsoft.com/en-us/library/system.net.networkinformation.ipglobalproperties.hostname(v=vs.110).aspx + // CLR 4.0 Port note - use https://msdn.microsoft.com/library/system.net.networkinformation.ipglobalproperties.hostname(v=vs.110).aspx string localName = System.Net.Dns.GetHostName(); // And for this, use PsUtils.GetHostname() - string localFullName = System.Net.Dns.GetHostEntry("").HostName; + string localFullName = System.Net.Dns.GetHostEntry(string.Empty).HostName; if (computer.Equals(".") || computer.Equals("localhost", StringComparison.OrdinalIgnoreCase) || computer.Equals(localName, StringComparison.OrdinalIgnoreCase) || computer.Equals(localFullName, StringComparison.OrdinalIgnoreCase)) @@ -721,6 +740,7 @@ private void ConnectRemoveWmi() _state = WmiState.Failed; RaiseOperationCompleteEvent(null, OperationState.StopComplete); } + return; } else @@ -731,7 +751,7 @@ private void ConnectRemoveWmi() if (removeObject.Path != null) { mPath = new ManagementPath(removeObject.Path); - if (String.IsNullOrEmpty(mPath.NamespacePath)) + if (string.IsNullOrEmpty(mPath.NamespacePath)) { mPath.NamespacePath = removeObject.Namespace; } @@ -752,11 +772,13 @@ private void ConnectRemoveWmi() RaiseOperationCompleteEvent(null, OperationState.StopComplete); return; } + if (!(mPath.Server == "." && removeObject.serverNameSpecified)) { _computerName = mPath.Server; } } + try { if (removeObject.Path != null) @@ -772,6 +794,7 @@ private void ConnectRemoveWmi() ManagementObject mInstance = new ManagementObject(mPath); mObject = mInstance; } + ManagementScope mScope = new ManagementScope(mPath, options); mObject.Scope = mScope; } @@ -782,6 +805,7 @@ private void ConnectRemoveWmi() mObject = mClass; mObject.Scope = scope; } + mObject.Delete(_results); } catch (ManagementException e) @@ -819,7 +843,7 @@ private void ConnectGetWMI() if (!getObject.ValidateClassFormat()) { ArgumentException e = new ArgumentException( - String.Format( + string.Format( Thread.CurrentThread.CurrentCulture, "Class", getObject.Class)); @@ -828,6 +852,7 @@ private void ConnectGetWMI() RaiseOperationCompleteEvent(null, OperationState.StopComplete); return; } + try { if (getObject.Recurse.IsPresent) @@ -851,6 +876,7 @@ private void ConnectGetWMI() namespaceArray.Add(connectNamespace + "\\" + obj["Name"]); } } + if (topNamespace) { topNamespace = false; @@ -860,6 +886,7 @@ private void ConnectGetWMI() { sinkArray.Add(_job.GetNewSink()); } + connectArray.Add(scope); currentNamespaceCount++; } @@ -882,6 +909,7 @@ private void ConnectGetWMI() currentNamespaceCount++; continue; } + if (topNamespace) { topNamespace = false; @@ -891,6 +919,7 @@ private void ConnectGetWMI() { searcher.Get((ManagementOperationObserver)sinkArray[currentNamespaceCount]); } + currentNamespaceCount++; } } @@ -922,8 +951,10 @@ private void ConnectGetWMI() _state = WmiState.Failed; RaiseOperationCompleteEvent(null, OperationState.StopComplete); } + return; } + string queryString = string.IsNullOrEmpty(getObject.Query) ? GetWmiQueryString() : getObject.Query; ObjectQuery query = new ObjectQuery(queryString.ToString()); try @@ -961,30 +992,30 @@ private void ConnectGetWMI() } } - /// + /// /// Event which will be triggered when WMI state is changed. /// Currently it is to notify Jobs that state has changed to running. /// Other states are notified via OperationComplete. - /// + /// internal sealed class WmiJobStateEventArgs : EventArgs { - /// + /// /// WMI state - /// + /// internal WmiState WmiState { get; set; } } /// - /// Enumerated type defining the state of the WMI operation + /// Enumerated type defining the state of the WMI operation. /// public enum WmiState { /// - /// The operation has not been started + /// The operation has not been started. /// NotStarted = 0, /// - /// The operation is executing + /// The operation is executing. /// Running = 1, /// @@ -1011,29 +1042,28 @@ internal static string GetScopeString(string computer, string namespaceParameter { StringBuilder returnValue = new StringBuilder("\\\\"); returnValue.Append(computer); - returnValue.Append("\\"); + returnValue.Append('\\'); returnValue.Append(namespaceParameter); return returnValue.ToString(); } } #endregion Helper Classes - /// - /// A class to set WMI connection options + /// A class to set WMI connection options. /// public class WmiBaseCmdlet : Cmdlet { #region Parameters /// - /// Perform Async operation + /// Perform Async operation. /// [Parameter] public SwitchParameter AsJob { get; set; } = false; /// - /// The Impersonation level to use + /// The Impersonation level to use. /// [Parameter(ParameterSetName = "path")] [Parameter(ParameterSetName = "class")] @@ -1043,7 +1073,7 @@ public class WmiBaseCmdlet : Cmdlet public ImpersonationLevel Impersonation { get; set; } = ImpersonationLevel.Impersonate; /// - /// The Authentication level to use + /// The Authentication level to use. /// [Parameter(ParameterSetName = "path")] [Parameter(ParameterSetName = "class")] @@ -1053,7 +1083,7 @@ public class WmiBaseCmdlet : Cmdlet public AuthenticationLevel Authentication { get; set; } = AuthenticationLevel.PacketPrivacy; /// - /// The Locale to use + /// The Locale to use. /// [Parameter(ParameterSetName = "path")] [Parameter(ParameterSetName = "class")] @@ -1063,7 +1093,7 @@ public class WmiBaseCmdlet : Cmdlet public string Locale { get; set; } = null; /// - /// If all Privileges are enabled + /// If all Privileges are enabled. /// [Parameter(ParameterSetName = "path")] [Parameter(ParameterSetName = "class")] @@ -1073,7 +1103,7 @@ public class WmiBaseCmdlet : Cmdlet public SwitchParameter EnableAllPrivileges { get; set; } /// - /// The Authority to use + /// The Authority to use. /// [Parameter(ParameterSetName = "path")] [Parameter(ParameterSetName = "class")] @@ -1083,24 +1113,24 @@ public class WmiBaseCmdlet : Cmdlet public string Authority { get; set; } = null; /// - /// The credential to use + /// The credential to use. /// [Parameter(ParameterSetName = "path")] [Parameter(ParameterSetName = "class")] [Parameter(ParameterSetName = "WQLQuery")] [Parameter(ParameterSetName = "query")] [Parameter(ParameterSetName = "list")] - [Credential()] + [Credential] public PSCredential Credential { get; set; } /// - /// The credential to use + /// The credential to use. /// [Parameter] public Int32 ThrottleLimit { get; set; } = s_DEFAULT_THROTTLE_LIMIT; /// - /// The ComputerName in which to query + /// The ComputerName in which to query. /// [Parameter(ParameterSetName = "path")] [Parameter(ParameterSetName = "class")] @@ -1113,10 +1143,11 @@ public class WmiBaseCmdlet : Cmdlet public string[] ComputerName { get { return _computerName; } + set { _computerName = value; serverNameSpecified = true; } } /// - /// The WMI namespace to use + /// The WMI namespace to use. /// [Parameter(ParameterSetName = "path")] [Parameter(ParameterSetName = "class")] @@ -1127,6 +1158,7 @@ public string[] ComputerName public string Namespace { get { return _nameSpace; } + set { _nameSpace = value; namespaceSpecified = true; } } #endregion Parameters @@ -1137,7 +1169,7 @@ public string Namespace /// private string[] _computerName = new string[] { "localhost" }; /// - /// WMI namespace + /// WMI namespace. /// private string _nameSpace = "root\\cimv2"; /// @@ -1155,7 +1187,7 @@ public string Namespace #region Command code /// - /// Get connection options + /// Get connection options. /// internal ConnectionOptions GetConnectionOption() { @@ -1174,10 +1206,11 @@ internal ConnectionOptions GetConnectionOption() options.SecurePassword = this.Credential.Password; } } + return options; } /// - /// Set wmi instance helper + /// Set wmi instance helper. /// internal ManagementObject SetWmiInstanceGetObject(ManagementPath mPath, string serverName) { @@ -1198,7 +1231,7 @@ internal ManagementObject SetWmiInstanceGetObject(ManagementPath mPath, string s } else { - //This can throw if path does not exist caller should catch it. + // This can throw if path does not exist caller should catch it. ManagementObject mInstance = new ManagementObject(mPath); mInstance.Scope = mScope; try @@ -1211,23 +1244,26 @@ internal ManagementObject SetWmiInstanceGetObject(ManagementPath mPath, string s { throw; } + int namespaceIndex = setObject.Path.IndexOf(':'); if (namespaceIndex == -1) { throw; } + int classIndex = (setObject.Path.Substring(namespaceIndex)).IndexOf('.'); if (classIndex == -1) { throw; } - //Get class object and create instance. + // Get class object and create instance. string newPath = setObject.Path.Substring(0, classIndex + namespaceIndex); ManagementPath classPath = new ManagementPath(newPath); ManagementClass mClass = new ManagementClass(classPath); mClass.Scope = mScope; mInstance = mClass.CreateInstance(); } + mObject = mInstance; } } @@ -1238,6 +1274,7 @@ internal ManagementObject SetWmiInstanceGetObject(ManagementPath mPath, string s mClass.Scope = scope; mObject = mClass.CreateInstance(); } + if (setObject.Arguments != null) { IDictionaryEnumerator en = setObject.Arguments.GetEnumerator(); @@ -1247,10 +1284,11 @@ internal ManagementObject SetWmiInstanceGetObject(ManagementPath mPath, string s } } } + return mObject; } /// - /// Set wmi instance helper for building management path + /// Set wmi instance helper for building management path. /// internal ManagementPath SetWmiInstanceBuildManagementPath() { @@ -1258,30 +1296,31 @@ internal ManagementPath SetWmiInstanceBuildManagementPath() var wmiInstance = this as SetWmiInstance; if (wmiInstance != null) { - //If Class is specified only CreateOnly flag is supported + // If Class is specified only CreateOnly flag is supported if (wmiInstance.Class != null) { if (wmiInstance.flagSpecified && wmiInstance.PutType != PutType.CreateOnly) { - //Throw Terminating error + // Throw Terminating error ThrowTerminatingError(new ErrorRecord( new InvalidOperationException(), "CreateOnlyFlagNotSpecifiedWithClassPath", ErrorCategory.InvalidOperation, wmiInstance.PutType)); } + wmiInstance.PutType = PutType.CreateOnly; } else { mPath = new ManagementPath(wmiInstance.Path); - if (String.IsNullOrEmpty(mPath.NamespacePath)) + if (string.IsNullOrEmpty(mPath.NamespacePath)) { mPath.NamespacePath = wmiInstance.Namespace; } else if (wmiInstance.namespaceSpecified) { - //ThrowTerminatingError + // ThrowTerminatingError ThrowTerminatingError(new ErrorRecord( new InvalidOperationException(), "NamespaceSpecifiedWithPath", @@ -1291,24 +1330,26 @@ internal ManagementPath SetWmiInstanceBuildManagementPath() if (mPath.Server != "." && wmiInstance.serverNameSpecified) { - //ThrowTerminatingError + // ThrowTerminatingError ThrowTerminatingError(new ErrorRecord( new InvalidOperationException(), "ComputerNameSpecifiedWithPath", ErrorCategory.InvalidOperation, wmiInstance.ComputerName)); } + if (mPath.IsClass) { if (wmiInstance.flagSpecified && wmiInstance.PutType != PutType.CreateOnly) { - //Throw Terminating error + // Throw Terminating error ThrowTerminatingError(new ErrorRecord( new InvalidOperationException(), "CreateOnlyFlagNotSpecifiedWithClassPath", ErrorCategory.InvalidOperation, wmiInstance.PutType)); } + wmiInstance.PutType = PutType.CreateOnly; } else @@ -1317,7 +1358,7 @@ internal ManagementPath SetWmiInstanceBuildManagementPath() { if (!(wmiInstance.PutType == PutType.UpdateOnly || wmiInstance.PutType == PutType.UpdateOrCreate)) { - //Throw terminating error + // Throw terminating error ThrowTerminatingError(new ErrorRecord( new InvalidOperationException(), "NonUpdateFlagSpecifiedWithInstancePath", @@ -1332,45 +1373,47 @@ internal ManagementPath SetWmiInstanceBuildManagementPath() } } } + return mPath; } /// - /// Set wmi instance helper for pipeline input + /// Set wmi instance helper for pipeline input. /// internal ManagementObject SetWmiInstanceGetPipelineObject() { - //Should only be called from Set-WMIInstance cmdlet + // Should only be called from Set-WMIInstance cmdlet ManagementObject mObj = null; var wmiInstance = this as SetWmiInstance; if (wmiInstance != null) { - //Extra check + // Extra check if (wmiInstance.InputObject != null) { if (wmiInstance.InputObject.GetType() == typeof(ManagementClass)) { - //Check if Flag specified is CreateOnly or not + // Check if Flag specified is CreateOnly or not if (wmiInstance.flagSpecified && wmiInstance.PutType != PutType.CreateOnly) { - //Throw terminating error + // Throw terminating error ThrowTerminatingError(new ErrorRecord( new InvalidOperationException(), "CreateOnlyFlagNotSpecifiedWithClassPath", ErrorCategory.InvalidOperation, wmiInstance.PutType)); } + mObj = ((ManagementClass)wmiInstance.InputObject).CreateInstance(); wmiInstance.PutType = PutType.CreateOnly; } else { - //Check if Flag specified is Updateonly or UpdateOrCreateOnly or not + // Check if Flag specified is Updateonly or UpdateOrCreateOnly or not if (wmiInstance.flagSpecified) { if (!(wmiInstance.PutType == PutType.UpdateOnly || wmiInstance.PutType == PutType.UpdateOrCreate)) { - //Throw terminating error + // Throw terminating error ThrowTerminatingError(new ErrorRecord( new InvalidOperationException(), "NonUpdateFlagSpecifiedWithInstancePath", @@ -1385,6 +1428,7 @@ internal ManagementObject SetWmiInstanceGetPipelineObject() mObj = (ManagementObject)wmiInstance.InputObject.Clone(); } + if (wmiInstance.Arguments != null) { IDictionaryEnumerator en = wmiInstance.Arguments.GetEnumerator(); @@ -1395,6 +1439,7 @@ internal ManagementObject SetWmiInstanceGetPipelineObject() } } } + return mObj; } @@ -1408,6 +1453,7 @@ internal void RunAsJob(string cmdletName) { ((System.Management.Automation.Runspaces.LocalRunspace)_context.CurrentRunspace).JobRepository.Add(wmiJob); } + WriteObject(wmiJob); } // Get the PowerShell execution context if it's available at cmdlet creation time... @@ -1416,7 +1462,7 @@ internal void RunAsJob(string cmdletName) #endregion Command code } /// - /// A class to perform async operations for WMI cmdlets + /// A class to perform async operations for WMI cmdlets. /// internal class PSWmiJob : Job @@ -1424,7 +1470,7 @@ internal class PSWmiJob : Job #region internal constructor /// - ///Internal constructor for initializing WMI jobs + ///Internal constructor for initializing WMI jobs. /// internal PSWmiJob(Cmdlet cmds, string[] computerName, int throttleLimt, string command) : base(command, null) @@ -1438,6 +1484,7 @@ internal PSWmiJob(Cmdlet cmds, string[] computerName, int throttleLimt, string c job.JobUnblocked += new EventHandler(HandleJobUnblocked); ChildJobs.Add(job); } + CommonInit(throttleLimt); } @@ -1476,7 +1523,7 @@ internal PSWmiJob(Cmdlet cmds, string[] computerName, int throttleLimit, string private const string WMIJobType = "WmiJob"; /// - /// Handles the StateChanged event from each of the child job objects + /// Handles the StateChanged event from each of the child job objects. /// /// /// @@ -1494,14 +1541,15 @@ private void HandleChildJobStateChanged(object sender, JobStateEventArgs e) return; } - //Ignore state changes which are not resulting in state change to finished. + // Ignore state changes which are not resulting in state change to finished. if ((!IsFinishedState(e.JobStateInfo.State)) || (e.JobStateInfo.State == JobState.NotStarted)) { return; } + if (e.JobStateInfo.State == JobState.Failed) { - //If any of the child job failed, we set status to failed + // If any of the child job failed, we set status to failed _atleastOneChildJobFailed = true; } @@ -1510,17 +1558,18 @@ private void HandleChildJobStateChanged(object sender, JobStateEventArgs e) { _finishedChildJobsCount++; - //We are done + // We are done if (_finishedChildJobsCount == ChildJobs.Count) { allChildJobsFinished = true; } } + if (allChildJobsFinished) { - //if any child job failed, set status to failed - //If stop was called set, status to stopped - //else completed + // if any child job failed, set status to failed + // If stop was called set, status to stopped + // else completed if (_atleastOneChildJobFailed) { SetJobState(JobState.Failed); @@ -1539,7 +1588,7 @@ private void HandleChildJobStateChanged(object sender, JobStateEventArgs e) private bool _stopIsCalled = false; private string _statusMessage; /// - /// Message indicating status of the job + /// Message indicating status of the job. /// public override string StatusMessage { @@ -1548,19 +1597,19 @@ public override string StatusMessage return _statusMessage; } } - //ISSUE: Implement StatusMessage + // ISSUE: Implement StatusMessage /// - /// Checks the status of remote command execution + /// Checks the status of remote command execution. /// [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] private void SetStatusMessage() { _statusMessage = "test"; - } // SetStatusMessage + } private bool _moreData = false; /// - /// indicates if more data is available + /// Indicates if more data is available. /// /// /// This has more data if any of the child jobs have more data. @@ -1570,10 +1619,10 @@ public override bool HasMoreData get { // moreData is set to false and will be set to true - //if at least one child is has more data. + // if at least one child is has more data. - //if ( (!moreData)) - //{ + // if ( (!moreData)) + // { bool atleastOneChildHasMoreData = false; for (int i = 0; i < ChildJobs.Count; i++) @@ -1586,14 +1635,14 @@ public override bool HasMoreData } _moreData = atleastOneChildHasMoreData; - //} + // } return _moreData; } } /// - /// Computers on which this job is running + /// Computers on which this job is running. /// public override string Location { @@ -1602,25 +1651,27 @@ public override string Location return ConstructLocation(); } } - private String ConstructLocation() + + private string ConstructLocation() { StringBuilder location = new StringBuilder(); foreach (PSWmiChildJob job in ChildJobs) { location.Append(job.Location); - location.Append(","); + location.Append(','); } + location.Remove(location.Length - 1, 1); return location.ToString(); } /// - /// Stop Job + /// Stop Job. /// public override void StopJob() { - //AssertNotDisposed(); + // AssertNotDisposed(); if (!IsFinishedState(JobStateInfo.State)) { @@ -1650,6 +1701,7 @@ protected override void Dispose(bool disposing) { StopJob(); } + _throttleManager.Dispose(); foreach (Job job in ChildJobs) { @@ -1666,12 +1718,12 @@ protected override void Dispose(bool disposing) private bool _isDisposed = false; /// - /// Initialization common to both constructors + /// Initialization common to both constructors. /// private void CommonInit(int throttleLimit) { - //Since no results are produced by any streams. We should - //close all the streams + // Since no results are produced by any streams. We should + // close all the streams base.CloseAllStreams(); // set status to "in progress" @@ -1683,9 +1735,9 @@ private void CommonInit(int throttleLimit) /// /// Handles JobUnblocked event from a child job and decrements /// count of blocked child jobs. When count reaches 0, sets the - /// state of the parent job to running + /// state of the parent job to running. /// - /// sender of this event, unused + /// Sender of this event, unused. /// event arguments, should be empty in this /// case private void HandleJobUnblocked(object sender, EventArgs eventArgs) @@ -1708,22 +1760,20 @@ private void HandleJobUnblocked(object sender, EventArgs eventArgs) } } - - private ThrottleManager _throttleManager = new ThrottleManager(); private object _syncObject = new object(); // sync object } /// - /// Class for WmiChildJob object. This job object Execute wmi cmdlet + /// Class for WmiChildJob object. This job object Execute wmi cmdlet. /// internal class PSWmiChildJob : Job { #region internal constructor /// - /// Internal constructor for initializing WMI jobs + /// Internal constructor for initializing WMI jobs. /// internal PSWmiChildJob(Cmdlet cmds, string computerName, ThrottleManager throttleManager) : base(null, null) @@ -1776,25 +1826,24 @@ internal PSWmiChildJob(Cmdlet cmds, string computerName, ThrottleManager throttl #endregion internal constructor private WmiAsyncCmdletHelper _helper; - //bool _bFinished; + // bool _bFinished; private ThrottleManager _throttleManager; private object _syncObject = new object(); // sync object private int _sinkCompleted; private bool _bJobFailed; private bool _bAtLeastOneObject; - private ArrayList _wmiSinkArray; /// /// Event raised by this job to indicate to its parent that - /// its now unblocked by the user + /// its now unblocked by the user. /// internal event EventHandler JobUnblocked; /// /// Set the state of the current job from blocked to /// running and raise an event indicating to this - /// parent job that this job is unblocked + /// parent job that this job is unblocked. /// [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] internal void UnblockJob() @@ -1802,6 +1851,7 @@ internal void UnblockJob() SetJobState(JobState.Running, null); JobUnblocked.SafeInvoke(this, EventArgs.Empty); } + internal ManagementOperationObserver GetNewSink() { ManagementOperationObserver wmiSink = new ManagementOperationObserver(); @@ -1810,13 +1860,14 @@ internal ManagementOperationObserver GetNewSink() { _sinkCompleted++; } + wmiSink.ObjectReady += new ObjectReadyEventHandler(this.NewObject); wmiSink.Completed += new CompletedEventHandler(this.JobDone); return wmiSink; } /// - /// it receives Management objects + /// It receives Management objects. /// private void NewObject(object sender, ObjectReadyEventArgs obj) { @@ -1824,6 +1875,7 @@ private void NewObject(object sender, ObjectReadyEventArgs obj) { _bAtLeastOneObject = true; } + this.WriteObject(obj.NewObject); } @@ -1836,14 +1888,16 @@ private void JobDone(object sender, CompletedEventArgs obj) { _sinkCompleted--; } + if (obj.Status != ManagementStatus.NoError) { _bJobFailed = true; } + if (_sinkCompleted == 0) { - //Notify throttle manager and change the state to complete - //Two cases where _bFinished should be set to false. + // Notify throttle manager and change the state to complete + // Two cases where _bFinished should be set to false. // 1) Invalid class or some other condition so that after making a connection WMI is throwing an error // 2) We could not get any instance for the class. /*if(bAtLeastOneObject ) @@ -1863,7 +1917,7 @@ private void JobDone(object sender, CompletedEventArgs obj) } /// - /// It is called when the call to Win32shutdown is successfully completed + /// It is called when the call to Win32shutdown is successfully completed. /// private void JobDoneForWin32Shutdown(object sender, EventArgs arg) { @@ -1871,6 +1925,7 @@ private void JobDoneForWin32Shutdown(object sender, EventArgs arg) { _sinkCompleted--; } + if (_sinkCompleted == 0) { _helper.RaiseOperationCompleteEvent(null, OperationState.StopComplete); @@ -1880,14 +1935,13 @@ private void JobDoneForWin32Shutdown(object sender, EventArgs arg) } /// - /// Message indicating status of the job + /// Message indicating status of the job. /// public override string StatusMessage { get; } = "test"; - /// /// Indicates if there is more data available in - /// this Job + /// this Job. /// public override bool HasMoreData { @@ -1899,12 +1953,12 @@ public override bool HasMoreData /// /// Returns the computer on which this command is - /// running + /// running. /// public override string Location { get; } /// - /// Stops the job + /// Stops the job. /// public override void StopJob() { @@ -1936,10 +1990,11 @@ protected override void Dispose(bool disposing) } } } + private bool _isDisposed; /// - /// Handles operation complete event + /// Handles operation complete event. /// private void HandleOperationComplete(object sender, OperationStateEventArgs stateEventArgs) { @@ -1947,7 +2002,7 @@ private void HandleOperationComplete(object sender, OperationStateEventArgs stat if (helper.State == WmiState.NotStarted) { - //This is a case WMI operation was not started. + // This is a case WMI operation was not started. SetJobState(JobState.Stopped, helper.InternalException); } else if (helper.State == WmiState.Running) @@ -1968,7 +2023,7 @@ private void HandleOperationComplete(object sender, OperationStateEventArgs stat } } /// - /// Handles WMI state changed + /// Handles WMI state changed. /// private void HandleWMIState(object sender, WmiJobStateEventArgs stateEventArgs) { @@ -1995,15 +2050,15 @@ private void HandleWMIState(object sender, WmiJobStateEventArgs stateEventArgs) } /// - /// Handle a throttle complete event + /// Handle a throttle complete event. /// - /// sender of this event - /// not used in this method + /// Sender of this event. + /// Not used in this method. private void HandleThrottleComplete(object sender, EventArgs eventArgs) { if (_helper.State == WmiState.NotStarted) { - //This is a case WMI operation was not started. + // This is a case WMI operation was not started. SetJobState(JobState.Stopped, _helper.InternalException); } else if (_helper.State == WmiState.Running) @@ -2022,7 +2077,7 @@ private void HandleThrottleComplete(object sender, EventArgs eventArgs) { SetJobState(JobState.Stopped, _helper.InternalException); } - //Do Nothing - } // HandleThrottleComplete + // Do Nothing + } } } diff --git a/src/Microsoft.PowerShell.Commands.Management/commands/management/WebServiceProxy.cs b/src/Microsoft.PowerShell.Commands.Management/commands/management/WebServiceProxy.cs index 30837a5c4a1..3f91d597e57 100644 --- a/src/Microsoft.PowerShell.Commands.Management/commands/management/WebServiceProxy.cs +++ b/src/Microsoft.PowerShell.Commands.Management/commands/management/WebServiceProxy.cs @@ -1,39 +1,39 @@ -/********************************************************************-- -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. using System; -using System.IO; -using System.Net; -using System.Xml; -using System.Text; using System.CodeDom; using System.CodeDom.Compiler; -using System.Web.Services; -using System.Web.Services.Description; -using System.Web.Services.Discovery; -using System.Management; -using System.Management.Automation; -using System.Diagnostics.CodeAnalysis; -using System.Reflection; -using Microsoft.CSharp; using System.Collections; using System.Collections.Generic; using System.Collections.Specialized; +using System.Diagnostics.CodeAnalysis; using System.Globalization; -using Dbg = System.Management.Automation; -using System.Runtime.InteropServices; +using System.IO; +using System.Management; +using System.Management.Automation; +using System.Net; +using System.Reflection; using System.Resources; -using Microsoft.Win32; +using System.Runtime.InteropServices; +using System.Text; using System.Text.RegularExpressions; +using System.Web.Services; +using System.Web.Services.Description; +using System.Web.Services.Discovery; +using System.Xml; +using Microsoft.CSharp; +using Microsoft.Win32; + +using Dbg = System.Management.Automation; namespace Microsoft.PowerShell.Commands { #region New-WebServiceProxy /// - /// Cmdlet for new-WebService Proxy + /// Cmdlet for new-WebService Proxy. /// [Cmdlet(VerbsCommon.New, "WebServiceProxy", DefaultParameterSetName = "NoCredentials", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=135238")] public sealed class NewWebServiceProxy : PSCmdlet @@ -41,7 +41,7 @@ public sealed class NewWebServiceProxy : PSCmdlet #region Parameters /// - /// URI of the web service + /// URI of the web service. /// [Parameter(Mandatory = true, Position = 0)] [ValidateNotNullOrEmpty] @@ -49,15 +49,17 @@ public sealed class NewWebServiceProxy : PSCmdlet public System.Uri Uri { get { return _uri; } + set { _uri = value; } } + private System.Uri _uri; /// - /// Parameter Class name + /// Parameter Class name. /// [Parameter(Position = 1)] [ValidateNotNullOrEmpty] @@ -65,15 +67,17 @@ public System.Uri Uri public string Class { get { return _class; } + set { _class = value; } } + private string _class; /// - /// namespace + /// Namespace. /// [Parameter(Position = 2)] [ValidateNotNullOrEmpty] @@ -81,15 +85,17 @@ public string Class public string Namespace { get { return _namespace; } + set { _namespace = value; } } + private string _namespace; /// - /// Credential + /// Credential. /// [Parameter(ParameterSetName = "Credential")] [ValidateNotNullOrEmpty] @@ -98,16 +104,17 @@ public string Namespace public PSCredential Credential { get { return _credential; } + set { _credential = value; } } - private PSCredential _credential; + private PSCredential _credential; /// - /// use default credential.. + /// Use default credential.. /// [Parameter(ParameterSetName = "UseDefaultCredential")] [ValidateNotNull] @@ -115,11 +122,13 @@ public PSCredential Credential public SwitchParameter UseDefaultCredential { get { return _usedefaultcredential; } + set { _usedefaultcredential = value; } } + private SwitchParameter _usedefaultcredential; #endregion @@ -136,27 +145,27 @@ public SwitchParameter UseDefaultCredential private static Dictionary s_srccodeCache = new Dictionary(); /// - /// holds the hash code of the source generated. + /// Holds the hash code of the source generated. /// private int _sourceHash; /// - /// Random class + /// Random class. /// private object _cachelock = new object(); private static Random s_rnd = new Random(); /// - /// BeginProcessing code + /// BeginProcessing code. /// protected override void BeginProcessing() { - if (_uri.ToString().Trim().Length == 0) + if (string.IsNullOrWhiteSpace(_uri.ToString())) { Exception ex = new ArgumentException(WebServiceResources.InvalidUri); ErrorRecord er = new ErrorRecord(ex, "ArgumentException", ErrorCategory.InvalidOperation, null); ThrowTerminatingError(er); } - //check if system.web is available.This assembly is not available in win server core. + // check if system.web is available.This assembly is not available in win server core. string AssemblyString = "System.Web, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"; try { @@ -175,7 +184,7 @@ protected override void BeginProcessing() { if (s_uriCache.ContainsKey(_uri)) { - //if uri is present in the cache + // if uri is present in the cache string ns; s_uriCache.TryGetValue(_uri, out ns); string[] data = ns.Split('|'); @@ -187,14 +196,16 @@ protected override void BeginProcessing() _class = data[1]; } } + sourceCache = Int32.Parse(data[2].ToString(), CultureInfo.InvariantCulture); } } + if (string.IsNullOrEmpty(_namespace)) { _namespace = "Microsoft.PowerShell.Commands.NewWebserviceProxy.AutogeneratedTypes.WebServiceProxy" + GenerateRandomName(); } - //if class is null,generate a name for it + // if class is null,generate a name for it if (string.IsNullOrEmpty(_class)) { _class = "MyClass" + GenerateRandomName(); @@ -203,9 +214,9 @@ protected override void BeginProcessing() Assembly webserviceproxy = GenerateWebServiceProxyAssembly(_namespace, _class); if (webserviceproxy == null) return; - Object instance = InstantiateWebServiceProxy(webserviceproxy); + object instance = InstantiateWebServiceProxy(webserviceproxy); - //to set the credentials into the generated webproxy Object + // to set the credentials into the generated webproxy Object PropertyInfo[] pinfo = instance.GetType().GetProperties(); foreach (PropertyInfo pr in pinfo) { @@ -217,6 +228,7 @@ protected override void BeginProcessing() pr.SetValue(instance, flag as object, null); } } + if (pr.Name.Equals("Credentials", StringComparison.OrdinalIgnoreCase)) { if (Credential != null) @@ -227,12 +239,13 @@ protected override void BeginProcessing() } } - //disposing the entries in a cache - //Adding to Cache + // disposing the entries in a cache + // Adding to Cache lock (s_uriCache) { s_uriCache.Remove(_uri); } + if (sourceCache > 0) { lock (_cachelock) @@ -240,18 +253,20 @@ protected override void BeginProcessing() s_srccodeCache.Remove(sourceCache); } } + string key = string.Join("|", new string[] { _namespace, _class, _sourceHash.ToString(System.Globalization.CultureInfo.InvariantCulture) }); lock (s_uriCache) { s_uriCache.Add(_uri, key); } + lock (_cachelock) { s_srccodeCache.Add(_sourceHash, instance); } WriteObject(instance, true); - }//End BeginProcessing() + } #endregion @@ -261,9 +276,9 @@ protected override void BeginProcessing() private static object s_sequenceNumberLock = new object(); /// - /// Generates a random name + /// Generates a random name. /// - /// string + /// String. private string GenerateRandomName() { string rndname = null; @@ -291,11 +306,12 @@ private string GenerateRandomName() { return (sequenceString + rndname.Substring(rndname.Length - 30)); } + return (sequenceString + rndname); } /// - /// Generates the Assembly + /// Generates the Assembly. /// /// /// @@ -305,11 +321,11 @@ private Assembly GenerateWebServiceProxyAssembly(string NameSpace, string ClassN { DiscoveryClientProtocol dcp = new DiscoveryClientProtocol(); - //if paramset is defaultcredential, set the flag in wcclient + // if paramset is defaultcredential, set the flag in wcclient if (_usedefaultcredential.IsPresent) dcp.UseDefaultCredentials = true; - //if paramset is credential, assign the credentials + // if paramset is credential, assign the credentials if (ParameterSetName.Equals("Credential", StringComparison.OrdinalIgnoreCase)) dcp.Credentials = _credential.GetNetworkCredential(); @@ -339,7 +355,7 @@ private Assembly GenerateWebServiceProxyAssembly(string NameSpace, string ClassN if (!string.IsNullOrEmpty(NameSpace)) codeNS.Name = NameSpace; - //create the class and add it to the namespace + // create the class and add it to the namespace if (!string.IsNullOrEmpty(ClassName)) { CodeTypeDeclaration codeClass = new CodeTypeDeclaration(ClassName); @@ -348,12 +364,12 @@ private Assembly GenerateWebServiceProxyAssembly(string NameSpace, string ClassN codeNS.Types.Add(codeClass); } - //create a web reference to the uri docs + // create a web reference to the uri docs WebReference wref = new WebReference(dcp.Documents, codeNS); WebReferenceCollection wrefs = new WebReferenceCollection(); wrefs.Add(wref); - //create a codecompileunit and add the namespace to it + // create a codecompileunit and add the namespace to it CodeCompileUnit codecompileunit = new CodeCompileUnit(); codecompileunit.Namespaces.Add(codeNS); @@ -361,7 +377,7 @@ private Assembly GenerateWebServiceProxyAssembly(string NameSpace, string ClassN wrefOptions.CodeGenerationOptions = System.Xml.Serialization.CodeGenerationOptions.GenerateNewAsync | System.Xml.Serialization.CodeGenerationOptions.GenerateOldAsync | System.Xml.Serialization.CodeGenerationOptions.GenerateProperties; wrefOptions.Verbose = true; - //create a csharpprovider and compile it + // create a csharpprovider and compile it CSharpCodeProvider csharpprovider = new CSharpCodeProvider(); StringCollection Warnings = ServiceDescriptionImporter.GenerateWebReferences(wrefs, csharpprovider, codecompileunit, wrefOptions); @@ -376,10 +392,10 @@ private Assembly GenerateWebServiceProxyAssembly(string NameSpace, string ClassN ErrorRecord er = new ErrorRecord(ex, "NotImplementedException", ErrorCategory.ObjectNotFound, _uri); WriteError(er); } - //generate the hashcode of the CodeCompileUnit + // generate the hashcode of the CodeCompileUnit _sourceHash = codegenerator.ToString().GetHashCode(); - //if the sourcehash matches the hashcode in the cache,the proxy hasnt changed and so + // if the sourcehash matches the hashcode in the cache,the proxy hasnt changed and so // return the instance of th eproxy in the cache if (s_srccodeCache.ContainsKey(_sourceHash)) { @@ -388,6 +404,7 @@ private Assembly GenerateWebServiceProxyAssembly(string NameSpace, string ClassN WriteObject(obj, true); return null; } + CompilerParameters options = new CompilerParameters(); CompilerResults results = null; @@ -421,7 +438,7 @@ private Assembly GenerateWebServiceProxyAssembly(string NameSpace, string ClassN } /// - /// Function to add all the assemblies required to generate the web proxy + /// Function to add all the assemblies required to generate the web proxy. /// /// /// @@ -440,24 +457,28 @@ private void GetReferencedAssemblies(Assembly assembly, CompilerParameters param } /// /// Instantiates the object - /// if a type of WebServiceBindingAttribute is not found, throw an exception + /// if a type of WebServiceBindingAttribute is not found, throw an exception. /// /// /// private object InstantiateWebServiceProxy(Assembly assembly) { Type proxyType = null; - //loop through the types of the assembly and identify the type having + // loop through the types of the assembly and identify the type having // a web service binding attribute foreach (Type type in assembly.GetTypes()) { - Object[] obj = type.GetCustomAttributes(typeof(WebServiceBindingAttribute), false); + object[] obj = type.GetCustomAttributes(typeof(WebServiceBindingAttribute), false); if (obj.Length > 0) { proxyType = type; break; } - if (proxyType != null) break; + + if (proxyType != null) + { + break; + } } System.Management.Automation.Diagnostics.Assert( @@ -468,6 +489,6 @@ private object InstantiateWebServiceProxy(Assembly assembly) } #endregion - }//end class + } #endregion -}//Microsoft.Powershell.commands +} diff --git a/src/Microsoft.PowerShell.Commands.Management/commands/management/WriteContentCommandBase.cs b/src/Microsoft.PowerShell.Commands.Management/commands/management/WriteContentCommandBase.cs index 20fdab66dcf..a2a6387e9da 100644 --- a/src/Microsoft.PowerShell.Commands.Management/commands/management/WriteContentCommandBase.cs +++ b/src/Microsoft.PowerShell.Commands.Management/commands/management/WriteContentCommandBase.cs @@ -1,6 +1,5 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. using System; using System.Collections; @@ -8,7 +7,6 @@ using System.Collections.ObjectModel; using System.Management.Automation; using System.Management.Automation.Provider; -using Dbg = System.Management.Automation; namespace Microsoft.PowerShell.Commands { @@ -22,11 +20,9 @@ public class WriteContentCommandBase : PassThroughContentCommandBase /// /// The value of the content to set. /// - /// /// /// This value type is determined by the InvokeProvider. /// - /// [Parameter(Mandatory = true, Position = 1, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true)] [AllowNull] [AllowEmptyCollection] @@ -35,13 +31,13 @@ public object[] Value get { return _content; - } // get + } set { _content = value; } - } // Value + } #endregion Parameters @@ -60,7 +56,6 @@ public object[] Value /// This bool is used to determine if the path /// parameter was specified on the command line or via the pipeline. /// - /// private bool _pipingPaths; /// @@ -77,7 +72,7 @@ public object[] Value /// /// Determines if the paths are specified on the command line - /// or being piped in + /// or being piped in. /// protected override void BeginProcessing() { @@ -100,10 +95,7 @@ protected override void ProcessRecord() // Initialize the content - if (_content == null) - { - _content = new object[0]; - } + _content ??= Array.Empty(); if (_pipingPaths) { @@ -148,7 +140,7 @@ protected override void ProcessRecord() catch (Exception e) // Catch-all OK. 3rd party callout { ProviderInvocationException providerException = - new ProviderInvocationException( + new( "ProviderContentWriteError", SessionStateStrings.ProviderContentWriteError, holder.PathInfo.Provider, @@ -188,16 +180,15 @@ protected override void ProcessRecord() contentStreams = new List(); } } - } // ProcessRecord + } /// - /// Closes all the content writers + /// Closes all the content writers. /// - /// protected override void EndProcessing() { Dispose(true); - } // EndProcessing + } #endregion Command code @@ -208,24 +199,20 @@ protected override void EndProcessing() /// from the provider. If the current position needs to be changed before writing /// the content, this method should be overridden to do that. /// - /// /// /// The content holders that contain the writers to be moved. /// - /// internal virtual void SeekContentPosition(List contentHolders) { // default does nothing. - } // SeekContentPosition + } /// /// Called by the base class before the streams are open for the path. /// - /// /// /// The path to the items that will be opened for writing content. /// - /// internal virtual void BeforeOpenStreams(string[] paths) { } @@ -235,33 +222,29 @@ internal virtual void BeforeOpenStreams(string[] paths) /// that require dynamic parameters should override this method and return the /// dynamic parameter object. /// - /// /// /// The context under which the command is running. /// - /// /// /// An object representing the dynamic parameters for the cmdlet or null if there /// are none. /// - /// internal override object GetDynamicParameters(CmdletProviderContext context) { if (Path != null && Path.Length > 0) { return InvokeProvider.Content.GetContentWriterDynamicParameters(Path[0], context); } + return InvokeProvider.Content.GetContentWriterDynamicParameters(".", context); } /// /// Gets the IContentWriters for the current path(s) /// - /// /// /// An array of IContentWriters for the current path(s) /// - /// internal List GetContentWriters( string[] writerPaths, CmdletProviderContext currentCommandContext) @@ -272,7 +255,7 @@ internal List GetContentWriters( // Create the results array - List results = new List(); + List results = new(); foreach (PathInfo pathInfo in pathInfos) { @@ -325,27 +308,27 @@ internal List GetContentWriters( if (writers.Count == 1 && writers[0] != null) { ContentHolder holder = - new ContentHolder(pathInfo, null, writers[0]); + new(pathInfo, null, writers[0]); results.Add(holder); } } - } // foreach pathInfo in pathInfos + } return results; - } // GetContentWriters + } /// - /// Gets the list of paths accepted by the user + /// Gets the list of paths accepted by the user. /// - /// The list of unfiltered paths - /// The current context - /// The list of paths accepted by the user + /// The list of unfiltered paths. + /// The current context. + /// The list of paths accepted by the user. private string[] GetAcceptedPaths(string[] unfilteredPaths, CmdletProviderContext currentContext) { Collection pathInfos = ResolvePaths(unfilteredPaths, true, false, currentContext); - ArrayList paths = new ArrayList(); + var paths = new List(); foreach (PathInfo pathInfo in pathInfos) { @@ -355,9 +338,9 @@ private string[] GetAcceptedPaths(string[] unfilteredPaths, CmdletProviderContex } } - return (string[])paths.ToArray(typeof(String)); + return paths.ToArray(); } #endregion protected members - } // WriteContentCommandBase -} // namespace Microsoft.PowerShell.Commands + } +} diff --git a/src/Microsoft.PowerShell.Commands.Management/resources/ClipboardResources.resx b/src/Microsoft.PowerShell.Commands.Management/resources/ClipboardResources.resx index e306a959940..9e6cbd55cc1 100644 --- a/src/Microsoft.PowerShell.Commands.Management/resources/ClipboardResources.resx +++ b/src/Microsoft.PowerShell.Commands.Management/resources/ClipboardResources.resx @@ -131,4 +131,22 @@ Html can only be combined with Html Text format. + + Only Text format is supported on this platform. + + + The clipboard is not supported on this platform. + + + The '-AsHtml' switch is not supported on this platform. + + + The '-TextFormatType' parameter only supports 'Text' on this platform. + + + The '-Path' parameter is not supported on this platform. + + + The '-LiteralPath' parameter is not supported on this platform. + diff --git a/src/Microsoft.PowerShell.Commands.Management/resources/CmdletizationResources.resx b/src/Microsoft.PowerShell.Commands.Management/resources/CmdletizationResources.resx index 1a25e938a38..15bfadffe7b 100644 --- a/src/Microsoft.PowerShell.Commands.Management/resources/CmdletizationResources.resx +++ b/src/Microsoft.PowerShell.Commands.Management/resources/CmdletizationResources.resx @@ -124,10 +124,6 @@ {1} is a placeholder for a server name. Example: "localhost". - - ..\..\..\..\src\cimSupport\cmdletization\xml\cmdlets-over-objects.xsd;System.String, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089;utf-8 - {Locked} - CIM method {1} on the {0} CIM object {0} is a placeholder for a CIM path. Example: \\SERVER1\ROOT\cimv2:Win32_Process.Handle="11828" diff --git a/src/Microsoft.PowerShell.Commands.Management/resources/ComputerResources.resx b/src/Microsoft.PowerShell.Commands.Management/resources/ComputerResources.resx index aaa61c80568..95685239fd7 100644 --- a/src/Microsoft.PowerShell.Commands.Management/resources/ComputerResources.resx +++ b/src/Microsoft.PowerShell.Commands.Management/resources/ComputerResources.resx @@ -153,9 +153,6 @@ The command cannot locate the "{0}" restore point. Verify the "{0}" sequence number, and then try the command again. - - Testing connection to computer '{0}' failed: {1} - {0} ({1}) @@ -238,7 +235,7 @@ Verifying that the computer has been restarted... - Waiting for Windows PowerShell connectivity... + Waiting for PowerShell connectivity... Waiting for the restart to begin... @@ -357,18 +354,6 @@ The computer {0} is skipped. Fail to retrieve its LastBootUpTime via the WMI service with the following error message: {1}. - - Parameter WsmanAuthentication should not be specified when the DCOM protocol is in use. {0} - - - Parameters DcomAuthentication and Impersonation should not be specified when the WSMan protocol is in use. {0} - - - Parameter WsmanAuthentication should not be specified with DcomAuthentication and Impersonation at the same time. {0} - - - Parameter WsmanAuthentication is valid only if the WSMan protocol is used. Parameters DcomAuthentication (Authentication) and Impersonation are valid only if the DCOM protocol is used. - Cannot verify the secure channel for the local computer. Operation failed with the following exception: {0}. @@ -396,28 +381,13 @@ Cannot validate the time interval for restore point creation. It failed to retrieve the last restore point with the following error message: {0}. - - Parameter WsmanAuthentication cannot be specified with the DCOM protocol. Parameter WSManAuthentication is valid only when the WSMan protocol is used. - - - Parameters DcomAuthentication and Impersonation cannot be specified with the WSMan protocol. {0} - - - Parameter WsmanAuthentication is valid only when the WSMan protocol is used. Parameters DcomAuthentication and Impersonation are valid only when the DCOM protocol is used. - - - Parameter WsmanAuthentication cannot be specified with DcomAuthentication or Impersonation parameters. {0} - - - Parameter WsmanAuthentication cannot be specified with the DCOM protocol. {0} - - - DcomAuthentication is not supported. Please use WsmanAuthentication instead. - The AsJob Parameter Set is not supported. The {0} parameter is not supported for CoreCLR. + + The required native command 'shutdown' was not found. + diff --git a/src/Microsoft.PowerShell.Commands.Management/resources/EventlogResources.resx b/src/Microsoft.PowerShell.Commands.Management/resources/EventlogResources.resx index 70ef2ba399d..cee97f4adae 100644 --- a/src/Microsoft.PowerShell.Commands.Management/resources/EventlogResources.resx +++ b/src/Microsoft.PowerShell.Commands.Management/resources/EventlogResources.resx @@ -166,7 +166,7 @@ Do you want to remove the "{0}" source from the "{1}" computer? - Retention days is valid only if the overflow action is "OverwriteOlder". Please change and try again. + Retention days is valid only if the overflow action is "OverwriteOlder". Please change and try again. Specify a valid value for the number of retention days. diff --git a/src/Microsoft.PowerShell.Commands.Management/resources/HotFixResources.resx b/src/Microsoft.PowerShell.Commands.Management/resources/HotFixResources.resx index 9002dddc00c..a058ae71a11 100644 --- a/src/Microsoft.PowerShell.Commands.Management/resources/HotFixResources.resx +++ b/src/Microsoft.PowerShell.Commands.Management/resources/HotFixResources.resx @@ -1,4 +1,4 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Testing connection to computer '{0}' failed: {1} + + + Cannot resolve the target name. + + + Target IPv4/IPv6 address absent. + + + Cannot complete traceroute to destination '{0}': Number of hops required to reach host exceeds MaxHops ({1}). + + diff --git a/src/Microsoft.PowerShell.Commands.Management/resources/TestPathResources.resx b/src/Microsoft.PowerShell.Commands.Management/resources/TestPathResources.resx new file mode 100644 index 00000000000..c60c4318a64 --- /dev/null +++ b/src/Microsoft.PowerShell.Commands.Management/resources/TestPathResources.resx @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + The provided Path argument was null or an empty collection. + + diff --git a/src/Microsoft.PowerShell.Commands.Management/resources/TransactionResources.resx b/src/Microsoft.PowerShell.Commands.Management/resources/TransactionResources.resx index d319d696faf..9d92573d86d 100644 --- a/src/Microsoft.PowerShell.Commands.Management/resources/TransactionResources.resx +++ b/src/Microsoft.PowerShell.Commands.Management/resources/TransactionResources.resx @@ -1,4 +1,4 @@ - + - [Cmdlet(VerbsCommon.Get, "Random", DefaultParameterSetName = GetRandomCommand.RandomNumberParameterSet, - HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113446", RemotingCapability = RemotingCapability.None)] - [OutputType(typeof(Int32), typeof(Int64), typeof(Double))] - public class GetRandomCommand : PSCmdlet + [Cmdlet(VerbsCommon.Get, "Random", DefaultParameterSetName = GetRandomCommandBase.RandomNumberParameterSet, + HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2097016", RemotingCapability = RemotingCapability.None)] + [OutputType(typeof(int), typeof(long), typeof(double))] + public sealed class GetRandomCommand : GetRandomCommandBase { - #region Parameter set handling - - private const string RandomNumberParameterSet = "RandomNumberParameterSet"; - private const string RandomListItemParameterSet = "RandomListItemParameterSet"; - - private enum MyParameterSet - { - Unknown, - RandomNumber, - RandomListItem - } - - private MyParameterSet _effectiveParameterSet; - - private MyParameterSet EffectiveParameterSet - { - get - { - // cache MyParameterSet enum instead of doing string comparison every time - if (_effectiveParameterSet == MyParameterSet.Unknown) - { - if ((this.MyInvocation.ExpectingInput) && (this.Maximum == null) && (this.Minimum == null)) - { - _effectiveParameterSet = MyParameterSet.RandomListItem; - } - else if (ParameterSetName.Equals(GetRandomCommand.RandomListItemParameterSet, StringComparison.OrdinalIgnoreCase)) - { - _effectiveParameterSet = MyParameterSet.RandomListItem; - } - else if (this.ParameterSetName.Equals(GetRandomCommand.RandomNumberParameterSet, StringComparison.OrdinalIgnoreCase)) - { - if ((this.Maximum != null) && (this.Maximum.GetType().IsArray)) - { - this.InputObject = (object[])this.Maximum; - _effectiveParameterSet = MyParameterSet.RandomListItem; - } - else - { - _effectiveParameterSet = MyParameterSet.RandomNumber; - } - } - else - { - Debug.Assert(false, "Unrecognized parameter set"); - } - } - - return _effectiveParameterSet; - } - } - - #endregion Parameter set handling - - #region Error handling - - private void ThrowMinGreaterThanOrEqualMax(object min, object max) - { - if (min == null) - { - throw PSTraceSource.NewArgumentNullException("min"); - } - - if (max == null) - { - throw PSTraceSource.NewArgumentNullException("max"); - } - - ErrorRecord errorRecord = new ErrorRecord( - new ArgumentException(String.Format( - CultureInfo.InvariantCulture, GetRandomCommandStrings.MinGreaterThanOrEqualMax, min, max)), - "MinGreaterThanOrEqualMax", - ErrorCategory.InvalidArgument, - null); - - this.ThrowTerminatingError(errorRecord); - } - - #endregion - - #region Random generator state - - private static ReaderWriterLockSlim s_runspaceGeneratorMapLock = new ReaderWriterLockSlim(); - - // 1-to-1 mapping of runspaces and random number generators - private static Dictionary s_runspaceGeneratorMap = new Dictionary(); - - private static void CurrentRunspace_StateChanged(object sender, RunspaceStateEventArgs e) - { - switch (e.RunspaceStateInfo.State) - { - case RunspaceState.Broken: - case RunspaceState.Closed: - try - { - GetRandomCommand.s_runspaceGeneratorMapLock.EnterWriteLock(); - GetRandomCommand.s_runspaceGeneratorMap.Remove(((Runspace)sender).InstanceId); - } - finally - { - GetRandomCommand.s_runspaceGeneratorMapLock.ExitWriteLock(); - } - break; - } - } - - private PolymorphicRandomNumberGenerator _generator; - - /// - /// Gets and sets generator associated with the current runspace - /// - private PolymorphicRandomNumberGenerator Generator - { - get - { - if (_generator == null) - { - Guid runspaceId = this.Context.CurrentRunspace.InstanceId; - - bool needToInitialize = false; - try - { - GetRandomCommand.s_runspaceGeneratorMapLock.EnterReadLock(); - needToInitialize = !GetRandomCommand.s_runspaceGeneratorMap.TryGetValue(runspaceId, out _generator); - } - finally - { - GetRandomCommand.s_runspaceGeneratorMapLock.ExitReadLock(); - } - - if (needToInitialize) - { - this.Generator = new PolymorphicRandomNumberGenerator(); - } - } - - return _generator; - } - set - { - _generator = value; - Runspace myRunspace = this.Context.CurrentRunspace; - - try - { - GetRandomCommand.s_runspaceGeneratorMapLock.EnterWriteLock(); - if (!GetRandomCommand.s_runspaceGeneratorMap.ContainsKey(myRunspace.InstanceId)) - { - // make sure we won't leave the generator around after runspace exits - myRunspace.StateChanged += CurrentRunspace_StateChanged; - } - GetRandomCommand.s_runspaceGeneratorMap[myRunspace.InstanceId] = _generator; - } - finally - { - GetRandomCommand.s_runspaceGeneratorMapLock.ExitWriteLock(); - } - } - } - - #endregion - - #region Common parameters - /// - /// Seed used to reinitialize random numbers generator + /// Seed used to reinitialize random numbers generator. /// [Parameter] [ValidateNotNull] public int? SetSeed { get; set; } - #endregion Common parameters - - #region Parameters for RandomNumberParameterSet - - /// - /// Maximum number to generate - /// - [Parameter(ParameterSetName = RandomNumberParameterSet, Position = 0)] - public object Maximum { get; set; } - - /// - /// Minimum number to generate - /// - [Parameter(ParameterSetName = RandomNumberParameterSet)] - public object Minimum { get; set; } - - private bool IsInt(object o) - { - if (o == null || o is int) - { - return true; - } - return false; - } - - private bool IsInt64(object o) - { - if (o == null || o is Int64) - { - return true; - } - return false; - } - - private object ProcessOperand(object o) - { - if (o == null) - { - return null; - } - - PSObject pso = PSObject.AsPSObject(o); - object baseObject = pso.BaseObject; - - if (baseObject is string) - { - // The type argument passed in does not decide the number type we want to convert to. ScanNumber will return - // int/long/double based on the string form number passed in. - baseObject = System.Management.Automation.Language.Parser.ScanNumber((string)baseObject, typeof(int)); - } - - return baseObject; - } - - private double ConvertToDouble(object o, double defaultIfNull) - { - if (o == null) - { - return defaultIfNull; - } - - double result = (double)LanguagePrimitives.ConvertTo(o, typeof(double), CultureInfo.InvariantCulture); - return result; - } - - #endregion - - #region Parameters and variables for RandomListItemParameterSet - - private List _chosenListItems; - private int _numberOfProcessedListItems; - - /// - /// List from which random elements are chosen - /// - [Parameter(ParameterSetName = RandomListItemParameterSet, ValueFromPipeline = true, Position = 0, Mandatory = true)] - [ValidateNotNullOrEmpty] - [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] - public object[] InputObject { get; set; } - - /// - /// Number of items to output (number of list items or of numbers) - /// - [Parameter(ParameterSetName = GetRandomCommand.RandomListItemParameterSet)] - [ValidateRange(1, int.MaxValue)] - public int Count { get; set; } - - #endregion - - #region Cmdlet processing methods - - private double GetRandomDouble(double min, double max) - { - double randomNumber; - double diff = max - min; - - // I couldn't find a better fix for bug #216893 then - // to test and retry if a random number falls outside the bounds - // because of floating-point-arithmetic inaccuracies. - // - // Performance in the normal case is not impacted much. - // In low-precision situations we should converge to a solution quickly - // (diff gets smaller at a quick pace). - - if (double.IsInfinity(diff)) - { - do - { - double r = this.Generator.NextDouble(); - randomNumber = min + r * max - r * min; - } - while (randomNumber >= max); - } - else - { - do - { - double r = this.Generator.NextDouble(); - randomNumber = min + r * diff; - diff = diff * r; - } - while (randomNumber >= max); - } - - return randomNumber; - } - - /// - /// Get a random Int64 type number - /// - /// - /// - /// - private Int64 GetRandomInt64(Int64 min, Int64 max) - { - // Randomly generate eight bytes and convert the byte array to UInt64 - var buffer = new byte[sizeof(UInt64)]; - UInt64 randomUint64; - - BigInteger bigIntegerDiff = (BigInteger)max - (BigInteger)min; - - // When the difference is less than int.MaxValue, use Random.Next(int, int) - if (bigIntegerDiff <= int.MaxValue) - { - int randomDiff = this.Generator.Next(0, (int)(max - min)); - return min + randomDiff; - } - - // The difference of two Int64 numbers would not exceed UInt64.MaxValue, so it can be represented by a UInt64 number. - UInt64 uint64Diff = (UInt64)bigIntegerDiff; - - // Calculate the number of bits to represent the diff in type UInt64 - int bitsToRepresentDiff = 0; - UInt64 diffCopy = uint64Diff; - for (; diffCopy != 0; bitsToRepresentDiff++) - { - diffCopy >>= 1; - } - // Get the mask for the number of bits - UInt64 mask = (0xffffffffffffffff >> (64 - bitsToRepresentDiff)); - do - { - // Randomly fill the buffer - this.Generator.NextBytes(buffer); - randomUint64 = BitConverter.ToUInt64(buffer, 0); - // Get the last 'bitsToRepresentDiff' number of randon bits - randomUint64 &= mask; - } while (uint64Diff <= randomUint64); - - double result = min * 1.0 + randomUint64 * 1.0; - return (Int64)result; - } - /// - /// This method implements the BeginProcessing method for get-random command + /// This method implements the BeginProcessing method for get-random command. /// protected override void BeginProcessing() { - if (this.SetSeed.HasValue) - { - this.Generator = new PolymorphicRandomNumberGenerator(this.SetSeed.Value); - } - - if (this.EffectiveParameterSet == MyParameterSet.RandomNumber) - { - object maxOperand = ProcessOperand(this.Maximum); - object minOperand = ProcessOperand(this.Minimum); - - if (IsInt(maxOperand) && IsInt(minOperand)) - { - int min = minOperand != null ? (int)minOperand : 0; - int max = maxOperand != null ? (int)maxOperand : int.MaxValue; - - if (min >= max) - { - this.ThrowMinGreaterThanOrEqualMax(min, max); - } - - int randomNumber = this.Generator.Next(min, max); - Debug.Assert(min <= randomNumber, "lower bound <= random number"); - Debug.Assert(randomNumber < max, "random number < upper bound"); - - this.WriteObject(randomNumber); - } - else if ((IsInt64(maxOperand) || IsInt(maxOperand)) && (IsInt64(minOperand) || IsInt(minOperand))) - { - Int64 min = minOperand != null ? ((minOperand is Int64) ? (Int64)minOperand : (int)minOperand) : 0; - Int64 max = maxOperand != null ? ((maxOperand is Int64) ? (Int64)maxOperand : (int)maxOperand) : Int64.MaxValue; - - if (min >= max) - { - this.ThrowMinGreaterThanOrEqualMax(min, max); - } - - Int64 randomNumber = this.GetRandomInt64(min, max); - Debug.Assert(min <= randomNumber, "lower bound <= random number"); - Debug.Assert(randomNumber < max, "random number < upper bound"); - - this.WriteObject(randomNumber); - } - else - { - double min = (minOperand is double) ? (double)minOperand : this.ConvertToDouble(this.Minimum, 0.0); - double max = (maxOperand is double) ? (double)maxOperand : this.ConvertToDouble(this.Maximum, double.MaxValue); - - if (min >= max) - { - this.ThrowMinGreaterThanOrEqualMax(min, max); - } - - double randomNumber = this.GetRandomDouble(min, max); - Debug.Assert(min <= randomNumber, "lower bound <= random number"); - Debug.Assert(randomNumber < max, "random number < upper bound"); - - this.WriteObject(randomNumber); - } - } - else if (this.EffectiveParameterSet == MyParameterSet.RandomListItem) - { - _chosenListItems = new List(); - _numberOfProcessedListItems = 0; - - if (this.Count == 0) // -Count not specified - { - this.Count = 1; // default to one random item by default - } - } - } - - // rough proof that when choosing random K items out of N items - // each item has got K/N probability of being included in the final list - // - // probability that a particular item in this.chosenListItems is NOT going to be replaced - // when processing I-th input item [assumes I > K]: - // P_one_step(I) = 1 - ((K / I) * ((K - 1) / K) + ((I - K) / I) = (I - 1) / I - // <--A--> <-----B-----> <-----C-----> - // A - probability that I-th element is going to be replacing an element from this.chosenListItems - // (see (1) in the code below) - // B - probability that a particular element from this.chosenListItems is NOT going to be replaced - // (see (2) in the code below) - // C - probability that I-th element is NOT going to be replacing an element from this.chosenListItems - // (see (1) in the code below) - // - // probability that a particular item in this.chosenListItems is NOT going to be replaced - // when processing input items J through N [assumes J > K] - // P_removal(J) = Multiply(for I = J to N) P(I) = - // = ((J - 1) / J) * (J / (J + 1)) * ... * ((N - 2) / (N - 1)) * ((N - 1) / N) = - // = (J - 1) / N - // - // probability that when processing an element it is going to be put into this.chosenListItems - // P_insertion(I) = 1.0 when I <= K - see (3) in the code below - // P_insertion(I) = K/N otherwise - see (1) in the code below - // - // probability that a given element is going to be a part of the final list - // P_final(I) = P_insertion(I) * P_removal(max(I + 1, K + 1)) - // [for I <= K] = 1.0 * ((K + 1) - 1) / N = K / N - // [otherwise] = (K / I) * ((I + 1) - 1) / N = K / N - // - // which proves that P_final(I) = K / N for all values of I. QED. - - /// - /// This method implements the ProcessRecord method for get-random command - /// - protected override void ProcessRecord() - { - if (this.EffectiveParameterSet == MyParameterSet.RandomListItem) - { - foreach (object item in this.InputObject) - { - if (_numberOfProcessedListItems < this.Count) // (3) - { - Debug.Assert(_chosenListItems.Count == _numberOfProcessedListItems, "Initial K elements should all be included in this.chosenListItems"); - _chosenListItems.Add(item); - } - else - { - Debug.Assert(_chosenListItems.Count == this.Count, "After processing K initial elements, the length of this.chosenItems should stay equal to K"); - if (this.Generator.Next(_numberOfProcessedListItems + 1) < this.Count) // (1) - { - int indexToReplace = this.Generator.Next(_chosenListItems.Count); // (2) - _chosenListItems[indexToReplace] = item; - } - } - - _numberOfProcessedListItems++; - } - } - } - - /// - /// This method implements the EndProcessing method for get-random command - /// - protected override void EndProcessing() - { - if (this.EffectiveParameterSet == MyParameterSet.RandomListItem) - { - // make sure the order is truly random - // (all permutations with the same probability) - // O(n) time - int n = _chosenListItems.Count; - for (int i = 0; i < n; i++) - { - // randomly choose an item to go into the i-th position - int j = this.Generator.Next(i, n); - - // swap j-th item into i-th position - if (i != j) - { - object tmp = _chosenListItems[i]; - _chosenListItems[i] = _chosenListItems[j]; - _chosenListItems[j] = tmp; - } - } - - // output all items - foreach (object chosenItem in _chosenListItems) - { - this.WriteObject(chosenItem); - } - } - } - - #endregion Processing methods - } - - /// - /// Provides an adapter API for random numbers that may be either cryptographically random, or - /// generated with the regular pseudo-random number generator. Re-implementations of - /// methods using the NextBytes() primitive based on the CLR implementation: - /// http://referencesource.microsoft.com/#mscorlib/system/random.cs - /// - internal class PolymorphicRandomNumberGenerator - { - /// - /// Constructor - /// - public PolymorphicRandomNumberGenerator() - { - _cryptographicGenerator = RandomNumberGenerator.Create(); - _pseudoGenerator = null; - } - - internal PolymorphicRandomNumberGenerator(int seed) - { - _cryptographicGenerator = null; - _pseudoGenerator = new Random(seed); - } - - private Random _pseudoGenerator = null; - private RandomNumberGenerator _cryptographicGenerator = null; - - /// - /// Generates a random floating-point number that is greater than or equal to 0.0, and less than 1.0. - /// - /// A random floating-point number that is greater than or equal to 0.0, and less than 1.0 - internal double NextDouble() - { - // According to the CLR source: - // "Including this division at the end gives us significantly improved random number distribution." - return Next() * (1.0 / Int32.MaxValue); - } - - /// - /// Generates a non-negative random integer. - /// - /// A non-negative random integer. - internal int Next() - { - int result; - - // The CLR implementation just fudges - // Int32.MaxValue down to (Int32.MaxValue - 1). This implementation - // errs on the side of correctness. - do - { - result = InternalSample(); - } - while (result == Int32.MaxValue); - - if (result < 0) + if (SetSeed.HasValue) { - result += Int32.MaxValue; + Generator = new PolymorphicRandomNumberGenerator(SetSeed.Value); } - return result; - } - - /// - /// Returns a random integer that is within a specified range. - /// - /// The exclusive upper bound of the random number returned. - /// - internal int Next(int maxValue) - { - if (maxValue < 0) - { - throw new ArgumentOutOfRangeException("maxValue", GetRandomCommandStrings.MaxMustBeGreaterThanZeroApi); - } - - return Next(0, maxValue); - } - - /// - /// Returns a random integer that is within a specified range. - /// - /// The inclusive lower bound of the random number returned. - /// The exclusive upper bound of the random number returned. maxValue must be greater than or equal to minValue - /// - public int Next(int minValue, int maxValue) - { - if (minValue > maxValue) - { - throw new ArgumentOutOfRangeException("minValue", GetRandomCommandStrings.MinGreaterThanOrEqualMaxApi); - } - - long range = (long)maxValue - (long)minValue; - if (range <= int.MaxValue) - { - return ((int)(NextDouble() * range) + minValue); - } - else - { - double largeSample = InternalSampleLargeRange() * (1.0 / (2 * ((uint)Int32.MaxValue))); - int result = (int)((long)(largeSample * range) + minValue); - - return result; - } - } - - /// - /// Fills the elements of a specified array of bytes with random numbers. - /// - /// The array to be filled - internal void NextBytes(byte[] buffer) - { - if (_cryptographicGenerator != null) - { - _cryptographicGenerator.GetBytes(buffer); - } - else - { - _pseudoGenerator.NextBytes(buffer); - } - } - - /// - /// Samples a random integer - /// - /// A random integer, using the full range of Int32 - private int InternalSample() - { - int result; - byte[] data = new byte[sizeof(int)]; - - NextBytes(data); - result = BitConverter.ToInt32(data, 0); - - return result; - } - - /// - /// Samples a random int when the range is large. This does - /// not need to be in the range of -Double.MaxValue .. Double.MaxValue, - /// just 0.. (2 * Int32.MaxValue) - 1 - /// - /// - private double InternalSampleLargeRange() - { - double result; - - do - { - result = InternalSample(); - } while (result == Int32.MaxValue); - - result += Int32.MaxValue; - return result; + base.BeginProcessing(); } } } diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/GetRandomCommandBase.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/GetRandomCommandBase.cs new file mode 100644 index 00000000000..8e50b1cf5a9 --- /dev/null +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/GetRandomCommandBase.cs @@ -0,0 +1,719 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Management.Automation; +using System.Management.Automation.Runspaces; +using System.Numerics; +using System.Reflection; +using System.Security.Cryptography; +using System.Threading; + +using Debug = System.Management.Automation.Diagnostics; + +namespace Microsoft.PowerShell.Commands +{ + /// + /// This class implements base class for `Get-Random` and `Get-SecureRandom` cmdlets. + /// + public class GetRandomCommandBase : PSCmdlet + { + #region Parameter set handling + + internal const string RandomNumberParameterSet = "RandomNumberParameterSet"; + private const string RandomListItemParameterSet = "RandomListItemParameterSet"; + private const string ShuffleParameterSet = "ShuffleParameterSet"; + + private static readonly object[] _nullInArray = new object[] { null }; + + private enum MyParameterSet + { + Unknown, + RandomNumber, + RandomListItem + } + + private MyParameterSet _effectiveParameterSet; + + private MyParameterSet EffectiveParameterSet + { + get + { + // cache MyParameterSet enum instead of doing string comparison every time + if (_effectiveParameterSet == MyParameterSet.Unknown) + { + if (MyInvocation.ExpectingInput && (Maximum == null) && (Minimum == null)) + { + _effectiveParameterSet = MyParameterSet.RandomListItem; + } + else if (ParameterSetName == GetRandomCommandBase.RandomListItemParameterSet + || ParameterSetName == GetRandomCommandBase.ShuffleParameterSet) + { + _effectiveParameterSet = MyParameterSet.RandomListItem; + } + else if (ParameterSetName.Equals(GetRandomCommandBase.RandomNumberParameterSet, StringComparison.OrdinalIgnoreCase)) + { + if ((Maximum != null) && Maximum.GetType().IsArray) + { + InputObject = (object[])Maximum; + _effectiveParameterSet = MyParameterSet.RandomListItem; + } + else + { + _effectiveParameterSet = MyParameterSet.RandomNumber; + } + } + else + { + Debug.Assert(false, "Unrecognized parameter set"); + } + } + + return _effectiveParameterSet; + } + } + + #endregion Parameter set handling + + #region Error handling + + private void ThrowMinGreaterThanOrEqualMax(object minValue, object maxValue) + { + if (minValue == null) + { + throw PSTraceSource.NewArgumentNullException("min"); + } + + if (maxValue == null) + { + throw PSTraceSource.NewArgumentNullException("max"); + } + + ErrorRecord errorRecord = new( + new ArgumentException(string.Format( + CultureInfo.InvariantCulture, GetRandomCommandStrings.MinGreaterThanOrEqualMax, minValue, maxValue)), + "MinGreaterThanOrEqualMax", + ErrorCategory.InvalidArgument, + null); + + ThrowTerminatingError(errorRecord); + } + + #endregion + + #region Random generator state + + private static readonly ReaderWriterLockSlim s_runspaceGeneratorMapLock = new(); + + // 1-to-1 mapping of cmdlet + runspacesId and random number generators + private static readonly Dictionary s_runspaceGeneratorMap = new(); + + private static void CurrentRunspace_StateChanged(object sender, RunspaceStateEventArgs e) + { + switch (e.RunspaceStateInfo.State) + { + case RunspaceState.Broken: + case RunspaceState.Closed: + try + { + GetRandomCommandBase.s_runspaceGeneratorMapLock.EnterWriteLock(); + GetRandomCommandBase.s_runspaceGeneratorMap.Remove(MethodBase.GetCurrentMethod().DeclaringType.Name + ((Runspace)sender).InstanceId.ToString()); + } + finally + { + GetRandomCommandBase.s_runspaceGeneratorMapLock.ExitWriteLock(); + } + + break; + } + } + + private PolymorphicRandomNumberGenerator _generator; + + /// + /// Gets and sets generator associated with the current cmdlet and runspace. + /// + internal PolymorphicRandomNumberGenerator Generator + { + get + { + if (_generator == null) + { + string runspaceId = Context.CurrentRunspace.InstanceId.ToString(); + + bool needToInitialize = false; + try + { + GetRandomCommandBase.s_runspaceGeneratorMapLock.EnterReadLock(); + needToInitialize = !GetRandomCommandBase.s_runspaceGeneratorMap.TryGetValue(this.GetType().Name + runspaceId, out _generator); + } + finally + { + GetRandomCommandBase.s_runspaceGeneratorMapLock.ExitReadLock(); + } + + if (needToInitialize) + { + Generator = new PolymorphicRandomNumberGenerator(); + } + } + + return _generator; + } + + set + { + _generator = value; + Runspace myRunspace = Context.CurrentRunspace; + + try + { + GetRandomCommandBase.s_runspaceGeneratorMapLock.EnterWriteLock(); + if (!GetRandomCommandBase.s_runspaceGeneratorMap.ContainsKey(this.GetType().Name + myRunspace.InstanceId.ToString())) + { + // make sure we won't leave the generator around after runspace exits + myRunspace.StateChanged += CurrentRunspace_StateChanged; + } + + GetRandomCommandBase.s_runspaceGeneratorMap[this.GetType().Name + myRunspace.InstanceId.ToString()] = _generator; + } + finally + { + GetRandomCommandBase.s_runspaceGeneratorMapLock.ExitWriteLock(); + } + } + } + + #endregion + + #region Parameters for RandomNumberParameterSet + + /// + /// Gets or sets the maximum number to generate. + /// + [Parameter(ParameterSetName = RandomNumberParameterSet, Position = 0)] + public object Maximum { get; set; } + + /// + /// Gets or sets the minimum number to generate. + /// + [Parameter(ParameterSetName = RandomNumberParameterSet)] + public object Minimum { get; set; } + + private static bool IsInt(object o) + { + if (o == null || o is int) + { + return true; + } + + return false; + } + + private static bool IsInt64(object o) + { + if (o == null || o is long) + { + return true; + } + + return false; + } + + private static object ProcessOperand(object o) + { + if (o == null) + { + return null; + } + + PSObject pso = PSObject.AsPSObject(o); + object baseObject = pso.BaseObject; + + if (baseObject is string) + { + // The type argument passed in does not decide the number type we want to convert to. ScanNumber will return + // int/long/double based on the string form number passed in. + baseObject = System.Management.Automation.Language.Parser.ScanNumber((string)baseObject, typeof(int)); + } + + return baseObject; + } + + private static double ConvertToDouble(object o, double defaultIfNull) + { + if (o == null) + { + return defaultIfNull; + } + + double result = (double)LanguagePrimitives.ConvertTo(o, typeof(double), CultureInfo.InvariantCulture); + return result; + } + + #endregion + + #region Parameters and variables for RandomListItemParameterSet + + private List _chosenListItems; + private int _numberOfProcessedListItems; + + /// + /// Gets or sets the list from which random elements are chosen. + /// + [Parameter(ParameterSetName = RandomListItemParameterSet, ValueFromPipeline = true, Position = 0, Mandatory = true)] + [Parameter(ParameterSetName = ShuffleParameterSet, ValueFromPipeline = true, Position = 0, Mandatory = true)] + [System.Management.Automation.AllowNull] + [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] + public object[] InputObject { get; set; } + + /// + /// Gets or sets the number of items to output (number of list items or of numbers). + /// + [Parameter(ParameterSetName = RandomNumberParameterSet)] + [Parameter(ParameterSetName = RandomListItemParameterSet)] + [ValidateRange(1, int.MaxValue)] + public int Count { get; set; } = 1; + + #endregion + + #region Shuffle parameter + + /// + /// Gets or sets whether the command should return all input objects in randomized order. + /// + [Parameter(ParameterSetName = ShuffleParameterSet, Mandatory = true)] + public SwitchParameter Shuffle { get; set; } + + #endregion + + #region Cmdlet processing methods + + private double GetRandomDouble(double minValue, double maxValue) + { + double randomNumber; + double diff = maxValue - minValue; + + // I couldn't find a better fix for bug #216893 then + // to test and retry if a random number falls outside the bounds + // because of floating-point-arithmetic inaccuracies. + // + // Performance in the normal case is not impacted much. + // In low-precision situations we should converge to a solution quickly + // (diff gets smaller at a quick pace). + if (double.IsInfinity(diff)) + { + do + { + double r = Generator.NextDouble(); + randomNumber = minValue + (r * maxValue) - (r * minValue); + } + while (randomNumber >= maxValue); + } + else + { + do + { + double r = Generator.NextDouble(); + randomNumber = minValue + (r * diff); + diff *= r; + } + while (randomNumber >= maxValue); + } + + return randomNumber; + } + + /// + /// Get a random Int64 type number. + /// + /// Minimum value. + /// Maximum value. + /// Rnadom long. + private long GetRandomInt64(long minValue, long maxValue) + { + // Randomly generate eight bytes and convert the byte array to UInt64 + var buffer = new byte[sizeof(ulong)]; + ulong randomUint64; + + BigInteger bigIntegerDiff = (BigInteger)maxValue - (BigInteger)minValue; + + // When the difference is less than int.MaxValue, use Random.Next(int, int) + if (bigIntegerDiff <= int.MaxValue) + { + int randomDiff = Generator.Next(0, (int)(maxValue - minValue)); + return minValue + randomDiff; + } + + // The difference of two Int64 numbers would not exceed UInt64.MaxValue, so it can be represented by a UInt64 number. + ulong uint64Diff = (ulong)bigIntegerDiff; + + // Calculate the number of bits to represent the diff in type UInt64 + int bitsToRepresentDiff = 0; + ulong diffCopy = uint64Diff; + for (; diffCopy != 0; bitsToRepresentDiff++) + { + diffCopy >>= 1; + } + + // Get the mask for the number of bits + ulong mask = 0xffffffffffffffff >> (64 - bitsToRepresentDiff); + do + { + // Randomly fill the buffer + Generator.NextBytes(buffer); + randomUint64 = BitConverter.ToUInt64(buffer, 0); + + // Get the last 'bitsToRepresentDiff' number of random bits + randomUint64 &= mask; + } while (uint64Diff <= randomUint64); + + double randomNumber = (minValue * 1.0) + (randomUint64 * 1.0); + return (long)randomNumber; + } + + /// + /// This method implements the BeginProcessing method for derived cmdlets. + /// + protected override void BeginProcessing() + { + if (EffectiveParameterSet == MyParameterSet.RandomNumber) + { + object maxOperand = ProcessOperand(Maximum); + object minOperand = ProcessOperand(Minimum); + + if (IsInt(maxOperand) && IsInt(minOperand)) + { + int minValue = minOperand != null ? (int)minOperand : 0; + int maxValue = maxOperand != null ? (int)maxOperand : int.MaxValue; + + if (minValue >= maxValue) + { + ThrowMinGreaterThanOrEqualMax(minValue, maxValue); + } + + for (int i = 0; i < Count; i++) + { + int randomNumber = Generator.Next(minValue, maxValue); + Debug.Assert(minValue <= randomNumber, "lower bound <= random number"); + Debug.Assert(randomNumber < maxValue, "random number < upper bound"); + + WriteObject(randomNumber); + } + } + else if ((IsInt64(maxOperand) || IsInt(maxOperand)) && (IsInt64(minOperand) || IsInt(minOperand))) + { + long minValue = minOperand != null ? ((minOperand is long) ? (long)minOperand : (int)minOperand) : 0; + long maxValue = maxOperand != null ? ((maxOperand is long) ? (long)maxOperand : (int)maxOperand) : long.MaxValue; + + if (minValue >= maxValue) + { + ThrowMinGreaterThanOrEqualMax(minValue, maxValue); + } + + for (int i = 0; i < Count; i++) + { + long randomNumber = GetRandomInt64(minValue, maxValue); + Debug.Assert(minValue <= randomNumber, "lower bound <= random number"); + Debug.Assert(randomNumber < maxValue, "random number < upper bound"); + + WriteObject(randomNumber); + } + } + else + { + double minValue = (minOperand is double) ? (double)minOperand : ConvertToDouble(Minimum, 0.0); + double maxValue = (maxOperand is double) ? (double)maxOperand : ConvertToDouble(Maximum, double.MaxValue); + + if (minValue >= maxValue) + { + ThrowMinGreaterThanOrEqualMax(minValue, maxValue); + } + + for (int i = 0; i < Count; i++) + { + double randomNumber = GetRandomDouble(minValue, maxValue); + Debug.Assert(minValue <= randomNumber, "lower bound <= random number"); + Debug.Assert(randomNumber < maxValue, "random number < upper bound"); + + WriteObject(randomNumber); + } + } + } + else if (EffectiveParameterSet == MyParameterSet.RandomListItem) + { + _chosenListItems = new List(); + _numberOfProcessedListItems = 0; + } + } + + // rough proof that when choosing random K items out of N items + // each item has got K/N probability of being included in the final list + // + // probability that a particular item in chosenListItems is NOT going to be replaced + // when processing I-th input item [assumes I > K]: + // P_one_step(I) = 1 - ((K / I) * ((K - 1) / K) + ((I - K) / I) = (I - 1) / I + // <--A--> <-----B-----> <-----C-----> + // A - probability that I-th element is going to be replacing an element from chosenListItems + // (see (1) in the code below) + // B - probability that a particular element from chosenListItems is NOT going to be replaced + // (see (2) in the code below) + // C - probability that I-th element is NOT going to be replacing an element from chosenListItems + // (see (1) in the code below) + // + // probability that a particular item in chosenListItems is NOT going to be replaced + // when processing input items J through N [assumes J > K] + // P_removal(J) = Multiply(for I = J to N) P(I) = + // = ((J - 1) / J) * (J / (J + 1)) * ... * ((N - 2) / (N - 1)) * ((N - 1) / N) = + // = (J - 1) / N + // + // probability that when processing an element it is going to be put into chosenListItems + // P_insertion(I) = 1.0 when I <= K - see (3) in the code below + // P_insertion(I) = K/N otherwise - see (1) in the code below + // + // probability that a given element is going to be a part of the final list + // P_final(I) = P_insertion(I) * P_removal(max(I + 1, K + 1)) + // [for I <= K] = 1.0 * ((K + 1) - 1) / N = K / N + // [otherwise] = (K / I) * ((I + 1) - 1) / N = K / N + // + // which proves that P_final(I) = K / N for all values of I. QED. + + /// + /// This method implements the ProcessRecord method for derived cmdlets. + /// + protected override void ProcessRecord() + { + if (EffectiveParameterSet == MyParameterSet.RandomListItem) + { + if (Shuffle) + { + // this allows for $null to be in an array passed to InputObject + foreach (object item in InputObject ?? _nullInArray) + { + _chosenListItems.Add(item); + } + } + else + { + foreach (object item in InputObject ?? _nullInArray) + { + // (3) + if (_numberOfProcessedListItems < Count) + { + Debug.Assert(_chosenListItems.Count == _numberOfProcessedListItems, "Initial K elements should all be included in chosenListItems"); + _chosenListItems.Add(item); + } + else + { + Debug.Assert(_chosenListItems.Count == Count, "After processing K initial elements, the length of chosenItems should stay equal to K"); + + // (1) + if (Generator.Next(_numberOfProcessedListItems + 1) < Count) + { + // (2) + int indexToReplace = Generator.Next(_chosenListItems.Count); + _chosenListItems[indexToReplace] = item; + } + } + + _numberOfProcessedListItems++; + } + } + } + } + + /// + /// This method implements the EndProcessing method for derived cmdlets. + /// + protected override void EndProcessing() + { + if (EffectiveParameterSet == MyParameterSet.RandomListItem) + { + // make sure the order is truly random + // (all permutations with the same probability) + // O(n) time + int n = _chosenListItems.Count; + for (int i = 0; i < n; i++) + { + // randomly choose j from [i...n) + int j = Generator.Next(i, n); + + WriteObject(_chosenListItems[j]); + + // remove the output object from consideration in the next iteration. + if (i != j) + { + _chosenListItems[j] = _chosenListItems[i]; + } + } + } + } + + #endregion Processing methods + } + + /// + /// Provides an adapter API for random numbers that may be either cryptographically random, or + /// generated with the regular pseudo-random number generator. Re-implementations of + /// methods using the NextBytes() primitive based on the CLR implementation: + /// https://referencesource.microsoft.com/#mscorlib/system/random.cs. + /// + internal sealed class PolymorphicRandomNumberGenerator + { + /// + /// Initializes a new instance of the class. + /// + public PolymorphicRandomNumberGenerator() + { + _cryptographicGenerator = RandomNumberGenerator.Create(); + _pseudoGenerator = null; + } + + /// + /// Initializes a new instance of the using pseudorandom generator instead of the cryptographic one. + /// + /// The seed value. + internal PolymorphicRandomNumberGenerator(int seed) + { + _cryptographicGenerator = null; + _pseudoGenerator = new Random(seed); + } + + private readonly Random _pseudoGenerator = null; + private readonly RandomNumberGenerator _cryptographicGenerator = null; + + /// + /// Generates a random floating-point number that is greater than or equal to 0.0, and less than 1.0. + /// + /// A random floating-point number that is greater than or equal to 0.0, and less than 1.0. + internal double NextDouble() + { + // According to the CLR source: + // "Including this division at the end gives us significantly improved random number distribution." + return Next() * (1.0 / int.MaxValue); + } + + /// + /// Generates a non-negative random integer. + /// + /// A non-negative random integer. + internal int Next() + { + int randomNumber; + + // The CLR implementation just fudges + // Int32.MaxValue down to (Int32.MaxValue - 1). This implementation + // errs on the side of correctness. + do + { + randomNumber = InternalSample(); + } + while (randomNumber == int.MaxValue); + + if (randomNumber < 0) + { + randomNumber += int.MaxValue; + } + + return randomNumber; + } + + /// + /// Returns a random integer that is within a specified range. + /// + /// The exclusive upper bound of the random number returned. + /// Next random integer. + internal int Next(int maxValue) + { + if (maxValue < 0) + { + throw new ArgumentOutOfRangeException(nameof(maxValue), GetRandomCommandStrings.MaxMustBeGreaterThanZeroApi); + } + + return Next(0, maxValue); + } + + /// + /// Returns a random integer that is within a specified range. + /// + /// The inclusive lower bound of the random number returned. + /// The exclusive upper bound of the random number returned. maxValue must be greater than or equal to minValue. + /// Next random integer. + public int Next(int minValue, int maxValue) + { + if (minValue > maxValue) + { + throw new ArgumentOutOfRangeException(nameof(minValue), GetRandomCommandStrings.MinGreaterThanOrEqualMaxApi); + } + + int randomNumber = 0; + + long range = (long)maxValue - (long)minValue; + if (range <= int.MaxValue) + { + randomNumber = (int)(NextDouble() * range) + minValue; + } + else + { + double largeSample = InternalSampleLargeRange() * (1.0 / (2 * ((uint)int.MaxValue))); + randomNumber = (int)((long)(largeSample * range) + minValue); + } + + return randomNumber; + } + + /// + /// Fills the elements of a specified array of bytes with random numbers. + /// + /// The array to be filled. + internal void NextBytes(byte[] buffer) + { + if (_cryptographicGenerator != null) + { + _cryptographicGenerator.GetBytes(buffer); + } + else + { + _pseudoGenerator.NextBytes(buffer); + } + } + + /// + /// Samples a random integer. + /// + /// A random integer, using the full range of Int32. + private int InternalSample() + { + int randomNumber; + byte[] data = new byte[sizeof(int)]; + + NextBytes(data); + randomNumber = BitConverter.ToInt32(data, 0); + + return randomNumber; + } + + /// + /// Samples a random int when the range is large. This does + /// not need to be in the range of -Double.MaxValue .. Double.MaxValue, + /// just 0.. (2 * Int32.MaxValue) - 1 . + /// + /// A random double. + private double InternalSampleLargeRange() + { + double randomNumber; + + do + { + randomNumber = InternalSample(); + } + while (randomNumber == int.MaxValue); + + randomNumber += int.MaxValue; + return randomNumber; + } + } +} diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/GetRunspaceCommand.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/GetRunspaceCommand.cs index f138b9dd89d..10c050a4562 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/GetRunspaceCommand.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/GetRunspaceCommand.cs @@ -1,12 +1,12 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.Management.Automation; using System.Management.Automation.Runspaces; + using Dbg = System.Management.Automation.Diagnostics; namespace Microsoft.PowerShell.Commands @@ -15,7 +15,7 @@ namespace Microsoft.PowerShell.Commands /// This cmdlet returns runspaces in the PowerShell session. /// [Cmdlet(VerbsCommon.Get, "Runspace", DefaultParameterSetName = GetRunspaceCommand.NameParameterSet, - HelpUri = "https://go.microsoft.com/fwlink/?LinkID=403730")] + HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2096616")] [OutputType(typeof(Runspace))] public sealed class GetRunspaceCommand : PSCmdlet { @@ -73,7 +73,7 @@ public Guid[] InstanceId #region Overrides /// - /// Process record + /// Process record. /// protected override void ProcessRecord() { @@ -126,7 +126,7 @@ internal static IReadOnlyList GetAllRunspaces() internal static IReadOnlyList GetRunspacesByName(string[] names) { - List rtnRunspaces = new List(); + List rtnRunspaces = new(); IReadOnlyList runspaces = Runspace.RunspaceList; foreach (string name in names) @@ -146,7 +146,7 @@ internal static IReadOnlyList GetRunspacesByName(string[] names) internal static IReadOnlyList GetRunspacesById(int[] ids) { - List rtnRunspaces = new List(); + List rtnRunspaces = new(); foreach (int id in ids) { @@ -167,7 +167,7 @@ internal static IReadOnlyList GetRunspacesById(int[] ids) internal static IReadOnlyList GetRunspacesByInstanceId(Guid[] instanceIds) { - List rtnRunspaces = new List(); + List rtnRunspaces = new(); IReadOnlyList runspaces = Runspace.RunspaceList; foreach (Guid instanceId in instanceIds) diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/GetSecureRandomCommand.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/GetSecureRandomCommand.cs new file mode 100644 index 00000000000..e0ea7e68dbf --- /dev/null +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/GetSecureRandomCommand.cs @@ -0,0 +1,18 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.Management.Automation; + +namespace Microsoft.PowerShell.Commands +{ + /// + /// This class implements `Get-SecureRandom` cmdlet. + /// + [Cmdlet(VerbsCommon.Get, "SecureRandom", DefaultParameterSetName = GetRandomCommandBase.RandomNumberParameterSet, + HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2235055", RemotingCapability = RemotingCapability.None)] + [OutputType(typeof(int), typeof(long), typeof(double))] + public sealed class GetSecureRandomCommand : GetRandomCommandBase + { + // nothing unique from base class + } +} diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/GetUICultureCommand.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/GetUICultureCommand.cs index 9e6a983a30c..53e8c5d3adf 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/GetUICultureCommand.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/GetUICultureCommand.cs @@ -1,6 +1,5 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. using System.Management.Automation; @@ -9,18 +8,16 @@ namespace Microsoft.PowerShell.Commands /// /// Returns the thread's current UI culture. /// - [Cmdlet(VerbsCommon.Get, "UICulture", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113334")] + [Cmdlet(VerbsCommon.Get, "UICulture", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2096613")] [OutputType(typeof(System.Globalization.CultureInfo))] public sealed class GetUICultureCommand : PSCmdlet { /// - /// Output the current UI Culture info object + /// Output the current UI Culture info object. /// protected override void BeginProcessing() { WriteObject(Host.CurrentUICulture); - } // EndProcessing - } // GetUICultureCommand -} // Microsoft.PowerShell.Commands - - + } + } +} diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/GetUnique.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/GetUnique.cs index a17f2c0ec45..09bc78d9693 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/GetUnique.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/GetUnique.cs @@ -1,29 +1,25 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. using System; +using System.Globalization; using System.Management.Automation; using System.Management.Automation.Internal; -using System.Globalization; namespace Microsoft.PowerShell.Commands { /// - /// /// [Cmdlet(VerbsCommon.Get, "Unique", DefaultParameterSetName = "AsString", - HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113335", RemotingCapability = RemotingCapability.None)] + HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2097028", RemotingCapability = RemotingCapability.None)] public sealed class GetUniqueCommand : PSCmdlet { #region Parameters /// - /// /// /// [Parameter(ValueFromPipeline = true)] - public PSObject InputObject { set; get; } = AutomationNull.Value; - + public PSObject InputObject { get; set; } = AutomationNull.Value; /// /// This parameter specifies that objects should be converted to @@ -34,10 +30,11 @@ public sealed class GetUniqueCommand : PSCmdlet public SwitchParameter AsString { get { return _asString; } + set { _asString = value; } } - private bool _asString; + private bool _asString; /// /// This parameter specifies that just the types of the objects @@ -48,19 +45,27 @@ public SwitchParameter AsString public SwitchParameter OnType { get { return _onType; } + set { _onType = value; } } + private bool _onType = false; + + /// + /// Gets or sets case insensitive switch for string comparison. + /// + [Parameter] + public SwitchParameter CaseInsensitive { get; set; } + #endregion Parameters #region Overrides /// - /// /// protected override void ProcessRecord() { bool isUnique = true; - if (null == _lastObject) + if (_lastObject == null) { // always write first object, but return nothing // on "MSH> get-unique" @@ -74,14 +79,12 @@ protected override void ProcessRecord() else if (AsString) { string inputString = InputObject.ToString(); - if (null == _lastObjectAsString) - { - _lastObjectAsString = _lastObject.ToString(); - } - if (0 == String.Compare( + _lastObjectAsString ??= _lastObject.ToString(); + + if (string.Equals( inputString, _lastObjectAsString, - StringComparison.CurrentCulture)) + CaseInsensitive.IsPresent ? StringComparison.CurrentCultureIgnoreCase : StringComparison.CurrentCulture)) { isUnique = false; } @@ -92,14 +95,12 @@ protected override void ProcessRecord() } else // compare as objects { - if (null == _comparer) - { - _comparer = new ObjectCommandComparer( - true, // ascending (doesn't matter) - CultureInfo.CurrentCulture, - true); // case-sensitive - } - isUnique = (0 != _comparer.Compare(InputObject, _lastObject)); + _comparer ??= new ObjectCommandComparer( + ascending: true, + CultureInfo.CurrentCulture, + caseSensitive: !CaseInsensitive.IsPresent); + + isUnique = (_comparer.Compare(InputObject, _lastObject) != 0); } if (isUnique) @@ -117,4 +118,3 @@ protected override void ProcessRecord() #endregion Internal } } - diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/GetUptime.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/GetUptime.cs index f7389e606af..c21165301e2 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/GetUptime.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/GetUptime.cs @@ -1,16 +1,15 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. using System; -using System.Management.Automation; using System.Diagnostics; +using System.Management.Automation; using System.Management.Automation.Internal; namespace Microsoft.PowerShell.Commands { /// - /// This class implements Get-Uptime + /// This class implements Get-Uptime. /// [Cmdlet(VerbsCommon.Get, "Uptime", DefaultParameterSetName = TimespanParameterSet, HelpUri = "https://go.microsoft.com/fwlink/?linkid=834862")] [OutputType(typeof(TimeSpan), ParameterSetName = new string[] { TimespanParameterSet })] @@ -18,16 +17,14 @@ namespace Microsoft.PowerShell.Commands public class GetUptimeCommand : PSCmdlet { /// - /// Since parameter - /// The system startup time + /// The system startup time. /// /// [Parameter(ParameterSetName = SinceParameterSet)] public SwitchParameter Since { get; set; } = new SwitchParameter(); /// - /// ProcessRecord() override - /// This is the main entry point for the cmdlet + /// This is the main entry point for the cmdlet. /// protected override void ProcessRecord() { @@ -37,18 +34,17 @@ protected override void ProcessRecord() // InternalTestHooks.StopwatchIsNotHighResolution is used as test hook. if (Stopwatch.IsHighResolution && !InternalTestHooks.StopwatchIsNotHighResolution) { - TimeSpan uptime = TimeSpan.FromSeconds(Stopwatch.GetTimestamp()/Stopwatch.Frequency); + TimeSpan uptime = TimeSpan.FromSeconds(Stopwatch.GetTimestamp() / Stopwatch.Frequency); - switch (ParameterSetName) + if (Since) + { + // Output the time of the last system boot. + WriteObject(DateTime.Now.Subtract(uptime)); + } + else { - case TimespanParameterSet: - // return TimeSpan of time since the system started up - WriteObject(uptime); - break; - case SinceParameterSet: - // return Datetime when the system started up - WriteObject(DateTime.Now.Subtract(uptime)); - break; + // Output the time elapsed since the last system boot. + WriteObject(uptime); } } else @@ -68,5 +64,5 @@ protected override void ProcessRecord() /// Parameter set name for DateTime OutputType. /// private const string SinceParameterSet = "Since"; - } + } } diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/GetVerbCommand.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/GetVerbCommand.cs index 128ffcb3cbb..61a0fe1a390 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/GetVerbCommand.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/GetVerbCommand.cs @@ -1,30 +1,30 @@ -using System; +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + using System.Management.Automation; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Reflection; +using static System.Management.Automation.Verbs; namespace Microsoft.PowerShell.Commands { - /// - /// Implementation of the Get Verb Command + /// Implementation of the Get Verb Command. /// - [Cmdlet(VerbsCommon.Get, "Verb", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=160712")] + [Cmdlet(VerbsCommon.Get, "Verb", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2097026")] [OutputType(typeof(VerbInfo))] public class GetVerbCommand : Cmdlet { /// - /// Optional Verb filter + /// Optional Verb filter. /// [Parameter(ValueFromPipeline = true, ValueFromPipelineByPropertyName = true, Position = 0)] + [ArgumentCompleter(typeof(VerbArgumentCompleter))] public string[] Verb { get; set; } /// - /// Optional Group filter + /// Optional Group filter. /// [Parameter(ValueFromPipeline = true, ValueFromPipelineByPropertyName = true, Position = 1)] [ValidateSet("Common", "Communications", "Data", "Diagnostic", "Lifecycle", "Other", "Security")] @@ -34,47 +34,13 @@ public string[] Group } /// - /// Returns a list of verbs + /// Returns a list of verbs. /// protected override void ProcessRecord() { - - Type[] verbTypes = new Type[] { typeof(VerbsCommon), typeof(VerbsCommunications), typeof(VerbsData), - typeof(VerbsDiagnostic), typeof(VerbsLifecycle), typeof(VerbsOther), typeof(VerbsSecurity) }; - - Collection matchingVerbs = SessionStateUtilities.CreateWildcardsFromStrings( - this.Verb, - WildcardOptions.IgnoreCase - ); - - foreach (Type type in verbTypes) + foreach (VerbInfo verb in FilterByVerbsAndGroups(Verb, Group)) { - string groupName = type.Name.Substring(5); - if (this.Group != null) - { - if (!SessionStateUtilities.CollectionContainsValue(this.Group, groupName, StringComparer.OrdinalIgnoreCase)) - { - continue; - } - } - foreach (FieldInfo field in type.GetFields()) - { - if (field.IsLiteral) - { - if (this.Verb != null) - { - if (!SessionStateUtilities.MatchesAnyWildcardPattern(field.Name, matchingVerbs, false)) - { - continue; - } - } - - VerbInfo verb = new VerbInfo(); - verb.Verb = field.Name; - verb.Group = groupName; - WriteObject(verb); - } - } + WriteObject(verb); } } } diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Group-Object.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Group-Object.cs new file mode 100644 index 00000000000..1f527258939 --- /dev/null +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Group-Object.cs @@ -0,0 +1,553 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Linq; +using System.Management.Automation; +using System.Management.Automation.Internal; +using System.Text; + +namespace Microsoft.PowerShell.Commands +{ + /// + /// PSTuple is a helper class used to create Tuple from an input array. + /// + internal static class PSTuple + { + /// + /// ArrayToTuple is a helper method used to create a tuple for the supplied input array. + /// + /// The first generic type parameter. + /// Input objects used to create a tuple. + /// Tuple object. + internal static object ArrayToTuple(IList inputObjects) + { + return ArrayToTuple(inputObjects, 0); + } + + /// + /// ArrayToTuple is a helper method used to create a tuple for the supplied input array. + /// + /// The first generic type parameter. + /// Input objects used to create a tuple. + /// Start index of the array from which the objects have to considered for the tuple creation. + /// Tuple object. + private static object ArrayToTuple(IList inputObjects, int startIndex) + { + Diagnostics.Assert(inputObjects != null, "inputObjects is null"); + Diagnostics.Assert(inputObjects.Count > 0, "inputObjects is empty"); + + switch (inputObjects.Count - startIndex) + { + case 0: + return null; + case 1: + return Tuple.Create(inputObjects[startIndex]); + case 2: + return Tuple.Create(inputObjects[startIndex], inputObjects[startIndex + 1]); + case 3: + return Tuple.Create(inputObjects[startIndex], inputObjects[startIndex + 1], inputObjects[startIndex + 2]); + case 4: + return Tuple.Create(inputObjects[startIndex], inputObjects[startIndex + 1], inputObjects[startIndex + 2], inputObjects[startIndex + 3]); + case 5: + return Tuple.Create( + inputObjects[startIndex], + inputObjects[startIndex + 1], + inputObjects[startIndex + 2], + inputObjects[startIndex + 3], + inputObjects[startIndex + 4]); + case 6: + return Tuple.Create( + inputObjects[startIndex], + inputObjects[startIndex + 1], + inputObjects[startIndex + 2], + inputObjects[startIndex + 3], + inputObjects[startIndex + 4], + inputObjects[startIndex + 5]); + case 7: + return Tuple.Create( + inputObjects[startIndex], + inputObjects[startIndex + 1], + inputObjects[startIndex + 2], + inputObjects[startIndex + 3], + inputObjects[startIndex + 4], + inputObjects[startIndex + 5], + inputObjects[startIndex + 6]); + case 8: + return Tuple.Create( + inputObjects[startIndex], + inputObjects[startIndex + 1], + inputObjects[startIndex + 2], + inputObjects[startIndex + 3], + inputObjects[startIndex + 4], + inputObjects[startIndex + 5], + inputObjects[startIndex + 6], + inputObjects[startIndex + 7]); + default: + return Tuple.Create( + inputObjects[startIndex], + inputObjects[startIndex + 1], + inputObjects[startIndex + 2], + inputObjects[startIndex + 3], + inputObjects[startIndex + 4], + inputObjects[startIndex + 5], + inputObjects[startIndex + 6], + ArrayToTuple(inputObjects, startIndex + 7)); + } + } + } + + /// + /// Emitted by Group-Object when the NoElement option is true. + /// + public sealed class GroupInfoNoElement : GroupInfo + { + internal GroupInfoNoElement(OrderByPropertyEntry groupValue) : base(groupValue) + { + } + + internal override void Add(PSObject groupValue) + { + Count++; + } + } + + /// + /// Emitted by Group-Object. + /// + [DebuggerDisplay("{Name} ({Count})")] + public class GroupInfo + { + internal GroupInfo(OrderByPropertyEntry groupValue) + { + Group = new Collection(); + this.Add(groupValue.inputObject); + GroupValue = groupValue; + Name = BuildName(groupValue.orderValues); + } + + internal virtual void Add(PSObject groupValue) + { + Group.Add(groupValue); + Count++; + } + + private static string BuildName(List propValues) + { + StringBuilder sb = new(); + foreach (ObjectCommandPropertyValue propValue in propValues) + { + var propValuePropertyValue = propValue?.PropertyValue; + if (propValuePropertyValue != null) + { + if (propValuePropertyValue is ICollection propertyValueItems) + { + sb.Append('{'); + var length = sb.Length; + + foreach (object item in propertyValueItems) + { + sb.Append(CultureInfo.CurrentCulture, $"{item}, "); + } + + sb = sb.Length > length ? sb.Remove(sb.Length - 2, 2) : sb; + sb.Append("}, "); + } + else + { + sb.Append(CultureInfo.CurrentCulture, $"{propValuePropertyValue}, "); + } + } + } + + return sb.Length >= 2 ? sb.Remove(sb.Length - 2, 2).ToString() : string.Empty; + } + + /// + /// Gets the values of the group. + /// + public ArrayList Values + { + get + { + ArrayList values = new(); + foreach (ObjectCommandPropertyValue propValue in GroupValue.orderValues) + { + values.Add(propValue.PropertyValue); + } + + return values; + } + } + + /// + /// Gets the number of objects in the group. + /// + public int Count { get; internal set; } + + /// + /// Gets the list of objects in this group. + /// + public Collection Group { get; } + + /// + /// Gets the name of the group. + /// + public string Name { get; } + + /// + /// Gets the OrderByPropertyEntry used to build this group object. + /// + internal OrderByPropertyEntry GroupValue { get; } + } + + /// + /// Group-Object implementation. + /// + [Cmdlet(VerbsData.Group, "Object", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2096619", RemotingCapability = RemotingCapability.None)] + [OutputType(typeof(Hashtable), typeof(GroupInfo))] + public class GroupObjectCommand : ObjectBase + { + #region tracer + + /// + /// An instance of the PSTraceSource class used for trace output. + /// + [TraceSource("GroupObjectCommand", "Class that has group base implementation")] + private static readonly PSTraceSource s_tracer = PSTraceSource.GetTracer("GroupObjectCommand", "Class that has group base implementation"); + + #endregion tracer + + #region Command Line Switches + + /// + /// Gets or sets the NoElement parameter indicating of the groups should be flattened. + /// + /// + [Parameter] + public SwitchParameter NoElement { get; set; } + + /// + /// Gets or sets the AsHashTable parameter. + /// + /// + [Parameter(ParameterSetName = "HashTable")] + [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "HashTable")] + [Alias("AHT")] + public SwitchParameter AsHashTable { get; set; } + + /// + /// Gets or sets the AsString parameter. + /// + [Parameter(ParameterSetName = "HashTable")] + public SwitchParameter AsString { get; set; } + + private readonly List _groups = new(); + private readonly OrderByProperty _orderByProperty = new(); + private readonly Dictionary _tupleToGroupInfoMappingDictionary = new(); + private readonly List _entriesToOrder = new(); + private OrderByPropertyComparer _orderByPropertyComparer; + private bool _hasProcessedFirstInputObject; + private bool _hasDifferentValueTypes; + private Type[] _propertyTypesCandidate; + + #endregion + + #region utils + + /// + /// Utility function called by Group-Object to create Groups. + /// + /// Input object that needs to be grouped. + /// True if we are not accumulating objects. + /// List containing Groups. + /// Dictionary used to keep track of the groups with hash of the property values being the key. + /// The Comparer to be used while comparing to check if new group has to be created. + private static void DoGrouping( + OrderByPropertyEntry currentObjectEntry, + bool noElement, + List groups, + Dictionary groupInfoDictionary, + OrderByPropertyComparer orderByPropertyComparer) + { + var currentObjectOrderValues = currentObjectEntry.orderValues; + if (currentObjectOrderValues != null && currentObjectOrderValues.Count > 0) + { + object currentTupleObject = PSTuple.ArrayToTuple(currentObjectOrderValues); + + if (groupInfoDictionary.TryGetValue(currentTupleObject, out var currentGroupInfo)) + { + // add this inputObject to an existing group + currentGroupInfo.Add(currentObjectEntry.inputObject); + } + else + { + bool isCurrentItemGrouped = false; + + for (int groupsIndex = 0; groupsIndex < groups.Count; groupsIndex++) + { + // Check if the current input object can be converted to one of the already known types + // by looking up in the type to GroupInfo mapping. + if (orderByPropertyComparer.Compare(groups[groupsIndex].GroupValue, currentObjectEntry) == 0) + { + groups[groupsIndex].Add(currentObjectEntry.inputObject); + isCurrentItemGrouped = true; + break; + } + } + + if (!isCurrentItemGrouped) + { + // create a new group + s_tracer.WriteLine("Create a new group: {0}", currentObjectOrderValues); + GroupInfo newObjGrp = noElement ? new GroupInfoNoElement(currentObjectEntry) : new GroupInfo(currentObjectEntry); + groups.Add(newObjGrp); + + groupInfoDictionary.Add(currentTupleObject, newObjGrp); + } + } + } + } + + /// + /// Utility function called by Group-Object to create Groups. + /// + /// Input object that needs to be grouped. + /// True if we are not accumulating objects. + /// List containing Groups. + /// Dictionary used to keep track of the groups with hash of the property values being the key. + /// The Comparer to be used while comparing to check if new group has to be created. + private static void DoOrderedGrouping( + OrderByPropertyEntry currentObjectEntry, + bool noElement, + List groups, + Dictionary groupInfoDictionary, + OrderByPropertyComparer orderByPropertyComparer) + { + var currentObjectOrderValues = currentObjectEntry.orderValues; + if (currentObjectOrderValues != null && currentObjectOrderValues.Count > 0) + { + object currentTupleObject = PSTuple.ArrayToTuple(currentObjectOrderValues); + + if (groupInfoDictionary.TryGetValue(currentTupleObject, out var currentGroupInfo)) + { + // add this inputObject to an existing group + currentGroupInfo.Add(currentObjectEntry.inputObject); + } + else + { + bool isCurrentItemGrouped = false; + + if (groups.Count > 0) + { + var lastGroup = groups[groups.Count - 1]; + + // Check if the current input object can be converted to one of the already known types + // by looking up in the type to GroupInfo mapping. + if (orderByPropertyComparer.Compare(lastGroup.GroupValue, currentObjectEntry) == 0) + { + lastGroup.Add(currentObjectEntry.inputObject); + isCurrentItemGrouped = true; + } + } + + if (!isCurrentItemGrouped) + { + // create a new group + s_tracer.WriteLine("Create a new group: {0}", currentObjectOrderValues); + GroupInfo newObjGrp = noElement + ? new GroupInfoNoElement(currentObjectEntry) + : new GroupInfo(currentObjectEntry); + + groups.Add(newObjGrp); + + groupInfoDictionary.Add(currentTupleObject, newObjGrp); + } + } + } + } + + private void WriteNonTerminatingError(Exception exception, string resourceIdAndErrorId, ErrorCategory category) + { + Exception ex = new(StringUtil.Format(resourceIdAndErrorId), exception); + WriteError(new ErrorRecord(ex, resourceIdAndErrorId, category, null)); + } + + #endregion utils + + /// + /// Process every input object to group them. + /// + protected override void ProcessRecord() + { + if (InputObject != null && InputObject != AutomationNull.Value) + { + OrderByPropertyEntry currentEntry; + + if (!_hasProcessedFirstInputObject) + { + Property ??= OrderByProperty.GetDefaultKeyPropertySet(InputObject); + + _orderByProperty.ProcessExpressionParameter(this, Property); + + if (AsString && !AsHashTable) + { + ArgumentException ex = new(UtilityCommonStrings.GroupObjectWithHashTable); + ErrorRecord er = new(ex, "ArgumentException", ErrorCategory.InvalidArgument, AsString); + ThrowTerminatingError(er); + } + + if (AsHashTable && !AsString && (Property != null && (Property.Length > 1 || _orderByProperty.MshParameterList.Count > 1))) + { + ArgumentException ex = new(UtilityCommonStrings.GroupObjectSingleProperty); + ErrorRecord er = new(ex, "ArgumentException", ErrorCategory.InvalidArgument, Property); + ThrowTerminatingError(er); + } + + currentEntry = _orderByProperty.CreateOrderByPropertyEntry(this, InputObject, CaseSensitive, _cultureInfo); + bool[] ascending = new bool[currentEntry.orderValues.Count]; + for (int index = 0; index < currentEntry.orderValues.Count; index++) + { + ascending[index] = true; + } + + _orderByPropertyComparer = new OrderByPropertyComparer(ascending, _cultureInfo, CaseSensitive); + + _hasProcessedFirstInputObject = true; + } + else + { + currentEntry = _orderByProperty.CreateOrderByPropertyEntry(this, InputObject, CaseSensitive, _cultureInfo); + } + + _entriesToOrder.Add(currentEntry); + + var currentEntryOrderValues = currentEntry.orderValues; + if (!_hasDifferentValueTypes) + { + UpdateOrderPropertyTypeInfo(currentEntryOrderValues); + } + } + } + + private void UpdateOrderPropertyTypeInfo(List currentEntryOrderValues) + { + if (_propertyTypesCandidate == null) + { + _propertyTypesCandidate = currentEntryOrderValues.Select(static c => PSObject.Base(c.PropertyValue)?.GetType()).ToArray(); + return; + } + + if (_propertyTypesCandidate.Length != currentEntryOrderValues.Count) + { + _hasDifferentValueTypes = true; + return; + } + + // check all the types we group on. + // if we find more than one set of types, _hasDifferentValueTypes is set to true, + // and we are forced to take a slower code path when we group our objects + for (int i = 0; i < _propertyTypesCandidate.Length; i++) + { + var candidateType = _propertyTypesCandidate[i]; + var propertyType = PSObject.Base(currentEntryOrderValues[i].PropertyValue)?.GetType(); + if (propertyType == null) + { + // we ignore properties without values. We can always compare against null. + continue; + } + + // if we haven't gotten a type for a property yet, update it when we do get a value + if (propertyType != candidateType) + { + if (candidateType == null) + { + _propertyTypesCandidate[i] = propertyType; + } + else + { + _hasDifferentValueTypes = true; + break; + } + } + } + } + + /// + /// Completes the processing of the gathered group objects. + /// + protected override void EndProcessing() + { + if (!_hasDifferentValueTypes) + { + // using OrderBy to get stable sort. + // fast path when we only have the same object types to group + foreach (var entry in _entriesToOrder.Order(_orderByPropertyComparer)) + { + DoOrderedGrouping(entry, NoElement, _groups, _tupleToGroupInfoMappingDictionary, _orderByPropertyComparer); + if (Stopping) + { + return; + } + } + } + else + { + foreach (var entry in _entriesToOrder) + { + DoGrouping(entry, NoElement, _groups, _tupleToGroupInfoMappingDictionary, _orderByPropertyComparer); + if (Stopping) + { + return; + } + } + } + + s_tracer.WriteLine(_groups.Count); + if (_groups.Count > 0) + { + if (AsHashTable.IsPresent) + { + StringComparer comparer = CaseSensitive.IsPresent + ? StringComparer.CurrentCulture + : StringComparer.CurrentCultureIgnoreCase; + var hashtable = new Hashtable(comparer); + try + { + if (AsString) + { + foreach (GroupInfo grp in _groups) + { + hashtable.Add(grp.Name, grp.Group); + } + } + else + { + foreach (GroupInfo grp in _groups) + { + hashtable.Add(PSObject.Base(grp.Values[0]), grp.Group); + } + } + } + catch (ArgumentException e) + { + WriteNonTerminatingError(e, UtilityCommonStrings.InvalidOperation, ErrorCategory.InvalidArgument); + return; + } + + WriteObject(hashtable); + } + else + { + WriteObject(_groups, true); + } + } + } + } +} diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ImplicitRemotingCommands.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ImplicitRemotingCommands.cs index e694403c6b8..d01d144caca 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ImplicitRemotingCommands.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ImplicitRemotingCommands.cs @@ -1,55 +1,49 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. using System; using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; -using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; using System.Management.Automation; -using System.Management.Automation.Language; using System.Management.Automation.Internal; +using System.Management.Automation.Language; using System.Management.Automation.Remoting; using System.Management.Automation.Runspaces; using System.Reflection; +using System.Security.Cryptography.X509Certificates; using System.Text; using System.Text.RegularExpressions; using System.Xml; -using System.Security.Cryptography.X509Certificates; using Dbg = System.Management.Automation.Diagnostics; -// FxCop suppressions for resource strings: -[module: SuppressMessage("Microsoft.Naming", "CA1703:ResourceStringsShouldBeSpelledCorrectly", Scope = "resource", Target = "ImplicitRemotingStrings.resources", MessageId = "runspace")] -[module: SuppressMessage("Microsoft.Naming", "CA1703:ResourceStringsShouldBeSpelledCorrectly", Scope = "resource", Target = "ImplicitRemotingStrings.resources", MessageId = "Runspace")] - namespace Microsoft.PowerShell.Commands { using PowerShell = System.Management.Automation.PowerShell; /// /// This class implements Export-PSSession cmdlet. - /// Spec: TBD + /// Spec: TBD. /// - [Cmdlet(VerbsData.Export, "PSSession", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=135213")] + [Cmdlet(VerbsData.Export, "PSSession", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2096604")] [OutputType(typeof(FileInfo))] public sealed class ExportPSSessionCommand : ImplicitRemotingCommandBase { /// /// Version of the script generator used (by this Export-PSSession cmdlet) to generate psm1 and psd1 files. /// Generated script checks this version to see if it needs to be regenerated. There are 2 situations where this is needed - /// 1. the script needs to be regenerated because a bug fix made previous versions incompatible with the rest of the system (i.e. with ObjectModelWrapper) - /// 2. ths script needs to be regenerated because a security vulnerability was found inside generated code (there is no way to service generated code, but we can service the dll that reports the version that the generated script checks against) + /// 1. the script needs to be regenerated because a bug fix made previous versions incompatible with the rest of the system (i.e. with ObjectModelWrapper). + /// 2. ths script needs to be regenerated because a security vulnerability was found inside generated code (there is no way to service generated code, but we can service the dll that reports the version that the generated script checks against). /// public static Version VersionOfScriptGenerator { get { return ImplicitRemotingCodeGenerator.VersionOfScriptWriter; } } #region Parameters /// - /// Mandatory file name to write to + /// Mandatory file name to write to. /// [Parameter(Mandatory = true, Position = 1)] [ValidateNotNullOrEmpty] @@ -66,30 +60,37 @@ public SwitchParameter Force { return new SwitchParameter(_force); } + set { _force = value.IsPresent; } } + private bool _force; /// - /// Encoding optional flag + /// Encoding optional flag. /// [Parameter] - [ValidateSetAttribute(new string[] { "Unicode", "UTF7", "UTF8", "ASCII", "UTF32", "BigEndianUnicode", "Default", "OEM" })] - public string Encoding + [ArgumentToEncodingTransformation] + [ArgumentEncodingCompletions] + [ValidateNotNullOrEmpty] + public Encoding Encoding { get { - return _encoding.GetType().Name; + return _encoding; } + set { - _encoding = EncodingConversion.Convert(this, value); + EncodingConversion.WarnIfObsolete(this, value); + _encoding = value; } } - private Encoding _encoding = System.Text.Encoding.UTF8; + + private Encoding _encoding = Encoding.Default; #endregion Parameters @@ -125,8 +126,8 @@ protected override void BeginProcessing() // Throw out terminating error if this is the case. if (IsModuleSpecified && IsFullyQualifiedModuleSpecified) { - string errMsg = StringUtil.Format(SessionStateStrings.GetContent_TailAndHeadCannotCoexist, "Module", "FullyQualifiedModule"); - ErrorRecord error = new ErrorRecord(new InvalidOperationException(errMsg), "ModuleAndFullyQualifiedModuleCannotBeSpecifiedTogether", ErrorCategory.InvalidOperation, null); + string errMsg = StringUtil.Format(SessionStateStrings.GetContent_TailAndHeadCannotCoexist, nameof(Module), nameof(FullyQualifiedModule)); + ErrorRecord error = new(new InvalidOperationException(errMsg), "ModuleAndFullyQualifiedModuleCannotBeSpecifiedTogether", ErrorCategory.InvalidOperation, null); ThrowTerminatingError(error); } @@ -144,7 +145,7 @@ protected override void BeginProcessing() List generatedFiles = GenerateProxyModule( tempDirectory, Path.GetFileName(directory.FullName), - _encoding, + Encoding, _force, listOfCommandMetadata, alias2resolvedCommandName, @@ -162,9 +163,9 @@ protected override void BeginProcessing() /// /// This class implements Import-PSSession cmdlet. - /// Spec: http://cmdletdesigner/SpecViewer/Default.aspx?Project=PowerShell&Cmdlet=Import-Command + /// Spec: http://cmdletdesigner/SpecViewer/Default.aspx?Project=PowerShell&Cmdlet=Import-Command . /// - [Cmdlet(VerbsData.Import, "PSSession", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=135221")] + [Cmdlet(VerbsData.Import, "PSSession", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2096712")] [OutputType(typeof(PSModuleInfo))] public sealed class ImportPSSessionCommand : ImplicitRemotingCommandBase { @@ -199,7 +200,7 @@ private void RegisterModuleCleanUp(PSModuleInfo moduleInfo) { if (moduleInfo == null) { - throw PSTraceSource.NewArgumentNullException("moduleInfo"); + throw PSTraceSource.NewArgumentNullException(nameof(moduleInfo)); } // Note: we are using this.Context.Events to make sure that the event handler @@ -250,14 +251,15 @@ private PSModuleInfo CreateModule(string manifestFile) #region Extra parameters /// - /// This parameter specified a prefix used to modify names of imported commands + /// This parameter specified a prefix used to modify names of imported commands. /// [Parameter] [ValidateNotNullOrEmpty] public new string Prefix { - set { base.Prefix = value; } get { return base.Prefix; } + + set { base.Prefix = value; } } /// @@ -269,6 +271,7 @@ private PSModuleInfo CreateModule(string manifestFile) public SwitchParameter DisableNameChecking { get { return _disableNameChecking; } + set { _disableNameChecking = value; } } @@ -285,8 +288,8 @@ protected override void BeginProcessing() // Throw out terminating error if this is the case. if (IsModuleSpecified && IsFullyQualifiedModuleSpecified) { - string errMsg = StringUtil.Format(SessionStateStrings.GetContent_TailAndHeadCannotCoexist, "Module", "FullyQualifiedModule"); - ErrorRecord error = new ErrorRecord(new InvalidOperationException(errMsg), "ModuleAndFullyQualifiedModuleCannotBeSpecifiedTogether", ErrorCategory.InvalidOperation, null); + string errMsg = StringUtil.Format(SessionStateStrings.GetContent_TailAndHeadCannotCoexist, nameof(Module), nameof(FullyQualifiedModule)); + ErrorRecord error = new(new InvalidOperationException(errMsg), "ModuleAndFullyQualifiedModuleCannotBeSpecifiedTogether", ErrorCategory.InvalidOperation, null); ThrowTerminatingError(error); } @@ -313,6 +316,7 @@ protected override void BeginProcessing() manifestFile = file; } } + Dbg.Assert(manifestFile != null, "A psd1 file should always be generated"); PSModuleInfo moduleInfo = this.CreateModule(manifestFile); @@ -323,7 +327,7 @@ protected override void BeginProcessing() } /// - /// Base class for implicit remoting cmdlets + /// Base class for implicit remoting cmdlets. /// public class ImplicitRemotingCommandBase : PSCmdlet { @@ -349,10 +353,9 @@ internal ImplicitRemotingCommandBase() #region related to Get-Command /// - /// Gets or sets the path(s) or name(s) of the commands to retrieve + /// Gets or sets the path(s) or name(s) of the commands to retrieve. /// [Parameter(Position = 2)] - [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] [Alias("Name")] public string[] CommandName { @@ -360,6 +363,7 @@ public string[] CommandName { return _commandNameParameter; } + set { _commandNameParameter = value; @@ -369,11 +373,12 @@ public string[] CommandName WildcardOptions.CultureInvariant | WildcardOptions.IgnoreCase); } } + private string[] _commandNameParameter; private Collection _commandNamePatterns; // initialized to default value in the constructor /// - /// Allows shadowing and/or overwriting of existing local/client commands + /// Allows shadowing and/or overwriting of existing local/client commands. /// [Parameter] public SwitchParameter AllowClobber { get; set; } = new SwitchParameter(false); @@ -386,23 +391,24 @@ public string[] CommandName [AllowNull] [AllowEmptyCollection] [Alias("Args")] - [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] public object[] ArgumentList { get { return _commandArgs; } + set { _commandArgs = value; _commandParameterSpecified = true; } } + private object[] _commandArgs; /// - /// Gets or sets the type of the command to get + /// Gets or sets the type of the command to get. /// [Parameter] [Alias("Type")] @@ -412,6 +418,7 @@ public CommandTypes CommandType { return _commandType; } + set { _commandType = value; @@ -422,11 +429,9 @@ public CommandTypes CommandType private CommandTypes _commandType = CommandTypes.All & (~(CommandTypes.Application | CommandTypes.Script | CommandTypes.ExternalScript)); /// - /// Gets or sets the PSSnapin parameter to the cmdlet + /// Gets or sets the PSSnapin parameter to the cmdlet. /// [Parameter] - [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] - [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Snapin")] [Alias("PSSnapin")] [ValidateNotNull] public string[] Module @@ -438,22 +443,20 @@ public string[] Module set { - if (value == null) - { - value = new string[0]; - } + value ??= Array.Empty(); + _PSSnapins = value; _commandParameterSpecified = true; IsModuleSpecified = true; } } - private string[] _PSSnapins = new string[0]; + + private string[] _PSSnapins = Array.Empty(); internal bool IsModuleSpecified = false; /// - /// Gets or sets the FullyQualifiedModule parameter to the cmdlet + /// Gets or sets the FullyQualifiedModule parameter to the cmdlet. /// [Parameter] - [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] [ValidateNotNull] public ModuleSpecification[] FullyQualifiedModule { @@ -468,11 +471,13 @@ public ModuleSpecification[] FullyQualifiedModule { _moduleSpecifications = value; } + _commandParameterSpecified = true; IsFullyQualifiedModuleSpecified = true; } } - private ModuleSpecification[] _moduleSpecifications = new ModuleSpecification[0]; + + private ModuleSpecification[] _moduleSpecifications = Array.Empty(); internal bool IsFullyQualifiedModuleSpecified = false; private bool _commandParameterSpecified; // initialized to default value in the constructor @@ -482,9 +487,8 @@ public ModuleSpecification[] FullyQualifiedModule #region related to F&O /// - /// Gets or sets the types for which we should get formatting and output data + /// Gets or sets the types for which we should get formatting and output data. /// - [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] [Parameter(Position = 3)] public string[] FormatTypeName { @@ -492,6 +496,7 @@ public string[] FormatTypeName { return _formatTypeNameParameter; } + set { _formatTypeNameParameter = value; @@ -501,6 +506,7 @@ public string[] FormatTypeName WildcardOptions.CultureInvariant | WildcardOptions.IgnoreCase); } } + private string[] _formatTypeNameParameter; // initialized to default value in the constructor private Collection _formatTypeNamePatterns; private bool _formatTypeNamesSpecified; // initialized to default value in the constructor @@ -510,9 +516,9 @@ public string[] FormatTypeName #region Related to modules /// - /// This parameter specified a prefix used to modify names of imported commands + /// This parameter specified a prefix used to modify names of imported commands. /// - internal string Prefix { set; get; } = string.Empty; + internal string Prefix { get; set; } = string.Empty; /// /// Gets or sets the certificate with which to sign the format file and psm1 file. @@ -524,11 +530,10 @@ public string[] FormatTypeName /// /// The PSSession object describing the remote runspace - /// using which the specified cmdlet operation will be performed + /// using which the specified cmdlet operation will be performed. /// [Parameter(Mandatory = true, Position = 0)] [ValidateNotNull] - [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Runspace")] public PSSession Session { get; set; } #endregion Parameters @@ -539,7 +544,7 @@ internal ErrorDetails GetErrorDetails(string errorId, params object[] args) { if (string.IsNullOrEmpty(errorId)) { - throw PSTraceSource.NewArgumentNullException("errorId"); + throw PSTraceSource.NewArgumentNullException(nameof(errorId)); } return new ErrorDetails( @@ -551,11 +556,11 @@ internal ErrorDetails GetErrorDetails(string errorId, params object[] args) private ErrorRecord GetErrorNoCommandsImportedBecauseOfSkipping() { - string errorId = "ErrorNoCommandsImportedBecauseOfSkipping"; + const string errorId = "ErrorNoCommandsImportedBecauseOfSkipping"; ErrorDetails details = this.GetErrorDetails(errorId); - ErrorRecord errorRecord = new ErrorRecord( + ErrorRecord errorRecord = new( new ArgumentException(details.Message), errorId, ErrorCategory.InvalidResult, @@ -569,14 +574,14 @@ private ErrorRecord GetErrorMalformedDataFromRemoteCommand(string commandName) { if (string.IsNullOrEmpty(commandName)) { - throw PSTraceSource.NewArgumentNullException("commandName"); + throw PSTraceSource.NewArgumentNullException(nameof(commandName)); } - string errorId = "ErrorMalformedDataFromRemoteCommand"; + const string errorId = "ErrorMalformedDataFromRemoteCommand"; ErrorDetails details = this.GetErrorDetails(errorId, commandName); - ErrorRecord errorRecord = new ErrorRecord( + ErrorRecord errorRecord = new( new ArgumentException(details.Message), errorId, ErrorCategory.InvalidResult, @@ -590,14 +595,14 @@ private ErrorRecord GetErrorCommandSkippedBecauseOfShadowing(string commandNames { if (string.IsNullOrEmpty(commandNames)) { - throw PSTraceSource.NewArgumentNullException("commandNames"); + throw PSTraceSource.NewArgumentNullException(nameof(commandNames)); } - string errorId = "ErrorCommandSkippedBecauseOfShadowing"; + const string errorId = "ErrorCommandSkippedBecauseOfShadowing"; ErrorDetails details = this.GetErrorDetails(errorId, commandNames); - ErrorRecord errorRecord = new ErrorRecord( + ErrorRecord errorRecord = new( new InvalidOperationException(details.Message), errorId, ErrorCategory.InvalidData, @@ -611,14 +616,14 @@ private ErrorRecord GetErrorSkippedNonRequestedCommand(string commandName) { if (string.IsNullOrEmpty(commandName)) { - throw PSTraceSource.NewArgumentNullException("commandName"); + throw PSTraceSource.NewArgumentNullException(nameof(commandName)); } - string errorId = "ErrorSkippedNonRequestedCommand"; + const string errorId = "ErrorSkippedNonRequestedCommand"; ErrorDetails details = this.GetErrorDetails(errorId, commandName); - ErrorRecord errorRecord = new ErrorRecord( + ErrorRecord errorRecord = new( new InvalidOperationException(details.Message), errorId, ErrorCategory.ResourceExists, @@ -632,14 +637,14 @@ private ErrorRecord GetErrorSkippedNonRequestedTypeDefinition(string typeName) { if (string.IsNullOrEmpty(typeName)) { - throw PSTraceSource.NewArgumentNullException("typeName"); + throw PSTraceSource.NewArgumentNullException(nameof(typeName)); } - string errorId = "ErrorSkippedNonRequestedTypeDefinition"; + const string errorId = "ErrorSkippedNonRequestedTypeDefinition"; ErrorDetails details = this.GetErrorDetails(errorId, typeName); - ErrorRecord errorRecord = new ErrorRecord( + ErrorRecord errorRecord = new( new InvalidOperationException(details.Message), errorId, ErrorCategory.ResourceExists, @@ -653,14 +658,14 @@ private ErrorRecord GetErrorSkippedUnsafeCommandName(string commandName) { if (string.IsNullOrEmpty(commandName)) { - throw PSTraceSource.NewArgumentNullException("commandName"); + throw PSTraceSource.NewArgumentNullException(nameof(commandName)); } - string errorId = "ErrorSkippedUnsafeCommandName"; + const string errorId = "ErrorSkippedUnsafeCommandName"; ErrorDetails details = this.GetErrorDetails(errorId, commandName); - ErrorRecord errorRecord = new ErrorRecord( + ErrorRecord errorRecord = new( new InvalidOperationException(details.Message), errorId, ErrorCategory.InvalidData, @@ -674,23 +679,25 @@ private ErrorRecord GetErrorSkippedUnsafeNameInMetadata(string commandName, stri { if (string.IsNullOrEmpty(commandName)) { - throw PSTraceSource.NewArgumentNullException("commandName"); + throw PSTraceSource.NewArgumentNullException(nameof(commandName)); } + if (string.IsNullOrEmpty(nameType)) { - throw PSTraceSource.NewArgumentNullException("nameType"); + throw PSTraceSource.NewArgumentNullException(nameof(nameType)); } + Dbg.Assert(nameType.Equals("Alias") || nameType.Equals("ParameterSet") || nameType.Equals("Parameter"), "nameType matches resource names"); if (string.IsNullOrEmpty(name)) { - throw PSTraceSource.NewArgumentNullException("name"); + throw PSTraceSource.NewArgumentNullException(nameof(name)); } string errorId = "ErrorSkippedUnsafe" + nameType + "Name"; ErrorDetails details = this.GetErrorDetails(errorId, commandName, name); - ErrorRecord errorRecord = new ErrorRecord( + ErrorRecord errorRecord = new( new InvalidOperationException(details.Message), errorId, ErrorCategory.InvalidData, @@ -704,11 +711,12 @@ private ErrorRecord GetErrorFromRemoteCommand(string commandName, RuntimeExcepti { if (string.IsNullOrEmpty(commandName)) { - throw PSTraceSource.NewArgumentNullException("commandName"); + throw PSTraceSource.NewArgumentNullException(nameof(commandName)); } + if (runtimeException == null) { - throw PSTraceSource.NewArgumentNullException("runtimeException"); + throw PSTraceSource.NewArgumentNullException(nameof(runtimeException)); } string errorId; @@ -718,8 +726,7 @@ private ErrorRecord GetErrorFromRemoteCommand(string commandName, RuntimeExcepti // // handle recognized types of exceptions first // - RemoteException remoteException = runtimeException as RemoteException; - if ((remoteException != null) && (remoteException.SerializedRemoteException != null)) + if ((runtimeException is RemoteException remoteException) && (remoteException.SerializedRemoteException != null)) { if (Deserializer.IsInstanceOfType(remoteException.SerializedRemoteException, typeof(CommandNotFoundException))) { @@ -757,14 +764,14 @@ private ErrorRecord GetErrorCouldntResolvedAlias(string aliasName) { if (string.IsNullOrEmpty(aliasName)) { - throw PSTraceSource.NewArgumentNullException("aliasName"); + throw PSTraceSource.NewArgumentNullException(nameof(aliasName)); } - string errorId = "ErrorCouldntResolveAlias"; + const string errorId = "ErrorCouldntResolveAlias"; ErrorDetails details = this.GetErrorDetails(errorId, aliasName); - ErrorRecord errorRecord = new ErrorRecord( + ErrorRecord errorRecord = new( new ArgumentException(details.Message), errorId, ErrorCategory.OperationTimeout, @@ -778,14 +785,14 @@ private ErrorRecord GetErrorNoResultsFromRemoteEnd(string commandName) { if (string.IsNullOrEmpty(commandName)) { - throw PSTraceSource.NewArgumentNullException("commandName"); + throw PSTraceSource.NewArgumentNullException(nameof(commandName)); } - string errorId = "ErrorNoResultsFromRemoteEnd"; + const string errorId = "ErrorNoResultsFromRemoteEnd"; ErrorDetails details = this.GetErrorDetails(errorId, commandName); - ErrorRecord errorRecord = new ErrorRecord( + ErrorRecord errorRecord = new( new ArgumentException(details.Message), errorId, ErrorCategory.InvalidResult, @@ -795,13 +802,14 @@ private ErrorRecord GetErrorNoResultsFromRemoteEnd(string commandName) return errorRecord; } - private List _commandsSkippedBecauseOfShadowing = new List(); + private readonly List _commandsSkippedBecauseOfShadowing = new(); + private void ReportSkippedCommands() { if (_commandsSkippedBecauseOfShadowing.Count != 0) { string skippedCommands = string.Join(", ", _commandsSkippedBecauseOfShadowing.ToArray()); - ErrorRecord errorRecord = this.GetErrorCommandSkippedBecauseOfShadowing(skippedCommands.ToString()); + ErrorRecord errorRecord = this.GetErrorCommandSkippedBecauseOfShadowing(skippedCommands); this.WriteWarning(errorRecord.ErrorDetails.Message); } } @@ -827,6 +835,7 @@ private bool IsCommandNameMatchingParameters(string commandName) } private Dictionary _existingCommands; + private Dictionary ExistingCommands { get @@ -834,7 +843,7 @@ private Dictionary ExistingCommands if (_existingCommands == null) { _existingCommands = new Dictionary(StringComparer.OrdinalIgnoreCase); - CommandSearcher searcher = new CommandSearcher( + CommandSearcher searcher = new( "*", SearchResolutionOptions.CommandNameIsPattern | SearchResolutionOptions.ResolveAliasPatterns | SearchResolutionOptions.ResolveFunctionPatterns, CommandTypes.All, @@ -844,6 +853,7 @@ private Dictionary ExistingCommands _existingCommands[commandInfo.Name] = null; } } + return _existingCommands; } } @@ -852,7 +862,7 @@ private bool IsShadowingExistingCommands(string commandName) { commandName = ModuleCmdletBase.AddPrefixToCommandName(commandName, this.Prefix); - CommandSearcher searcher = new CommandSearcher(commandName, SearchResolutionOptions.None, CommandTypes.All, this.Context); + CommandSearcher searcher = new(commandName, SearchResolutionOptions.None, CommandTypes.All, this.Context); foreach (string expandedCommandName in searcher.ConstructSearchPatternsFromName(commandName)) { if (this.ExistingCommands.ContainsKey(expandedCommandName)) @@ -865,7 +875,7 @@ private bool IsShadowingExistingCommands(string commandName) } /// - /// Returns true if command doesn't shadow OR is in the -AllowShadowing parameter + /// Returns true if command doesn't shadow OR is in the -AllowShadowing parameter. /// /// /// @@ -873,7 +883,7 @@ private bool IsCommandNameAllowedForImport(string commandName) { if (string.IsNullOrEmpty(commandName)) { - throw PSTraceSource.NewArgumentNullException("commandName"); + throw PSTraceSource.NewArgumentNullException(nameof(commandName)); } if (this.AllowClobber.IsPresent) @@ -1010,6 +1020,7 @@ private T GetPropertyValue(string commandName, PSObject pso, string propertyN { this.ThrowTerminatingError(this.GetErrorMalformedDataFromRemoteCommand(commandName)); } + return ConvertTo(commandName, property.Value, nullOk); } @@ -1029,10 +1040,7 @@ private List RehydrateList(string commandName, PSObject deserializedObject private List RehydrateList(string commandName, object deserializedList, Func itemRehydrator) { - if (itemRehydrator == null) - { - itemRehydrator = delegate (PSObject pso) { return ConvertTo(commandName, pso); }; - } + itemRehydrator ??= (PSObject pso) => ConvertTo(commandName, pso); List result = null; @@ -1051,17 +1059,18 @@ private List RehydrateList(string commandName, object deserializedList, Fu return result; } - private Dictionary RehydrateDictionary(string commandName, PSObject deserializedObject, string propertyName, Func valueRehydrator) + private Dictionary RehydrateDictionary( + string commandName, + PSObject deserializedObject, + string propertyName, + Func valueRehydrator) { Dbg.Assert(deserializedObject != null, "deserializedObject parameter != null"); Dbg.Assert(!string.IsNullOrEmpty(propertyName), "propertyName parameter != null"); - if (valueRehydrator == null) - { - valueRehydrator = delegate (PSObject pso) { return ConvertTo(commandName, pso); }; - } + valueRehydrator ??= (PSObject pso) => ConvertTo(commandName, pso); - Dictionary result = new Dictionary(); + Dictionary result = new(); PSPropertyInfo deserializedDictionaryProperty = deserializedObject.Properties[propertyName]; if (deserializedDictionaryProperty != null) { @@ -1070,10 +1079,10 @@ private Dictionary RehydrateDictionary(string commandName, PSObject { foreach (DictionaryEntry deserializedItem in deserializedDictionary) { - K itemKey = ConvertTo(commandName, deserializedItem.Key); + TKey itemKey = ConvertTo(commandName, deserializedItem.Key); PSObject deserializedItemValue = ConvertTo(commandName, deserializedItem.Value); - V itemValue = valueRehydrator(deserializedItemValue); + TValue itemValue = valueRehydrator(deserializedItemValue); result.Add(itemKey, itemValue); } @@ -1089,10 +1098,10 @@ private Dictionary RehydrateDictionary(string commandName, PSObject /// /// Validates that a name or identifier is safe to use in generated code - /// (i.e. it can't be used for code injection attacks) + /// (i.e. it can't be used for code injection attacks). /// - /// name to validate - /// true if the name is safe; false otherwise + /// Name to validate. + /// if the name is safe; otherwise. private static bool IsSafeNameOrIdentifier(string name) { // '.' is needed for stuff like net.exe @@ -1107,21 +1116,21 @@ private static bool IsSafeNameOrIdentifier(string name) /// /// Validates that a parameter name is safe to use in generated code - /// (i.e. it can't be used for code injection attacks) + /// (i.e. it can't be used for code injection attacks). /// - /// parameter name to validate - /// true if the name is safe; false otherwise + /// Parameter name to validate. + /// if the name is safe; otherwise. private static bool IsSafeParameterName(string parameterName) { - return IsSafeNameOrIdentifier(parameterName) && !parameterName.Contains(":"); + return IsSafeNameOrIdentifier(parameterName) && !parameterName.Contains(':'); } /// /// Validates that a type can be safely used as a type constraint - /// (i.e. it doesn't introduce any side effects on the client) + /// (i.e. it doesn't introduce any side effects on the client). /// - /// type to validate - /// true if the type is safe; false otherwise + /// Type to validate. + /// if the type is safe; otherwise. private static bool IsSafeTypeConstraint(Type type) { if (type == null) @@ -1163,8 +1172,8 @@ private static bool IsSafeTypeConstraint(Type type) /// Validates that command metadata returned from the (potentially malicious) server is safe. /// Writes error messages if necessary. Modifies command metadata to make it safe if necessary. /// - /// command metadata to verify - /// true if the command metadata is safe; false otherwise + /// Command metadata to verify. + /// if the command metadata is safe; otherwise. private bool IsSafeCommandMetadata(CommandMetadata commandMetadata) { if (!IsCommandNameMatchingParameters(commandMetadata.Name)) @@ -1190,7 +1199,7 @@ private bool IsSafeCommandMetadata(CommandMetadata commandMetadata) } Dbg.Assert(commandMetadata.CommandType == null, "CommandType shouldn't get rehydrated"); - Dbg.Assert(commandMetadata.ImplementsDynamicParameters == false, "Proxies shouldn't do dynamic parameters"); + Dbg.Assert(!commandMetadata.ImplementsDynamicParameters, "Proxies shouldn't do dynamic parameters"); if (commandMetadata.Parameters != null) { @@ -1199,7 +1208,7 @@ private bool IsSafeCommandMetadata(CommandMetadata commandMetadata) Dbg.Assert(parameter.Attributes == null || parameter.Attributes.Count == 0, "Attributes shouldn't get rehydrated"); - // sanitize - remove type constraint that are not whitelisted + // sanitize - remove type constraint that are not allowed if (!IsSafeTypeConstraint(parameter.ParameterType)) { parameter.ParameterType = null; @@ -1270,7 +1279,7 @@ private ParameterMetadata RehydrateParameterMetadata(PSObject deserializedParame { if (deserializedParameterMetadata == null) { - throw PSTraceSource.NewArgumentNullException("deserializedParameterMetadata"); + throw PSTraceSource.NewArgumentNullException(nameof(deserializedParameterMetadata)); } string name = GetPropertyValue("Get-Command", deserializedParameterMetadata, "Name"); @@ -1279,8 +1288,8 @@ private ParameterMetadata RehydrateParameterMetadata(PSObject deserializedParame Type parameterType = RehydrateParameterType(deserializedParameterMetadata); List aliases = RehydrateList("Get-Command", deserializedParameterMetadata, "Aliases", null); - ParameterSetMetadata parameterSetMetadata = new ParameterSetMetadata(int.MinValue, 0, null); - Dictionary parameterSets = new Dictionary(StringComparer.OrdinalIgnoreCase); + ParameterSetMetadata parameterSetMetadata = new(int.MinValue, 0, null); + Dictionary parameterSets = new(StringComparer.OrdinalIgnoreCase); parameterSets.Add(ParameterAttribute.AllParameterSets, parameterSetMetadata); return new ParameterMetadata( @@ -1296,8 +1305,18 @@ private bool IsProxyForCmdlet(Dictionary parameters) // we are not sending CmdletBinding/DefaultParameterSet over the wire anymore // we need to infer IsProxyForCmdlet from presence of all common parameters - foreach (string commonParameterName in Cmdlet.CommonParameters) + // need to exclude `ProgressAction` which may not exist for downlevel platforms + bool isDownLevelRemote = Session.Runspace is RemoteRunspace remoteRunspace + && remoteRunspace.ServerVersion is not null + && remoteRunspace.ServerVersion <= new Version(7, 3); + + foreach (string commonParameterName in CommonParameters) { + if (isDownLevelRemote && commonParameterName == "ProgressAction") + { + continue; + } + if (!parameters.ContainsKey(commonParameterName)) { return false; @@ -1311,7 +1330,7 @@ private CommandMetadata RehydrateCommandMetadata(PSObject deserializedCommandInf { if (deserializedCommandInfo == null) { - throw PSTraceSource.NewArgumentNullException("deserializedCommandInfo"); + throw PSTraceSource.NewArgumentNullException(nameof(deserializedCommandInfo)); } string name = GetPropertyValue("Get-Command", deserializedCommandInfo, "Name"); @@ -1334,13 +1353,13 @@ private CommandMetadata RehydrateCommandMetadata(PSObject deserializedCommandInf // add client-side AsJob parameter parameters.Remove("AsJob"); - ParameterMetadata asJobParameter = new ParameterMetadata("AsJob", typeof(SwitchParameter)); + ParameterMetadata asJobParameter = new("AsJob", typeof(SwitchParameter)); parameters.Add(asJobParameter.Name, asJobParameter); return new CommandMetadata( name: name, commandType: commandType, - isProxyForCmdlet: this.IsProxyForCmdlet(parameters), + isProxyForCmdlet: IsProxyForCmdlet(parameters), defaultParameterSetName: ParameterAttribute.AllParameterSets, supportsShouldProcess: false, confirmImpact: ConfirmImpact.None, @@ -1350,7 +1369,7 @@ private CommandMetadata RehydrateCommandMetadata(PSObject deserializedCommandInf parameters: parameters); } - private int GetCommandTypePriority(CommandTypes commandType) + private static int GetCommandTypePriority(CommandTypes commandType) { switch (commandType) { @@ -1361,7 +1380,6 @@ private int GetCommandTypePriority(CommandTypes commandType) case CommandTypes.Filter: case CommandTypes.Function: case CommandTypes.Script: - case CommandTypes.Workflow: return 20; case CommandTypes.Cmdlet: @@ -1379,12 +1397,12 @@ private int GetCommandTypePriority(CommandTypes commandType) } /// - /// Converts remote (deserialized) CommandInfo objects into CommandMetadata equivalents + /// Converts remote (deserialized) CommandInfo objects into CommandMetadata equivalents. /// - /// Dictionary where rehydrated CommandMetadata are going to be stored - /// Dictionary mapping alias names to resolved command names - /// Remote (deserialized) CommandInfo object - /// CommandMetadata equivalents + /// Dictionary where rehydrated CommandMetadata are going to be stored. + /// Dictionary mapping alias names to resolved command names. + /// Remote (deserialized) CommandInfo object. + /// CommandMetadata equivalents. private void AddRemoteCommandMetadata( Dictionary name2commandMetadata, Dictionary alias2resolvedCommandName, @@ -1400,15 +1418,18 @@ private void AddRemoteCommandMetadata( { return; } + if (resolvedCommandName != null && !IsSafeNameOrIdentifier(commandMetadata.Name)) { this.WriteError(this.GetErrorSkippedUnsafeCommandName(resolvedCommandName)); return; } + if (IsCommandSkippedByServerDeclaration(commandMetadata.Name)) { return; } + if (!IsCommandNameAllowedForImport(commandMetadata.Name)) { return; @@ -1417,8 +1438,8 @@ private void AddRemoteCommandMetadata( CommandMetadata previousCommandWithSameName; if (name2commandMetadata.TryGetValue(commandMetadata.Name, out previousCommandWithSameName)) { - int previousCommandPriority = this.GetCommandTypePriority(previousCommandWithSameName.WrappedCommandType); - int currentCommandPriority = this.GetCommandTypePriority(commandMetadata.WrappedCommandType); + int previousCommandPriority = GetCommandTypePriority(previousCommandWithSameName.WrappedCommandType); + int currentCommandPriority = GetCommandTypePriority(commandMetadata.WrappedCommandType); if (previousCommandPriority < currentCommandPriority) { return; @@ -1563,8 +1584,7 @@ private PowerShell BuildPowerShellForGetFormatData() powerShell.AddParameter("TypeName", this.FormatTypeName); // For remote PS version 5.1 and greater, we need to include the new -PowerShellVersion parameter - RemoteRunspace remoteRunspace = Session.Runspace as RemoteRunspace; - if ((remoteRunspace != null) && (remoteRunspace.ServerVersion != null) && + if ((Session.Runspace is RemoteRunspace remoteRunspace) && (remoteRunspace.ServerVersion != null) && (remoteRunspace.ServerVersion >= new Version(5, 1))) { powerShell.AddParameter("PowerShellVersion", PSVersionInfo.PSVersion); @@ -1576,9 +1596,9 @@ private PowerShell BuildPowerShellForGetFormatData() } /// - /// Gets CommandMetadata objects from remote runspace + /// Gets CommandMetadata objects from remote runspace. /// - /// (rehydrated) CommandMetadata objects + /// (rehydrated) CommandMetadata objects. internal List GetRemoteFormatData() { if ((this.FormatTypeName == null) || (this.FormatTypeName.Length == 0) || @@ -1605,18 +1625,19 @@ internal List GetRemoteFormatData() { DateTime startTime = DateTime.UtcNow; - PSDataCollection asyncOutput = new PSDataCollection(); + PSDataCollection asyncOutput = new(); // process output and errors as soon as possible asyncResult = powerShell.BeginInvoke(null, asyncOutput); int numberOfReceivedObjects = 0; - List result = new List(); + List result = new(); foreach (PSObject deserializedFormatData in asyncOutput) { AddRemoteTypeDefinition(result, deserializedFormatData); this.DuplicatePowerShellStreams(powerShell); this.WriteProgress(startTime, ++numberOfReceivedObjects, expectedCount, ImplicitRemotingStrings.ProgressStatusGetFormatDataProgress); } + this.DuplicatePowerShellStreams(powerShell); powerShell.EndInvoke(asyncResult); @@ -1624,6 +1645,7 @@ internal List GetRemoteFormatData() { this.ThrowTerminatingError(this.GetErrorNoResultsFromRemoteEnd("Get-FormatData")); } + return result; } } @@ -1653,20 +1675,21 @@ private PowerShell BuildPowerShellForGetCommand() { powerShell.AddParameter("Name", this.CommandName); } - powerShell.AddParameter("Module", this.Module); + + powerShell.AddParameter(nameof(Module), this.Module); if (IsFullyQualifiedModuleSpecified) { - powerShell.AddParameter("FullyQualifiedModule", this.FullyQualifiedModule); + powerShell.AddParameter(nameof(FullyQualifiedModule), this.FullyQualifiedModule); } + powerShell.AddParameter("ArgumentList", this.ArgumentList); powerShell.Runspace = Session.Runspace; - powerShell.RemotePowerShell.HostCallReceived += new EventHandler>(HandleHostCallReceived); + powerShell.RemotePowerShell.HostCallReceived += HandleHostCallReceived; return powerShell; } /// - /// /// /// /// @@ -1676,13 +1699,13 @@ private void HandleHostCallReceived(object sender, RemoteDataEventArgs - /// Gets CommandMetadata objects from remote runspace + /// Gets CommandMetadata objects from remote runspace. /// - /// (rehydrated) CommandMetadata objects + /// (rehydrated) CommandMetadata objects. internal List GetRemoteCommandMetadata(out Dictionary alias2resolvedCommandName) { bool isReleaseCandidateBackcompatibilityMode = - this.Session.Runspace.GetRemoteProtocolVersion() == RemotingConstants.ProtocolVersionWin7RC; + this.Session.Runspace.GetRemoteProtocolVersion() == RemotingConstants.ProtocolVersion_2_0; alias2resolvedCommandName = new Dictionary(StringComparer.OrdinalIgnoreCase); if ((this.CommandName == null) || (this.CommandName.Length == 0) || @@ -1713,14 +1736,14 @@ internal List GetRemoteCommandMetadata(out Dictionary name2commandMetadata = - new Dictionary(StringComparer.OrdinalIgnoreCase); + new(StringComparer.OrdinalIgnoreCase); // invoke using (new PowerShellStopper(this.Context, powerShell)) { DateTime startTime = DateTime.UtcNow; - PSDataCollection asyncOutput = new PSDataCollection(); + PSDataCollection asyncOutput = new(); // process output and errors as soon as possible asyncResult = powerShell.BeginInvoke(null, asyncOutput); @@ -1740,6 +1763,7 @@ internal List GetRemoteCommandMetadata(out Dictionary GetRemoteCommandMetadata(out Dictionary(name2commandMetadata.Values); } } @@ -1789,10 +1814,11 @@ private void WriteProgress(string statusDescription, int? percentComplete, int? return; } } + _lastTimeProgressWasWritten = DateTime.UtcNow; string activityDescription = StringUtil.Format(ImplicitRemotingStrings.ProgressActivity); - ProgressRecord progressRecord = new ProgressRecord( + ProgressRecord progressRecord = new( 1905347799, // unique id for ImplicitRemoting (I just picked a random number) activityDescription, statusDescription); @@ -1839,17 +1865,17 @@ private void WriteProgress(DateTime startTime, int currentCount, int expectedCou /// /// Generates a proxy module in the given directory. /// - /// base directory for the module - /// fileName prefix for module files - /// encoding of generated files - /// whether to overwrite files - /// remote commands to generate proxies for - /// dictionary mapping alias names to resolved command names - /// remote format data to generate format.ps1xml for - /// Paths to generated files + /// Base directory for the module. + /// FileName prefix for module files. + /// Encoding of generated files. + /// Whether to overwrite files. + /// Remote commands to generate proxies for. + /// Dictionary mapping alias names to resolved command names. + /// Remote format data to generate format.ps1xml for. + /// Paths to generated files. internal List GenerateProxyModule( DirectoryInfo moduleRootDirectory, - String moduleNamePrefix, + string moduleNamePrefix, Encoding encoding, bool force, List listOfCommandMetadata, @@ -1867,7 +1893,7 @@ internal List GenerateProxyModule( } } - ImplicitRemotingCodeGenerator codeGenerator = new ImplicitRemotingCodeGenerator( + ImplicitRemotingCodeGenerator codeGenerator = new( this.Session, this.ModuleGuid, this.MyInvocation); @@ -1890,15 +1916,15 @@ internal List GenerateProxyModule( #endregion } - internal class ImplicitRemotingCodeGenerator + internal sealed class ImplicitRemotingCodeGenerator { - internal static readonly Version VersionOfScriptWriter = new Version(1, 0); + internal static readonly Version VersionOfScriptWriter = new(1, 0); #region Constructor and shared private data - private PSSession _remoteRunspaceInfo; - private Guid _moduleGuid; - private InvocationInfo _invocationInfo; + private readonly PSSession _remoteRunspaceInfo; + private readonly Guid _moduleGuid; + private readonly InvocationInfo _invocationInfo; internal ImplicitRemotingCodeGenerator( PSSession remoteRunspaceInfo, @@ -1906,7 +1932,7 @@ internal ImplicitRemotingCodeGenerator( InvocationInfo invocationInfo) { Dbg.Assert(remoteRunspaceInfo != null, "Caller should validate remoteRunspaceInfo != null"); - Dbg.Assert(moduleGuid != null, "Caller should validate moduleGuid != null"); + Dbg.Assert(moduleGuid != Guid.Empty, "Caller should validate moduleGuid is not empty"); Dbg.Assert(invocationInfo != null, "Caller should validate invocationInfo != null"); _remoteRunspaceInfo = remoteRunspaceInfo; @@ -1919,25 +1945,22 @@ internal ImplicitRemotingCodeGenerator( #region Code generation helpers /// - /// Gets a connection URI associated with the remote runspace + /// Gets a connection URI associated with the remote runspace. /// - /// Connection URI associated with the remote runspace + /// Connection URI associated with the remote runspace. private string GetConnectionString() { - WSManConnectionInfo connectionInfo = _remoteRunspaceInfo.Runspace.ConnectionInfo as WSManConnectionInfo; - if (connectionInfo != null) + if (_remoteRunspaceInfo.Runspace.ConnectionInfo is WSManConnectionInfo connectionInfo) { return connectionInfo.ConnectionUri.ToString(); } - VMConnectionInfo vmConnectionInfo = _remoteRunspaceInfo.Runspace.ConnectionInfo as VMConnectionInfo; - if (vmConnectionInfo != null) + if (_remoteRunspaceInfo.Runspace.ConnectionInfo is VMConnectionInfo vmConnectionInfo) { return vmConnectionInfo.ComputerName; } - ContainerConnectionInfo containerConnectionInfo = _remoteRunspaceInfo.Runspace.ConnectionInfo as ContainerConnectionInfo; - if (containerConnectionInfo != null) + if (_remoteRunspaceInfo.Runspace.ConnectionInfo is ContainerConnectionInfo containerConnectionInfo) { return containerConnectionInfo.ComputerName; } @@ -1952,23 +1975,24 @@ private string GetConnectionString() return null; } - private string EscapeFunctionNameForRemoteHelp(string name) + private static string EscapeFunctionNameForRemoteHelp(string name) { if (name == null) { - throw PSTraceSource.NewArgumentNullException("name"); + throw PSTraceSource.NewArgumentNullException(nameof(name)); } - StringBuilder result = new StringBuilder(name.Length); + StringBuilder result = new(name.Length); foreach (char c in name) { - if (("\"'`$".IndexOf(c) == (-1)) && - (!char.IsControl(c)) && - (!char.IsWhiteSpace(c))) + if (!"\"'`$".Contains(c) + && !char.IsControl(c) + && !char.IsWhiteSpace(c)) { result.Append(c); } } + return result.ToString(); } @@ -1976,7 +2000,7 @@ private string EscapeFunctionNameForRemoteHelp(string name) ############################################################################## "; - private void GenerateSectionSeparator(TextWriter writer) + private static void GenerateSectionSeparator(TextWriter writer) { writer.Write(SectionSeparator); } @@ -2004,7 +2028,7 @@ private void GenerateManifest(TextWriter writer, string psm1fileName, string for { if (writer == null) { - throw PSTraceSource.NewArgumentNullException("writer"); + throw PSTraceSource.NewArgumentNullException(nameof(writer)); } GenerateTopComment(writer); @@ -2053,7 +2077,6 @@ private void GenerateTopComment(TextWriter writer) throw '{3}' }} - $script:WriteHost = $executionContext.InvokeCommand.GetCommand('Write-Host', [System.Management.Automation.CommandTypes]::Cmdlet) $script:WriteWarning = $executionContext.InvokeCommand.GetCommand('Write-Warning', [System.Management.Automation.CommandTypes]::Cmdlet) $script:WriteInformation = $executionContext.InvokeCommand.GetCommand('Write-Information', [System.Management.Automation.CommandTypes]::Cmdlet) @@ -2077,12 +2100,14 @@ private void GenerateModuleHeader(TextWriter writer) { if (writer == null) { - throw PSTraceSource.NewArgumentNullException("writer"); + throw PSTraceSource.NewArgumentNullException(nameof(writer)); } // In Win8, we are no longer loading all assemblies by default. // So we need to use the fully qualified name when accessing a type in that assembly - string versionOfScriptGenerator = "[" + typeof(ExportPSSessionCommand).AssemblyQualifiedName + "]" + "::VersionOfScriptGenerator"; + Type type = typeof(ExportPSSessionCommand); + string asmName = type.Assembly.GetName().Name; + string versionOfScriptGenerator = $"[{type.FullName}, {asmName}]::VersionOfScriptGenerator"; GenerateTopComment(writer); writer.Write( HeaderTemplate, @@ -2109,11 +2134,12 @@ function Write-PSImplicitRemotingMessage try { & $script:WriteHost -Object $message -ErrorAction SilentlyContinue } catch { } } "; - private void GenerateHelperFunctionsWriteMessage(TextWriter writer) + + private static void GenerateHelperFunctionsWriteMessage(TextWriter writer) { if (writer == null) { - throw PSTraceSource.NewArgumentNullException("writer"); + throw PSTraceSource.NewArgumentNullException(nameof(writer)); } writer.Write(HelperFunctionsWriteMessage); @@ -2163,11 +2189,12 @@ function Set-PSImplicitRemotingSession if ($PSSessionOverride) {{ Set-PSImplicitRemotingSession $PSSessionOverride }} "; - private void GenerateHelperFunctionsSetImplicitRunspace(TextWriter writer) + + private static void GenerateHelperFunctionsSetImplicitRunspace(TextWriter writer) { if (writer == null) { - throw PSTraceSource.NewArgumentNullException("writer"); + throw PSTraceSource.NewArgumentNullException(nameof(writer)); } string runspaceNameTemplate = StringUtil.Format(ImplicitRemotingStrings.ProxyRunspaceNameTemplate); @@ -2194,11 +2221,12 @@ function Get-PSImplicitRemotingSessionOption }} }} "; + private void GenerateHelperFunctionsGetSessionOption(TextWriter writer) { if (writer == null) { - throw PSTraceSource.NewArgumentNullException("writer"); + throw PSTraceSource.NewArgumentNullException(nameof(writer)); } writer.Write( @@ -2219,59 +2247,70 @@ private PSPrimitiveDictionary GetApplicationArguments() private string GenerateNewPSSessionOption() { - StringBuilder result = new StringBuilder("& $script:NewPSSessionOption "); + StringBuilder result = new("& $script:NewPSSessionOption "); - RunspaceConnectionInfo runspaceConnectionInfo = _remoteRunspaceInfo.Runspace.ConnectionInfo as RunspaceConnectionInfo; - if (runspaceConnectionInfo != null) + if (_remoteRunspaceInfo.Runspace.ConnectionInfo is RunspaceConnectionInfo runspaceConnectionInfo) { - result.AppendFormat(null, "-Culture '{0}' ", CodeGeneration.EscapeSingleQuotedStringContent(runspaceConnectionInfo.Culture.ToString())); - result.AppendFormat(null, "-UICulture '{0}' ", CodeGeneration.EscapeSingleQuotedStringContent(runspaceConnectionInfo.UICulture.ToString())); + result.Append(null, $"-Culture '{CodeGeneration.EscapeSingleQuotedStringContent(runspaceConnectionInfo.Culture.ToString())}' "); + result.Append(null, $"-UICulture '{CodeGeneration.EscapeSingleQuotedStringContent(runspaceConnectionInfo.UICulture.ToString())}' "); - result.AppendFormat(null, "-CancelTimeOut {0} ", runspaceConnectionInfo.CancelTimeout); - result.AppendFormat(null, "-IdleTimeOut {0} ", runspaceConnectionInfo.IdleTimeout); - result.AppendFormat(null, "-OpenTimeOut {0} ", runspaceConnectionInfo.OpenTimeout); - result.AppendFormat(null, "-OperationTimeOut {0} ", runspaceConnectionInfo.OperationTimeout); + result.Append(null, $"-CancelTimeOut {runspaceConnectionInfo.CancelTimeout} "); + result.Append(null, $"-IdleTimeOut {runspaceConnectionInfo.IdleTimeout} "); + result.Append(null, $"-OpenTimeOut {runspaceConnectionInfo.OpenTimeout} "); + result.Append(null, $"-OperationTimeOut {runspaceConnectionInfo.OperationTimeout} "); } - WSManConnectionInfo wsmanConnectionInfo = _remoteRunspaceInfo.Runspace.ConnectionInfo as WSManConnectionInfo; - if (wsmanConnectionInfo != null) + if (_remoteRunspaceInfo.Runspace.ConnectionInfo is WSManConnectionInfo wsmanConnectionInfo) { - if (!wsmanConnectionInfo.UseCompression) { result.Append("-NoCompression "); } - if (wsmanConnectionInfo.NoEncryption) { result.Append("-NoEncryption "); } - if (wsmanConnectionInfo.NoMachineProfile) { result.Append("-NoMachineProfile "); } - if (wsmanConnectionInfo.UseUTF16) { result.Append("-UseUTF16 "); } + if (!wsmanConnectionInfo.UseCompression) + { + result.Append("-NoCompression "); + } + + if (wsmanConnectionInfo.NoEncryption) + { + result.Append("-NoEncryption "); + } - if (wsmanConnectionInfo.SkipCACheck) { result.Append("-SkipCACheck "); } - if (wsmanConnectionInfo.SkipCNCheck) { result.Append("-SkipCNCheck "); } - if (wsmanConnectionInfo.SkipRevocationCheck) { result.Append("-SkipRevocationCheck "); } + if (wsmanConnectionInfo.NoMachineProfile) + { + result.Append("-NoMachineProfile "); + } + + if (wsmanConnectionInfo.UseUTF16) + { + result.Append("-UseUTF16 "); + } + + if (wsmanConnectionInfo.SkipCACheck) + { + result.Append("-SkipCACheck "); + } + + if (wsmanConnectionInfo.SkipCNCheck) + { + result.Append("-SkipCNCheck "); + } + + if (wsmanConnectionInfo.SkipRevocationCheck) + { + result.Append("-SkipRevocationCheck "); + } if (wsmanConnectionInfo.MaximumReceivedDataSizePerCommand.HasValue) { - result.AppendFormat( - CultureInfo.InvariantCulture, - "-MaximumReceivedDataSizePerCommand {0} ", - wsmanConnectionInfo.MaximumReceivedDataSizePerCommand.Value); + result.Append(CultureInfo.InvariantCulture, $"-MaximumReceivedDataSizePerCommand {wsmanConnectionInfo.MaximumReceivedDataSizePerCommand.Value} "); } + if (wsmanConnectionInfo.MaximumReceivedObjectSize.HasValue) { - result.AppendFormat( - CultureInfo.InvariantCulture, - "-MaximumReceivedObjectSize {0} ", - wsmanConnectionInfo.MaximumReceivedObjectSize.Value); + result.Append(CultureInfo.InvariantCulture, $"-MaximumReceivedObjectSize {wsmanConnectionInfo.MaximumReceivedObjectSize.Value} "); } - result.AppendFormat( - CultureInfo.InvariantCulture, - "-MaximumRedirection {0} ", - wsmanConnectionInfo.MaximumConnectionRedirectionCount); - result.AppendFormat( - CultureInfo.InvariantCulture, - "-ProxyAccessType {0} ", - wsmanConnectionInfo.ProxyAccessType.ToString()); - result.AppendFormat( - CultureInfo.InvariantCulture, - "-ProxyAuthentication {0} ", - wsmanConnectionInfo.ProxyAuthentication.ToString()); + result.Append(CultureInfo.InvariantCulture, $"-MaximumRedirection {wsmanConnectionInfo.MaximumConnectionRedirectionCount} "); + + result.Append(CultureInfo.InvariantCulture, $"-ProxyAccessType {wsmanConnectionInfo.ProxyAccessType} "); + result.Append(CultureInfo.InvariantCulture, $"-ProxyAuthentication {wsmanConnectionInfo.ProxyAuthentication} "); result.Append(this.GenerateProxyCredentialParameter(wsmanConnectionInfo)); } @@ -2281,7 +2320,7 @@ private string GenerateNewPSSessionOption() result.Append("-ApplicationArguments $("); result.Append("& $script:ImportCliXml -Path $("); result.Append("& $script:JoinPath -Path $PSScriptRoot -ChildPath ApplicationArguments.xml"); - result.Append(")"); + result.Append(')'); result.Append(") "); } @@ -2343,6 +2382,7 @@ function Get-PSImplicitRemotingSession -InstanceId {0} ` -ErrorAction SilentlyContinue ) }} + if (($null -ne $script:PSSession) -and ($script:PSSession.Runspace.RunspaceStateInfo.State -eq 'Disconnected')) {{ # If we are handed a disconnected session, try re-connecting it before creating a new session. @@ -2351,6 +2391,7 @@ function Get-PSImplicitRemotingSession -Session $script:PSSession ` -ErrorAction SilentlyContinue) }} + if (($null -eq $script:PSSession) -or ($script:PSSession.Runspace.RunspaceStateInfo.State -ne 'Opened')) {{ Write-PSImplicitRemotingMessage ('{1}' -f $commandName) @@ -2370,10 +2411,12 @@ function Get-PSImplicitRemotingSession {8} }} + if (($null -eq $script:PSSession) -or ($script:PSSession.Runspace.RunspaceStateInfo.State -ne 'Opened')) {{ throw '{3}' }} + return [Management.Automation.Runspaces.PSSession]$script:PSSession }} "; @@ -2382,7 +2425,7 @@ private void GenerateHelperFunctionsGetImplicitRunspace(TextWriter writer) { if (writer == null) { - throw PSTraceSource.NewArgumentNullException("writer"); + throw PSTraceSource.NewArgumentNullException(nameof(writer)); } string hashString; @@ -2391,7 +2434,7 @@ private void GenerateHelperFunctionsGetImplicitRunspace(TextWriter writer) out hashString, ImplicitRemotingCommandBase.ImplicitRemotingKey, ImplicitRemotingCommandBase.ImplicitRemotingHashKey); - hashString = hashString ?? string.Empty; + hashString ??= string.Empty; writer.Write( HelperFunctionsGetImplicitRunspaceTemplate, @@ -2413,13 +2456,14 @@ private void GenerateHelperFunctionsGetImplicitRunspace(TextWriter writer) }} -ErrorAction SilentlyContinue }} catch {{ }} "; + private string GenerateReimportingOfModules() { - StringBuilder result = new StringBuilder(); + StringBuilder result = new(); - if (_invocationInfo.BoundParameters.ContainsKey("Module")) + if (_invocationInfo.BoundParameters.ContainsKey(nameof(Module))) { - string[] moduleNames = (string[])_invocationInfo.BoundParameters["Module"]; + string[] moduleNames = (string[])_invocationInfo.BoundParameters[nameof(Module)]; foreach (string moduleName in moduleNames) { result.AppendFormat( @@ -2481,8 +2525,7 @@ private string GenerateReimportingOfModules() private string GenerateNewRunspaceExpression() { - VMConnectionInfo vmConnectionInfo = _remoteRunspaceInfo.Runspace.ConnectionInfo as VMConnectionInfo; - if (vmConnectionInfo != null) + if (_remoteRunspaceInfo.Runspace.ConnectionInfo is VMConnectionInfo vmConnectionInfo) { string vmConfigurationName = vmConnectionInfo.ConfigurationName; return string.Format( @@ -2490,12 +2533,11 @@ private string GenerateNewRunspaceExpression() NewVMRunspaceTemplate, /* 0 */ this.GenerateConnectionStringForNewRunspace(), /* 1 */ this.GenerateCredentialParameter(), - /* 2 */ String.IsNullOrEmpty(vmConfigurationName) ? String.Empty : String.Concat("-ConfigurationName ", vmConfigurationName)); + /* 2 */ string.IsNullOrEmpty(vmConfigurationName) ? string.Empty : string.Concat("-ConfigurationName ", vmConfigurationName)); } else { - ContainerConnectionInfo containerConnectionInfo = _remoteRunspaceInfo.Runspace.ConnectionInfo as ContainerConnectionInfo; - if (containerConnectionInfo != null) + if (_remoteRunspaceInfo.Runspace.ConnectionInfo is ContainerConnectionInfo containerConnectionInfo) { string containerConfigurationName = containerConnectionInfo.ContainerProc.ConfigurationName; return string.Format( @@ -2503,7 +2545,7 @@ private string GenerateNewRunspaceExpression() NewContainerRunspaceTemplate, /* 0 */ this.GenerateConnectionStringForNewRunspace(), /* 1 */ containerConnectionInfo.ContainerProc.RunAsAdmin ? "-RunAsAdministrator" : string.Empty, - /* 2 */ String.IsNullOrEmpty(containerConfigurationName) ? String.Empty : String.Concat("-ConfigurationName ", containerConfigurationName)); + /* 2 */ string.IsNullOrEmpty(containerConfigurationName) ? string.Empty : string.Concat("-ConfigurationName ", containerConfigurationName)); } else { @@ -2522,6 +2564,7 @@ private string GenerateNewRunspaceExpression() private const string ComputerNameParameterTemplate = @"-ComputerName '{0}' ` -ApplicationName '{1}' {2} {3} "; + private const string VMIdParameterTemplate = @"-VMId '{0}' "; private const string ContainerIdParameterTemplate = @"-ContainerId '{0}' "; @@ -2536,19 +2579,16 @@ private string GenerateNewRunspaceExpression() /// private string GenerateConnectionStringForNewRunspace() { - WSManConnectionInfo connectionInfo = _remoteRunspaceInfo.Runspace.ConnectionInfo as WSManConnectionInfo; - if (null == connectionInfo) + if (_remoteRunspaceInfo.Runspace.ConnectionInfo is not WSManConnectionInfo connectionInfo) { - VMConnectionInfo vmConnectionInfo = _remoteRunspaceInfo.Runspace.ConnectionInfo as VMConnectionInfo; - if (vmConnectionInfo != null) + if (_remoteRunspaceInfo.Runspace.ConnectionInfo is VMConnectionInfo vmConnectionInfo) { return string.Format(CultureInfo.InvariantCulture, VMIdParameterTemplate, CodeGeneration.EscapeSingleQuotedStringContent(vmConnectionInfo.VMGuid.ToString())); } - ContainerConnectionInfo containerConnectionInfo = _remoteRunspaceInfo.Runspace.ConnectionInfo as ContainerConnectionInfo; - if (containerConnectionInfo != null) + if (_remoteRunspaceInfo.Runspace.ConnectionInfo is ContainerConnectionInfo containerConnectionInfo) { return string.Format(CultureInfo.InvariantCulture, ContainerIdParameterTemplate, @@ -2568,22 +2608,19 @@ private string GenerateConnectionStringForNewRunspace() CodeGeneration.EscapeSingleQuotedStringContent(connectionInfo.AppName), connectionInfo.UseDefaultWSManPort ? string.Empty : - string.Format(CultureInfo.InvariantCulture, - "-Port {0} ", connectionInfo.Port), + string.Create(CultureInfo.InvariantCulture, $"-Port {connectionInfo.Port} "), isSSLSpecified ? "-useSSL" : string.Empty); } else { - return string.Format(CultureInfo.InvariantCulture, - "-connectionUri '{0}'", - CodeGeneration.EscapeSingleQuotedStringContent(GetConnectionString())); + string connectionString = CodeGeneration.EscapeSingleQuotedStringContent(GetConnectionString()); + return string.Create(CultureInfo.InvariantCulture, $"-connectionUri '{connectionString}'"); } } private string GenerateAllowRedirectionParameter() { - WSManConnectionInfo wsmanConnectionInfo = _remoteRunspaceInfo.Runspace.ConnectionInfo as WSManConnectionInfo; - if (wsmanConnectionInfo == null) + if (_remoteRunspaceInfo.Runspace.ConnectionInfo is not WSManConnectionInfo wsmanConnectionInfo) { return string.Empty; } @@ -2609,8 +2646,7 @@ private string GenerateAuthenticationMechanismParameter() return string.Empty; } - WSManConnectionInfo wsmanConnectionInfo = _remoteRunspaceInfo.Runspace.ConnectionInfo as WSManConnectionInfo; - if (wsmanConnectionInfo == null) + if (_remoteRunspaceInfo.Runspace.ConnectionInfo is not WSManConnectionInfo wsmanConnectionInfo) { return string.Empty; } @@ -2705,7 +2741,8 @@ function Get-PSImplicitRemotingClientSideParameters $proxyForCmdlet) $clientSideParameters = @{} - $parametersToLeaveRemote = 'ErrorAction', 'WarningAction', 'InformationAction' + + $parametersToLeaveRemote = 'ErrorAction', 'WarningAction', 'InformationAction', 'ProgressAction' Modify-PSImplicitRemotingParameters $clientSideParameters $PSBoundParameters 'AsJob' if ($proxyForCmdlet) @@ -2726,11 +2763,12 @@ function Get-PSImplicitRemotingClientSideParameters return $clientSideParameters } "; - private void GenerateHelperFunctionsClientSideParameters(TextWriter writer) + + private static void GenerateHelperFunctionsClientSideParameters(TextWriter writer) { if (writer == null) { - throw PSTraceSource.NewArgumentNullException("writer"); + throw PSTraceSource.NewArgumentNullException(nameof(writer)); } writer.Write(HelperFunctionsModifyParameters); @@ -2740,12 +2778,12 @@ private void GenerateHelperFunctionsClientSideParameters(TextWriter writer) private void GenerateHelperFunctions(TextWriter writer) { - this.GenerateSectionSeparator(writer); - this.GenerateHelperFunctionsWriteMessage(writer); + GenerateSectionSeparator(writer); + GenerateHelperFunctionsWriteMessage(writer); this.GenerateHelperFunctionsGetSessionOption(writer); - this.GenerateHelperFunctionsSetImplicitRunspace(writer); + GenerateHelperFunctionsSetImplicitRunspace(writer); this.GenerateHelperFunctionsGetImplicitRunspace(writer); - this.GenerateHelperFunctionsClientSideParameters(writer); + GenerateHelperFunctionsClientSideParameters(writer); } #endregion @@ -2775,17 +2813,19 @@ private void GenerateHelperFunctions(TextWriter writer) $null = $positionalArguments.Add( $PSBoundParameters[$parameterName] ) $null = $PSBoundParameters.Remove($parameterName) }} + $positionalArguments.AddRange($args) $clientSideParameters = Get-PSImplicitRemotingClientSideParameters $PSBoundParameters ${8} - $scriptCmd = {{ & $script:InvokeCommand ` - @clientSideParameters ` - -HideComputerName ` - -Session (Get-PSImplicitRemotingSession -CommandName '{0}') ` - -Arg ('{0}', $PSBoundParameters, $positionalArguments) ` - -Script {{ param($name, $boundParams, $unboundParams) & $name @boundParams @unboundParams }} ` - }} + $scriptCmd = {{ + & $script:InvokeCommand ` + @clientSideParameters ` + -HideComputerName ` + -Session (Get-PSImplicitRemotingSession -CommandName '{0}') ` + -Arg ('{0}', $PSBoundParameters, $positionalArguments) ` + -Script {{ param($name, $boundParams, $unboundParams) & $name @boundParams @unboundParams }} ` + }} $steppablePipeline = $scriptCmd.GetSteppablePipeline($myInvocation.CommandOrigin) $steppablePipeline.Begin($myInvocation.ExpectingInput, $ExecutionContext) @@ -2793,7 +2833,9 @@ private void GenerateHelperFunctions(TextWriter writer) throw }} }} + Process {{ {6} }} + End {{ {7} }} # .ForwardHelpTargetName {1} @@ -2802,15 +2844,15 @@ private void GenerateHelperFunctions(TextWriter writer) }} "; - private void GenerateCommandProxy(TextWriter writer, CommandMetadata commandMetadata) + private static void GenerateCommandProxy(TextWriter writer, CommandMetadata commandMetadata) { if (writer == null) { - throw PSTraceSource.NewArgumentNullException("writer"); + throw PSTraceSource.NewArgumentNullException(nameof(writer)); } string functionNameForString = CodeGeneration.EscapeSingleQuotedStringContent(commandMetadata.Name); - string functionNameForHelp = this.EscapeFunctionNameForRemoteHelp(commandMetadata.Name); + string functionNameForHelp = EscapeFunctionNameForRemoteHelp(commandMetadata.Name); writer.Write( CommandProxyTemplate, /* 0 */ functionNameForString, @@ -2824,18 +2866,19 @@ private void GenerateCommandProxy(TextWriter writer, CommandMetadata commandMeta /* 8 */ commandMetadata.WrappedAnyCmdlet); } - private void GenerateCommandProxy(TextWriter writer, IEnumerable listOfCommandMetadata) + private static void GenerateCommandProxy(TextWriter writer, IEnumerable listOfCommandMetadata) { if (writer == null) { - throw PSTraceSource.NewArgumentNullException("writer"); + throw PSTraceSource.NewArgumentNullException(nameof(writer)); } + if (listOfCommandMetadata == null) { - throw PSTraceSource.NewArgumentNullException("listOfCommandMetadata"); + throw PSTraceSource.NewArgumentNullException(nameof(listOfCommandMetadata)); } - this.GenerateSectionSeparator(writer); + GenerateSectionSeparator(writer); foreach (CommandMetadata commandMetadata in listOfCommandMetadata) { GenerateCommandProxy(writer, commandMetadata); @@ -2850,60 +2893,63 @@ private void GenerateCommandProxy(TextWriter writer, IEnumerable listOfCommandMetadata) + private static void GenerateExportDeclaration(TextWriter writer, IEnumerable listOfCommandMetadata) { if (writer == null) { - throw PSTraceSource.NewArgumentNullException("writer"); + throw PSTraceSource.NewArgumentNullException(nameof(writer)); } + if (listOfCommandMetadata == null) { - throw PSTraceSource.NewArgumentNullException("listOfCommandMetadata"); + throw PSTraceSource.NewArgumentNullException(nameof(listOfCommandMetadata)); } - this.GenerateSectionSeparator(writer); + GenerateSectionSeparator(writer); List listOfCommandNames = GetListOfCommandNames(listOfCommandMetadata); string exportString = GenerateArrayString(listOfCommandNames); writer.Write(ExportFunctionsTemplate, exportString); } - private List GetListOfCommandNames(IEnumerable listOfCommandMetadata) + private static List GetListOfCommandNames(IEnumerable listOfCommandMetadata) { if (listOfCommandMetadata == null) { - throw PSTraceSource.NewArgumentNullException("listOfCommandMetadata"); + throw PSTraceSource.NewArgumentNullException(nameof(listOfCommandMetadata)); } - List listOfCommandNames = new List(); + List listOfCommandNames = new(); foreach (CommandMetadata commandMetadata in listOfCommandMetadata) { listOfCommandNames.Add(commandMetadata.Name); } + return listOfCommandNames; } - private string GenerateArrayString(IEnumerable listOfStrings) + private static string GenerateArrayString(IEnumerable listOfStrings) { if (listOfStrings == null) { - throw PSTraceSource.NewArgumentNullException("listOfStrings"); + throw PSTraceSource.NewArgumentNullException(nameof(listOfStrings)); } - StringBuilder arrayString = new StringBuilder(); + StringBuilder arrayString = new(); foreach (string s in listOfStrings) { if (arrayString.Length != 0) { arrayString.Append(", "); } + arrayString.Append('\''); arrayString.Append(CodeGeneration.EscapeSingleQuotedStringContent(s)); arrayString.Append('\''); } arrayString.Insert(0, "@("); - arrayString.Append(")"); + arrayString.Append(')'); return arrayString.ToString(); } @@ -2920,9 +2966,9 @@ private string GenerateArrayString(IEnumerable listOfStrings) & $script:ExportModuleMember -Alias {0} "; - private void GenerateAliases(TextWriter writer, Dictionary alias2resolvedCommandName) + private static void GenerateAliases(TextWriter writer, Dictionary alias2resolvedCommandName) { - this.GenerateSectionSeparator(writer); + GenerateSectionSeparator(writer); foreach (KeyValuePair pair in alias2resolvedCommandName) { @@ -2943,18 +2989,19 @@ private void GenerateAliases(TextWriter writer, Dictionary alias #region Generating format.ps1xml file - private void GenerateFormatFile(TextWriter writer, List listOfFormatData) + private static void GenerateFormatFile(TextWriter writer, List listOfFormatData) { if (writer == null) { - throw PSTraceSource.NewArgumentNullException("writer"); + throw PSTraceSource.NewArgumentNullException(nameof(writer)); } + if (listOfFormatData == null) { - throw PSTraceSource.NewArgumentNullException("listOfFormatData"); + throw PSTraceSource.NewArgumentNullException(nameof(listOfFormatData)); } - XmlWriterSettings settings = new XmlWriterSettings(); + XmlWriterSettings settings = new(); settings.CloseOutput = false; settings.ConformanceLevel = ConformanceLevel.Document; settings.Encoding = writer.Encoding; @@ -2970,18 +3017,18 @@ private void GenerateFormatFile(TextWriter writer, List /// /// Generates a proxy module in the given directory. /// - /// base directory for the module - /// filename prefix for module files - /// encoding of generated files - /// whether to overwrite files - /// remote commands to generate proxies for - /// dictionary mapping alias names to resolved command names - /// remote format data to generate format.ps1xml for - /// certificate with which to sign the format files - /// Path to the created files + /// Base directory for the module. + /// Filename prefix for module files. + /// Encoding of generated files. + /// Whether to overwrite files. + /// Remote commands to generate proxies for. + /// Dictionary mapping alias names to resolved command names. + /// Remote format data to generate format.ps1xml for. + /// Certificate with which to sign the format files. + /// Path to the created files. internal List GenerateProxyModule( DirectoryInfo moduleRootDirectory, - String fileNamePrefix, + string fileNamePrefix, Encoding encoding, bool force, List listOfCommandMetadata, @@ -2989,7 +3036,7 @@ internal List GenerateProxyModule( List listOfFormatData, X509Certificate2 certificate) { - List result = new List(); + List result = new(); Dbg.Assert(moduleRootDirectory != null, "Caller should validate moduleRootDirectory != null"); Dbg.Assert(Directory.Exists(moduleRootDirectory.FullName), "Caller should validate moduleRootDirectory exists"); @@ -2999,17 +3046,14 @@ internal List GenerateProxyModule( FileMode fileMode = force ? FileMode.OpenOrCreate : FileMode.CreateNew; result.Add(baseName + ".psm1"); - FileStream psm1 = new FileStream( + FileStream psm1 = new( baseName + ".psm1", fileMode, FileAccess.Write, FileShare.None); using (TextWriter writer = new StreamWriter(psm1, encoding)) { - if (listOfCommandMetadata == null) - { - listOfCommandMetadata = new List(); - } + listOfCommandMetadata ??= new List(); GenerateModuleHeader(writer); GenerateHelperFunctions(writer); @@ -3020,17 +3064,14 @@ internal List GenerateProxyModule( } result.Add(baseName + ".format.ps1xml"); - FileStream formatPs1xml = new FileStream( + FileStream formatPs1xml = new( baseName + ".format.ps1xml", fileMode, FileAccess.Write, FileShare.None); using (TextWriter writer = new StreamWriter(formatPs1xml, encoding)) { - if (listOfFormatData == null) - { - listOfFormatData = new List(); - } + listOfFormatData ??= new List(); GenerateFormatFile(writer, listOfFormatData); formatPs1xml.SetLength(formatPs1xml.Position); @@ -3049,7 +3090,7 @@ internal List GenerateProxyModule( } else { - String currentFile = baseName + ".psm1"; + string currentFile = baseName + ".psm1"; try { SignatureHelper.SignFile(SigningOption.Default, currentFile, certificate, string.Empty, null); @@ -3065,8 +3106,8 @@ internal List GenerateProxyModule( } result.Add(baseName + ".psd1"); - FileInfo manifestFile = new FileInfo(baseName + ".psd1"); - FileStream psd1 = new FileStream( + FileInfo manifestFile = new(baseName + ".psd1"); + FileStream psd1 = new( manifestFile.FullName, fileMode, FileAccess.Write, @@ -3086,7 +3127,7 @@ internal List GenerateProxyModule( using (var stream = new FileStream(applicationArgumentsFile, FileMode.Create, FileAccess.Write, FileShare.Read)) using (var xmlWriter = XmlWriter.Create(stream)) { - Serializer serializer = new Serializer(xmlWriter); + Serializer serializer = new(xmlWriter); serializer.Serialize(applicationArguments); serializer.Done(); } diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Import-LocalizedData.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Import-LocalizedData.cs index fdcbb57a166..c4e423ffd1d 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Import-LocalizedData.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Import-LocalizedData.cs @@ -1,29 +1,28 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; -using System.Management.Automation.Internal; using System.Management.Automation; -using System.Diagnostics.CodeAnalysis; +using System.Management.Automation.Internal; +using System.Management.Automation.Security; namespace Microsoft.PowerShell.Commands { /// - /// The implementation of the "import-localizeddata" cmdlet + /// The implementation of the "import-localizeddata" cmdlet. /// - /// - [Cmdlet(VerbsData.Import, "LocalizedData", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113342")] + [Cmdlet(VerbsData.Import, "LocalizedData", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2096710")] public sealed class ImportLocalizedData : PSCmdlet { #region Parameters /// - /// The path from which to import the aliases + /// The path from which to import the aliases. /// - /// [Parameter(Position = 0)] [Alias("Variable")] [ValidateNotNullOrEmpty] @@ -39,12 +38,12 @@ public string BindingVariable _bindingVariable = value; } } + private string _bindingVariable; /// /// The scope to import the aliases to. /// - /// [Parameter(Position = 1)] public string UICulture { @@ -58,12 +57,12 @@ public string UICulture _uiculture = value; } } + private string _uiculture; /// /// The scope to import the aliases to. /// - /// [Parameter] public string BaseDirectory { @@ -77,12 +76,12 @@ public string BaseDirectory _baseDirectory = value; } } + private string _baseDirectory; /// /// The scope to import the aliases to. /// - /// [Parameter] public string FileName { @@ -96,13 +95,14 @@ public string FileName _fileName = value; } } + private string _fileName; /// - /// The command allowed in the data file. If unspecified, then ConvertFrom-StringData - /// is allowed. + /// The command allowed in the data file. If unspecified, then ConvertFrom-StringData is allowed. /// [Parameter] + [ValidateTrustedData] [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Justification = "Cmdlets use arrays for parameters.")] public string[] SupportedCommand { @@ -110,12 +110,14 @@ public string[] SupportedCommand { return _commandsAllowed; } + set { _setSupportedCommand = true; _commandsAllowed = value; } } + private string[] _commandsAllowed = new string[] { "ConvertFrom-StringData" }; private bool _setSupportedCommand = false; @@ -126,7 +128,6 @@ public string[] SupportedCommand /// /// The main processing loop of the command. /// - /// protected override void ProcessRecord() { string path = GetFilePath(); @@ -147,9 +148,9 @@ protected override void ProcessRecord() } // Prevent additional commands in ConstrainedLanguage mode - if (Context.LanguageMode == PSLanguageMode.ConstrainedLanguage) + if (_setSupportedCommand && Context.LanguageMode == PSLanguageMode.ConstrainedLanguage) { - if (_setSupportedCommand) + if (SystemPolicy.GetSystemLockdownPolicy() != SystemEnforcementMode.Audit) { NotSupportedException nse = PSTraceSource.NewNotSupportedException( @@ -157,6 +158,13 @@ protected override void ProcessRecord() ThrowTerminatingError( new ErrorRecord(nse, "CannotDefineSupportedCommand", ErrorCategory.PermissionDenied, null)); } + + SystemPolicy.LogWDACAuditMessage( + context: Context, + title: ImportLocalizedDataStrings.WDACLogTitle, + message: ImportLocalizedDataStrings.WDACLogMessage, + fqid: "SupportedCommandsDisabled", + dropIntoDebugger: true); } string script = GetScript(path); @@ -187,7 +195,7 @@ protected override void ProcessRecord() if (_bindingVariable != null) { - VariablePath variablePath = new VariablePath(_bindingVariable); + VariablePath variablePath = new(_bindingVariable); if (variablePath.IsUnscopedVariable) { variablePath = variablePath.CloneAndSetLocal(); @@ -213,8 +221,15 @@ protected override void ProcessRecord() else { variable.Value = result; + + if (Context.LanguageMode == PSLanguageMode.ConstrainedLanguage) + { + // Mark untrusted values for assignments to 'Global:' variables, and 'Script:' variables in + // a module scope, if it's necessary. + ExecutionContext.MarkObjectAsUntrustedForVariableAssignment(variable, scope, Context.EngineSessionState); + } } - } // end _bindingvariable != null + } // If binding variable is null, write the object to stream else @@ -233,13 +248,13 @@ protected override void ProcessRecord() } return; - } // ProcessRecord + } private string GetFilePath() { - if (String.IsNullOrEmpty(_fileName)) + if (string.IsNullOrEmpty(_fileName)) { - if (InvocationExtent == null || String.IsNullOrEmpty(InvocationExtent.File)) + if (InvocationExtent == null || string.IsNullOrEmpty(InvocationExtent.File)) { throw PSTraceSource.NewInvalidOperationException(ImportLocalizedDataStrings.NotCalledFromAScriptFile); } @@ -247,9 +262,9 @@ private string GetFilePath() string dir = _baseDirectory; - if (String.IsNullOrEmpty(dir)) + if (string.IsNullOrEmpty(dir)) { - if (InvocationExtent != null && !String.IsNullOrEmpty(InvocationExtent.File)) + if (InvocationExtent != null && !string.IsNullOrEmpty(InvocationExtent.File)) { dir = Path.GetDirectoryName(InvocationExtent.File); } @@ -262,13 +277,13 @@ private string GetFilePath() dir = PathUtils.ResolveFilePath(dir, this); string fileName = _fileName; - if (String.IsNullOrEmpty(fileName)) + if (string.IsNullOrEmpty(fileName)) { fileName = InvocationExtent.File; } else { - if (!String.IsNullOrEmpty(Path.GetDirectoryName(fileName))) + if (!string.IsNullOrEmpty(Path.GetDirectoryName(fileName))) { throw PSTraceSource.NewInvalidOperationException(ImportLocalizedDataStrings.FileNameParameterCannotHavePath); } @@ -276,7 +291,7 @@ private string GetFilePath() fileName = Path.GetFileNameWithoutExtension(fileName); - CultureInfo culture = null; + CultureInfo culture; if (_uiculture == null) { culture = CultureInfo.CurrentUICulture; @@ -293,19 +308,33 @@ private string GetFilePath() } } - CultureInfo currentCulture = culture; + List cultureList = new List { culture }; + if (_uiculture == null && culture.Name != "en-US") + { + // .NET 4.8 presents en-US as a parent of any current culture when accessed via the CurrentUICulture + // property. + // + // This feature is not present when GetCultureInfo is called, therefore this fallback change only + // applies when the UICulture parameter is not supplied. + cultureList.Add(CultureInfo.GetCultureInfo("en-US")); + } + string filePath; string fullFileName = fileName + ".psd1"; - while (currentCulture != null && !String.IsNullOrEmpty(currentCulture.Name)) + foreach (CultureInfo cultureToTest in cultureList) { - filePath = Path.Combine(dir, currentCulture.Name, fullFileName); - - if (File.Exists(filePath)) + CultureInfo currentCulture = cultureToTest; + while (currentCulture != null && !string.IsNullOrEmpty(currentCulture.Name)) { - return filePath; - } + filePath = Path.Combine(dir, currentCulture.Name, fullFileName); + + if (File.Exists(filePath)) + { + return filePath; + } - currentCulture = currentCulture.Parent; + currentCulture = currentCulture.Parent; + } } filePath = Path.Combine(dir, fullFileName); @@ -334,8 +363,8 @@ private string GetScript(string filePath) // 197751: WR BUG BASH: Powershell: localized text display as garbage // leaving the encoding to be decided by the StreamReader. StreamReader // will read the preamble and decide proper encoding. - using (FileStream scriptStream = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read)) - using (StreamReader scriptReader = new StreamReader(scriptStream)) + using (FileStream scriptStream = new(filePath, FileMode.Open, FileAccess.Read, FileShare.Read)) + using (StreamReader scriptReader = new(scriptStream)) { return scriptReader.ReadToEnd(); } @@ -374,6 +403,5 @@ private string GetScript(string filePath) } #endregion Command code - } // class ImportLocalizedData -}//Microsoft.PowerShell.Commands - + } +} diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ImportAliasCommand.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ImportAliasCommand.cs index 07372404d70..657d00b4fc5 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ImportAliasCommand.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ImportAliasCommand.cs @@ -1,6 +1,5 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. using System; using System.Collections.Generic; @@ -13,10 +12,9 @@ namespace Microsoft.PowerShell.Commands { /// - /// The implementation of the "import-alias" cmdlet + /// The implementation of the "import-alias" cmdlet. /// - /// - [Cmdlet(VerbsData.Import, "Alias", SupportsShouldProcess = true, DefaultParameterSetName = "ByPath", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113339")] + [Cmdlet(VerbsData.Import, "Alias", SupportsShouldProcess = true, DefaultParameterSetName = "ByPath", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2097125")] [OutputType(typeof(AliasInfo))] public class ImportAliasCommand : PSCmdlet { @@ -29,18 +27,16 @@ public class ImportAliasCommand : PSCmdlet #region Parameters /// - /// The path from which to import the aliases + /// The path from which to import the aliases. /// - /// [Parameter(Position = 0, Mandatory = true, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true, ParameterSetName = "ByPath")] public string Path { get; set; } /// - /// The literal path from which to import the aliases + /// The literal path from which to import the aliases. /// - /// [Parameter(Mandatory = true, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true, ParameterSetName = LiteralPathParameterSetName)] - [Alias("PSPath")] + [Alias("PSPath", "LP")] public string LiteralPath { get @@ -57,16 +53,14 @@ public string LiteralPath /// /// The scope to import the aliases to. /// - /// [Parameter] [ValidateNotNullOrEmpty] + [ArgumentCompleter(typeof(ScopeArgumentCompleter))] public string Scope { get; set; } /// - /// If set to true, the alias that is set is passed to the - /// pipeline. + /// If set to true, the alias that is set is passed to the pipeline. /// - /// [Parameter] public SwitchParameter PassThru { @@ -80,13 +74,13 @@ public SwitchParameter PassThru _passThru = value; } } + private bool _passThru; /// /// If set to true and an existing alias of the same name exists /// and is ReadOnly, the alias will be overwritten. /// - /// [Parameter] public SwitchParameter Force { @@ -100,6 +94,7 @@ public SwitchParameter Force _force = value; } } + private bool _force; #endregion Parameters @@ -109,7 +104,6 @@ public SwitchParameter Force /// /// The main processing loop of the command. /// - /// protected override void ProcessRecord() { Collection importedAliases = GetAliasesFromFile(this.ParameterSetName.Equals(LiteralPathParameterSetName, @@ -132,7 +126,7 @@ protected override void ProcessRecord() if (!Force) { AliasInfo existingAlias = null; - if (String.IsNullOrEmpty(Scope)) + if (string.IsNullOrEmpty(Scope)) { existingAlias = SessionState.Internal.GetAlias(alias.Name); } @@ -161,7 +155,7 @@ protected override void ProcessRecord() // Since the alias already exists, write an error. SessionStateException aliasExists = - new SessionStateException( + new( alias.Name, SessionStateCategory.Alias, "AliasAlreadyExists", @@ -179,7 +173,7 @@ protected override void ProcessRecord() { continue; } - } // if (!Force) + } // Set the alias in the specified scope or the // current scope. @@ -188,7 +182,7 @@ protected override void ProcessRecord() try { - if (String.IsNullOrEmpty(Scope)) + if (string.IsNullOrEmpty(Scope)) { result = SessionState.Internal.SetAliasItem(alias, Force, MyInvocation.CommandOrigin); } @@ -229,9 +223,10 @@ protected override void ProcessRecord() WriteObject(result); } } - } // ProcessRecord + } private Dictionary _existingCommands; + private Dictionary ExistingCommands { get @@ -239,7 +234,7 @@ private Dictionary ExistingCommands if (_existingCommands == null) { _existingCommands = new Dictionary(StringComparer.OrdinalIgnoreCase); - CommandSearcher searcher = new CommandSearcher( + CommandSearcher searcher = new( "*", SearchResolutionOptions.CommandNameIsPattern | SearchResolutionOptions.ResolveAliasPatterns | SearchResolutionOptions.ResolveFunctionPatterns, CommandTypes.All ^ CommandTypes.Alias, @@ -259,13 +254,14 @@ private Dictionary ExistingCommands } } } + return _existingCommands; } } private bool VerifyShadowingExistingCommandsAndWriteError(string aliasName) { - CommandSearcher searcher = new CommandSearcher(aliasName, SearchResolutionOptions.None, CommandTypes.All ^ CommandTypes.Alias, this.Context); + CommandSearcher searcher = new(aliasName, SearchResolutionOptions.None, CommandTypes.All ^ CommandTypes.Alias, this.Context); foreach (string expandedCommandName in searcher.ConstructSearchPatternsFromName(aliasName)) { CommandTypes commandTypeOfExistingCommand; @@ -273,7 +269,7 @@ private bool VerifyShadowingExistingCommandsAndWriteError(string aliasName) { // Since the alias already exists, write an error. SessionStateException aliasExists = - new SessionStateException( + new( aliasName, SessionStateCategory.Alias, "AliasAlreadyExists", @@ -294,14 +290,14 @@ private bool VerifyShadowingExistingCommandsAndWriteError(string aliasName) private Collection GetAliasesFromFile(bool isLiteralPath) { - Collection result = new Collection(); + Collection result = new(); string filePath = null; using (StreamReader reader = OpenFile(out filePath, isLiteralPath)) { - CSVHelper csvHelper = new CSVHelper(','); + CSVHelper csvHelper = new(','); - Int64 lineNumber = 0; + long lineNumber = 0; string line = null; while ((line = reader.ReadLine()) != null) { @@ -332,10 +328,10 @@ private Collection GetAliasesFromFile(bool isLiteralPath) string message = StringUtil.Format(AliasCommandStrings.ImportAliasFileInvalidFormat, filePath, lineNumber); FormatException formatException = - new FormatException(message); + new(message); ErrorRecord errorRecord = - new ErrorRecord( + new( formatException, "ImportAliasFileFormatError", ErrorCategory.ReadError, @@ -357,7 +353,7 @@ private Collection GetAliasesFromFile(bool isLiteralPath) string message = StringUtil.Format(AliasCommandStrings.ImportAliasOptionsError, filePath, lineNumber); ErrorRecord errorRecord = - new ErrorRecord( + new( argException, "ImportAliasOptionsError", ErrorCategory.ReadError, @@ -369,21 +365,23 @@ private Collection GetAliasesFromFile(bool isLiteralPath) } AliasInfo newAlias = - new AliasInfo( + new( values[0], values[1], Context, options); - if (!String.IsNullOrEmpty(values[2])) + if (!string.IsNullOrEmpty(values[2])) { newAlias.Description = values[2]; } result.Add(newAlias); } + reader.Dispose(); } + return result; } @@ -430,7 +428,7 @@ private StreamReader OpenFile(out string filePath, bool isLiteralPath) try { - FileStream file = new FileStream(filePath, FileMode.Open, FileAccess.Read, FileShare.Read); + FileStream file = new(filePath, FileMode.Open, FileAccess.Read, FileShare.Read); result = new StreamReader(file); } catch (IOException ioException) @@ -454,7 +452,7 @@ private void ThrowFileOpenError(Exception e, string pathWithError) string message = StringUtil.Format(AliasCommandStrings.ImportAliasFileOpenFailed, pathWithError, e.Message); - ErrorRecord errorRecord = new ErrorRecord( + ErrorRecord errorRecord = new( e, "FileOpenFailure", ErrorCategory.OpenError, @@ -478,9 +476,9 @@ private static bool OnlyContainsWhitespace(string line) result = false; break; } + return result; } #endregion Command code - } // class ImportAliasCommand -}//Microsoft.PowerShell.Commands - + } +} diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ImportPowerShellDataFile.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ImportPowerShellDataFile.cs index d0418892bdb..5661df24fa9 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ImportPowerShellDataFile.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ImportPowerShellDataFile.cs @@ -1,3 +1,6 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + using System; using System.Diagnostics.CodeAnalysis; using System.Management.Automation; @@ -12,39 +15,45 @@ namespace Microsoft.PowerShell.Commands HelpUri = "https://go.microsoft.com/fwlink/?LinkID=623621", RemotingCapability = RemotingCapability.None)] public class ImportPowerShellDataFileCommand : PSCmdlet { - bool _isLiteralPath; + private bool _isLiteralPath; /// - /// Path specified, using globbing to resolve + /// Path specified, using globbing to resolve. /// - [Parameter(Mandatory = true, Position = 0, ParameterSetName = "ByPath")] + [Parameter(Mandatory = true, Position = 0, ParameterSetName = "ByPath")] [ValidateNotNullOrEmpty] [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] public string[] Path { get; set; } /// - /// Specifies a path to one or more locations, without globbing + /// Specifies a path to one or more locations, without globbing. /// [Parameter(Mandatory = true, Position = 0, ParameterSetName = "ByLiteralPath", ValueFromPipelineByPropertyName = true)] [ValidateNotNullOrEmpty] - [Alias("PSPath")] + [Alias("PSPath", "LP")] [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] public string[] LiteralPath { get { return _isLiteralPath ? Path : null; } + set { _isLiteralPath = true; Path = value; } } /// - /// For each path, resolve it, parse it and write all hashtables - /// to the output stream + /// Gets or sets switch that determines if built-in limits are applied to the data. + /// + [Parameter] + public SwitchParameter SkipLimitCheck { get; set; } + + /// + /// For each path, resolve it, parse it and write all hashtables to the output stream. /// protected override void ProcessRecord() { foreach (var path in Path) { var resolved = PathUtils.ResolveFilePath(path, this, _isLiteralPath); - if(!string.IsNullOrEmpty(resolved) && System.IO.File.Exists(resolved)) + if (!string.IsNullOrEmpty(resolved) && System.IO.File.Exists(resolved)) { Token[] tokens; ParseError[] errors; @@ -55,10 +64,10 @@ protected override void ProcessRecord() } else { - var data = ast.Find(a => a is HashtableAst, false); + var data = ast.Find(static a => a is HashtableAst, false); if (data != null) { - WriteObject(data.SafeGetValue()); + WriteObject(data.SafeGetValue(SkipLimitCheck)); } else { @@ -75,18 +84,18 @@ protected override void ProcessRecord() private void WritePathNotFoundError(string path) { - var errorId = "PathNotFound"; - var errorCategory = ErrorCategory.InvalidArgument; - var errorMessage = string.Format(UtilityResources.PathDoesNotExist, path); + const string errorId = "PathNotFound"; + const ErrorCategory errorCategory = ErrorCategory.InvalidArgument; + var errorMessage = string.Format(UtilityCommonStrings.PathDoesNotExist, path); var exception = new ArgumentException(errorMessage); var errorRecord = new ErrorRecord(exception, errorId, errorCategory, path); WriteError(errorRecord); } - void WriteInvalidDataFileError(string resolvedPath, string errorId) - { - var errorCategory = ErrorCategory.InvalidData; - var errorMessage = string.Format(UtilityResources.CouldNotParseAsPowerShellDataFile, resolvedPath); + private void WriteInvalidDataFileError(string resolvedPath, string errorId) + { + const ErrorCategory errorCategory = ErrorCategory.InvalidData; + var errorMessage = string.Format(UtilityCommonStrings.CouldNotParseAsPowerShellDataFile, resolvedPath); var exception = new InvalidOperationException(errorMessage); var errorRecord = new ErrorRecord(exception, errorId, errorCategory, resolvedPath); WriteError(errorRecord); diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/InvokeCommandCmdlet.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/InvokeCommandCmdlet.cs deleted file mode 100644 index 4392ec00a84..00000000000 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/InvokeCommandCmdlet.cs +++ /dev/null @@ -1,57 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -using System.Management.Automation; -using System.Management.Automation.Internal; - -namespace Microsoft.PowerShell.Commands -{ - /// - /// Class implementing Invoke-Expression - /// - [Cmdlet(VerbsLifecycle.Invoke, "Expression", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113343")] - public sealed - class - InvokeExpressionCommand : PSCmdlet - { - #region parameters - - /// - /// Command to execute. - /// - [Parameter(Position = 0, Mandatory = true, ValueFromPipeline = true)] - public string Command { get; set; } - - #endregion parameters - - /// - /// For each record, execute it, and push the results into the - /// success stream. - /// - protected override void ProcessRecord() - { - Diagnostics.Assert(null != Command, "Command is null"); - - ScriptBlock myScriptBlock = InvokeCommand.NewScriptBlock(Command); - - // If the runspace has ever been in ConstrainedLanguage, lock down this - // invocation as well - it is too easy for the command to be negatively influenced - // by malicious input (such as ReadOnly + Constant variables) - if (Context.HasRunspaceEverUsedConstrainedLanguageMode) - { - myScriptBlock.LanguageMode = PSLanguageMode.ConstrainedLanguage; - } - - var emptyArray = Utils.EmptyArray(); - myScriptBlock.InvokeUsingCmdlet( - contextCmdlet: this, - useLocalScope: false, - errorHandlingBehavior: ScriptBlock.ErrorHandlingBehavior.WriteToCurrentErrorPipe, - dollarUnder: AutomationNull.Value, - input: emptyArray, - scriptThis: AutomationNull.Value, - args: emptyArray); - } - } -} // namespace Microsoft.PowerShell.Commands diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/InvokeExpressionCommand.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/InvokeExpressionCommand.cs new file mode 100644 index 00000000000..acbffeb66f4 --- /dev/null +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/InvokeExpressionCommand.cs @@ -0,0 +1,68 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Management.Automation; +using System.Management.Automation.Internal; +using System.Management.Automation.Security; + +namespace Microsoft.PowerShell.Commands +{ + /// + /// Class implementing Invoke-Expression. + /// + [Cmdlet(VerbsLifecycle.Invoke, "Expression", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2097030")] + public sealed + class + InvokeExpressionCommand : PSCmdlet + { + #region parameters + + /// + /// Command to execute. + /// + [Parameter(Position = 0, Mandatory = true, ValueFromPipeline = true)] + [ValidateTrustedData] + public string Command { get; set; } + + #endregion parameters + + /// + /// For each record, execute it, and push the results into the success stream. + /// + protected override void ProcessRecord() + { + Diagnostics.Assert(Command != null, "Command is null"); + + ScriptBlock myScriptBlock = InvokeCommand.NewScriptBlock(Command); + + // If the runspace has ever been in ConstrainedLanguage, lock down this + // invocation as well - it is too easy for the command to be negatively influenced + // by malicious input (such as ReadOnly + Constant variables) + if (Context.HasRunspaceEverUsedConstrainedLanguageMode) + { + myScriptBlock.LanguageMode = PSLanguageMode.ConstrainedLanguage; + } + + if (SystemPolicy.GetSystemLockdownPolicy() == SystemEnforcementMode.Audit) + { + SystemPolicy.LogWDACAuditMessage( + context: Context, + title: UtilityCommonStrings.IEXWDACLogTitle, + message: UtilityCommonStrings.IEXWDACLogMessage, + fqid: "InvokeExpressionCmdletConstrained", + dropIntoDebugger: true); + } + + var emptyArray = Array.Empty(); + myScriptBlock.InvokeUsingCmdlet( + contextCmdlet: this, + useLocalScope: false, + errorHandlingBehavior: ScriptBlock.ErrorHandlingBehavior.WriteToCurrentErrorPipe, + dollarUnder: AutomationNull.Value, + input: emptyArray, + scriptThis: AutomationNull.Value, + args: emptyArray); + } + } +} diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Join-String.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Join-String.cs new file mode 100644 index 00000000000..80a04b07f17 --- /dev/null +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Join-String.cs @@ -0,0 +1,274 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Linq; +using System.Management.Automation; +using System.Management.Automation.Internal; +using System.Management.Automation.Language; +using System.Text; + +namespace Microsoft.PowerShell.Commands.Utility +{ + /// + /// Join-Object implementation. + /// + [Cmdlet(VerbsCommon.Join, "String", RemotingCapability = RemotingCapability.None, DefaultParameterSetName = "default")] + [OutputType(typeof(string))] + public sealed class JoinStringCommand : PSCmdlet + { + /// A bigger default to not get re-allocations in common use cases. + private const int DefaultOutputStringCapacity = 256; + + private readonly StringBuilder _outputBuilder = new(DefaultOutputStringCapacity); + private CultureInfo _cultureInfo = CultureInfo.InvariantCulture; + private string _separator; + private char _quoteChar; + private bool _firstInputObject = true; + + /// + /// Gets or sets the property name or script block to use as the value to join. + /// + [Parameter(Position = 0)] + [ArgumentCompleter(typeof(PropertyNameCompleter))] + public PSPropertyExpression Property { get; set; } + + /// + /// Gets or sets the delimiter to join the output with. + /// + [Parameter(Position = 1)] + [ArgumentCompleter(typeof(SeparatorArgumentCompleter))] + [AllowEmptyString] + public string Separator + { + get => _separator ?? LanguagePrimitives.ConvertTo(GetVariableValue("OFS")); + set => _separator = value; + } + + /// + /// Gets or sets text to include before the joined input text. + /// + [Parameter] + [Alias("op")] + public string OutputPrefix { get; set; } + + /// + /// Gets or sets text to include after the joined input text. + /// + [Parameter] + [Alias("os")] + public string OutputSuffix { get; set; } + + /// + /// Gets or sets if the output items should we wrapped in single quotes. + /// + [Parameter(ParameterSetName = "SingleQuote")] + public SwitchParameter SingleQuote { get; set; } + + /// + /// Gets or sets if the output items should we wrapped in double quotes. + /// + [Parameter(ParameterSetName = "DoubleQuote")] + public SwitchParameter DoubleQuote { get; set; } + + /// + /// Gets or sets a format string that is applied to each input object. + /// + [Parameter(ParameterSetName = "Format")] + [ArgumentCompleter(typeof(FormatStringArgumentCompleter))] + public string FormatString { get; set; } + + /// + /// Gets or sets if the current culture should be used with formatting instead of the invariant culture. + /// + [Parameter] + public SwitchParameter UseCulture { get; set; } + + /// + /// Gets or sets the input object to join into text. + /// + [Parameter(ValueFromPipeline = true)] + public PSObject[] InputObject { get; set; } + + /// + protected override void BeginProcessing() + { + _quoteChar = SingleQuote ? '\'' : DoubleQuote ? '"' : char.MinValue; + _outputBuilder.Append(OutputPrefix); + if (UseCulture) + { + _cultureInfo = CultureInfo.CurrentCulture; + } + } + + /// + protected override void ProcessRecord() + { + if (InputObject != null) + { + foreach (PSObject inputObject in InputObject) + { + if (inputObject != null && inputObject != AutomationNull.Value) + { + var inputValue = Property == null + ? inputObject + : Property.GetValues(inputObject, false, true).FirstOrDefault()?.Result; + + // conversion to string always succeeds. + if (!LanguagePrimitives.TryConvertTo(inputValue, _cultureInfo, out var stringValue)) + { + throw new PSInvalidCastException("InvalidCastFromAnyTypeToString", ExtendedTypeSystem.InvalidCastCannotRetrieveString, null); + } + + if (_firstInputObject) + { + _firstInputObject = false; + } + else + { + _outputBuilder.Append(Separator); + } + + if (_quoteChar != char.MinValue) + { + _outputBuilder.Append(_quoteChar); + _outputBuilder.Append(stringValue); + _outputBuilder.Append(_quoteChar); + } + else if (string.IsNullOrEmpty(FormatString)) + { + _outputBuilder.Append(stringValue); + } + else + { + _outputBuilder.AppendFormat(_cultureInfo, FormatString, inputValue); + } + } + } + } + } + + /// + protected override void EndProcessing() + { + _outputBuilder.Append(OutputSuffix); + WriteObject(_outputBuilder.ToString()); + } + } + + /// + /// Provides completion for the Separator parameter of the Join-String cmdlet. + /// + public sealed class SeparatorArgumentCompleter : IArgumentCompleter + { + private const string NewLineText = +#if UNIX + "`n"; +#else + "`r`n"; +#endif + + private static readonly CompletionHelpers.CompletionDisplayInfoMapper SeparatorDisplayInfoMapper = separator => separator switch + { + "," => ( + ToolTip: TabCompletionStrings.SeparatorCommaToolTip, + ListItemText: "Comma"), + ", " => ( + ToolTip: TabCompletionStrings.SeparatorCommaSpaceToolTip, + ListItemText: "Comma-Space"), + ";" => ( + ToolTip: TabCompletionStrings.SeparatorSemiColonToolTip, + ListItemText: "Semi-Colon"), + "; " => ( + ToolTip: TabCompletionStrings.SeparatorSemiColonSpaceToolTip, + ListItemText: "Semi-Colon-Space"), + "-" => ( + ToolTip: TabCompletionStrings.SeparatorDashToolTip, + ListItemText: "Dash"), + " " => ( + ToolTip: TabCompletionStrings.SeparatorSpaceToolTip, + ListItemText: "Space"), + NewLineText => ( + ToolTip: StringUtil.Format(TabCompletionStrings.SeparatorNewlineToolTip, NewLineText), + ListItemText: "Newline"), + _ => ( + ToolTip: separator, + ListItemText: separator), + }; + + private static readonly IReadOnlyList s_separatorValues = new List(capacity: 7) + { + ",", + ", ", + ";", + "; ", + NewLineText, + "-", + " ", + }; + + /// + /// Returns completion results for Separator parameter. + /// + /// The command name. + /// The parameter name. + /// The word to complete. + /// The command AST. + /// The fake bound parameters. + /// List of Completion Results. + public IEnumerable CompleteArgument( + string commandName, + string parameterName, + string wordToComplete, + CommandAst commandAst, + IDictionary fakeBoundParameters) + => CompletionHelpers.GetMatchingResults( + wordToComplete, + possibleCompletionValues: s_separatorValues, + displayInfoMapper: SeparatorDisplayInfoMapper, + resultType: CompletionResultType.ParameterValue); + } + + /// + /// Provides completion for the FormatString parameter of the Join-String cmdlet. + /// + public sealed class FormatStringArgumentCompleter : IArgumentCompleter + { + private static readonly IReadOnlyList s_formatStringValues = new List(capacity: 4) + { + "[{0}]", + "{0:N2}", +#if UNIX + "`n `${0}", + "`n [string] `${0}", +#else + "`r`n `${0}", + "`r`n [string] `${0}", +#endif + }; + + /// + /// Returns completion results for FormatString parameter. + /// + /// The command name. + /// The parameter name. + /// The word to complete. + /// The command AST. + /// The fake bound parameters. + /// List of Completion Results. + public IEnumerable CompleteArgument( + string commandName, + string parameterName, + string wordToComplete, + CommandAst commandAst, + IDictionary fakeBoundParameters) + => CompletionHelpers.GetMatchingResults( + wordToComplete, + possibleCompletionValues: s_formatStringValues, + matchStrategy: CompletionHelpers.WildcardPatternEscapeMatch); + } +} diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/JsonSchemaReferenceResolutionException.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/JsonSchemaReferenceResolutionException.cs new file mode 100644 index 00000000000..7c2a7ac65f4 --- /dev/null +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/JsonSchemaReferenceResolutionException.cs @@ -0,0 +1,27 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#nullable enable + +using System; + +namespace Microsoft.PowerShell.Commands; + +/// +/// Thrown during evaluation of when an attempt +/// to resolve a $ref or $dynamicRef fails. +/// +internal sealed class JsonSchemaReferenceResolutionException : Exception +{ + /// + /// Initializes a new instance of the class. + /// + /// + /// The exception that is the cause of the current exception, or a null reference + /// (Nothing in Visual Basic) if no inner exception is specified. + /// + public JsonSchemaReferenceResolutionException(Exception innerException) + : base(message: null, innerException) + { + } +} diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/MarkdownOptionCommands.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/MarkdownOptionCommands.cs new file mode 100644 index 00000000000..cd18f33c41d --- /dev/null +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/MarkdownOptionCommands.cs @@ -0,0 +1,314 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections.Concurrent; +using System.Management.Automation; +using System.Management.Automation.Internal; +using System.Management.Automation.Runspaces; + +using Microsoft.PowerShell.MarkdownRender; + +namespace Microsoft.PowerShell.Commands +{ + /// + /// Class for implementing Set-MarkdownOption cmdlet. + /// + [Cmdlet( + VerbsCommon.Set, "MarkdownOption", + DefaultParameterSetName = IndividualSetting, + HelpUri = "https://go.microsoft.com/fwlink/?linkid=2006265")] + [OutputType(typeof(Microsoft.PowerShell.MarkdownRender.PSMarkdownOptionInfo))] + public class SetMarkdownOptionCommand : PSCmdlet + { + /// + /// Gets or sets the VT100 escape sequence for Header Level 1. + /// + [ValidatePattern(@"^\[*[0-9;]*?m{1}")] + [Parameter(ParameterSetName = IndividualSetting)] + public string Header1Color { get; set; } + + /// + /// Gets or sets the VT100 escape sequence for Header Level 2. + /// + [ValidatePattern(@"^\[*[0-9;]*?m{1}")] + [Parameter(ParameterSetName = IndividualSetting)] + public string Header2Color { get; set; } + + /// + /// Gets or sets the VT100 escape sequence for Header Level 3. + /// + [ValidatePattern(@"^\[*[0-9;]*?m{1}")] + [Parameter(ParameterSetName = IndividualSetting)] + public string Header3Color { get; set; } + + /// + /// Gets or sets the VT100 escape sequence for Header Level 4. + /// + [ValidatePattern(@"^\[*[0-9;]*?m{1}")] + [Parameter(ParameterSetName = IndividualSetting)] + public string Header4Color { get; set; } + + /// + /// Gets or sets the VT100 escape sequence for Header Level 5. + /// + [ValidatePattern(@"^\[*[0-9;]*?m{1}")] + [Parameter(ParameterSetName = IndividualSetting)] + public string Header5Color { get; set; } + + /// + /// Gets or sets the VT100 escape sequence for Header Level 6. + /// + [ValidatePattern(@"^\[*[0-9;]*?m{1}")] + [Parameter(ParameterSetName = IndividualSetting)] + public string Header6Color { get; set; } + + /// + /// Gets or sets the VT100 escape sequence for code block background. + /// + [ValidatePattern(@"^\[*[0-9;]*?m{1}")] + [Parameter(ParameterSetName = IndividualSetting)] + public string Code { get; set; } + + /// + /// Gets or sets the VT100 escape sequence for image alt text foreground. + /// + [ValidatePattern(@"^\[*[0-9;]*?m{1}")] + [Parameter(ParameterSetName = IndividualSetting)] + public string ImageAltTextForegroundColor { get; set; } + + /// + /// Gets or sets the VT100 escape sequence for link foreground. + /// + [ValidatePattern(@"^\[*[0-9;]*?m{1}")] + [Parameter(ParameterSetName = IndividualSetting)] + public string LinkForegroundColor { get; set; } + + /// + /// Gets or sets the VT100 escape sequence for italics text foreground. + /// + [ValidatePattern(@"^\[*[0-9;]*?m{1}")] + [Parameter(ParameterSetName = IndividualSetting)] + public string ItalicsForegroundColor { get; set; } + + /// + /// Gets or sets the VT100 escape sequence for bold text foreground. + /// + [ValidatePattern(@"^\[*[0-9;]*?m{1}")] + [Parameter(ParameterSetName = IndividualSetting)] + public string BoldForegroundColor { get; set; } + + /// + /// Gets or sets the switch to PassThru the values set. + /// + [Parameter] + public SwitchParameter PassThru { get; set; } + + /// + /// Gets or sets the Theme. + /// + [ValidateNotNullOrEmpty] + [Parameter(ParameterSetName = ThemeParamSet, Mandatory = true)] + [ValidateSet(DarkThemeName, LightThemeName)] + public string Theme { get; set; } + + /// + /// Gets or sets InputObject. + /// + [ValidateNotNullOrEmpty] + [Parameter(ParameterSetName = InputObjectParamSet, Mandatory = true, ValueFromPipeline = true, Position = 0)] + public PSObject InputObject { get; set; } + + private const string IndividualSetting = "IndividualSetting"; + private const string InputObjectParamSet = "InputObject"; + private const string ThemeParamSet = "Theme"; + private const string LightThemeName = "Light"; + private const string DarkThemeName = "Dark"; + + /// + /// Override EndProcessing. + /// + protected override void EndProcessing() + { + PSMarkdownOptionInfo mdOptionInfo = null; + + switch (ParameterSetName) + { + case ThemeParamSet: + mdOptionInfo = new PSMarkdownOptionInfo(); + if (string.Equals(Theme, LightThemeName, StringComparison.OrdinalIgnoreCase)) + { + mdOptionInfo.SetLightTheme(); + } + else if (string.Equals(Theme, DarkThemeName, StringComparison.OrdinalIgnoreCase)) + { + mdOptionInfo.SetDarkTheme(); + } + + break; + + case InputObjectParamSet: + object baseObj = InputObject.BaseObject; + mdOptionInfo = baseObj as PSMarkdownOptionInfo; + + if (mdOptionInfo == null) + { + var errorMessage = StringUtil.Format(ConvertMarkdownStrings.InvalidInputObjectType, baseObj.GetType()); + + ErrorRecord errorRecord = new( + new ArgumentException(errorMessage), + "InvalidObject", + ErrorCategory.InvalidArgument, + InputObject); + } + + break; + + case IndividualSetting: + mdOptionInfo = new PSMarkdownOptionInfo(); + SetOptions(mdOptionInfo); + break; + } + + var setOption = PSMarkdownOptionInfoCache.Set(this.CommandInfo, mdOptionInfo); + + if (PassThru.IsPresent) + { + WriteObject(setOption); + } + } + + private void SetOptions(PSMarkdownOptionInfo mdOptionInfo) + { + if (!string.IsNullOrEmpty(Header1Color)) + { + mdOptionInfo.Header1 = Header1Color; + } + + if (!string.IsNullOrEmpty(Header2Color)) + { + mdOptionInfo.Header2 = Header2Color; + } + + if (!string.IsNullOrEmpty(Header3Color)) + { + mdOptionInfo.Header3 = Header3Color; + } + + if (!string.IsNullOrEmpty(Header4Color)) + { + mdOptionInfo.Header4 = Header4Color; + } + + if (!string.IsNullOrEmpty(Header5Color)) + { + mdOptionInfo.Header5 = Header5Color; + } + + if (!string.IsNullOrEmpty(Header6Color)) + { + mdOptionInfo.Header6 = Header6Color; + } + + if (!string.IsNullOrEmpty(Code)) + { + mdOptionInfo.Code = Code; + } + + if (!string.IsNullOrEmpty(ImageAltTextForegroundColor)) + { + mdOptionInfo.Image = ImageAltTextForegroundColor; + } + + if (!string.IsNullOrEmpty(LinkForegroundColor)) + { + mdOptionInfo.Link = LinkForegroundColor; + } + + if (!string.IsNullOrEmpty(ItalicsForegroundColor)) + { + mdOptionInfo.EmphasisItalics = ItalicsForegroundColor; + } + + if (!string.IsNullOrEmpty(BoldForegroundColor)) + { + mdOptionInfo.EmphasisBold = BoldForegroundColor; + } + } + } + + /// + /// Implements the cmdlet for getting the Markdown options that are set. + /// + [Cmdlet( + VerbsCommon.Get, "MarkdownOption", + HelpUri = "https://go.microsoft.com/fwlink/?linkid=2006371")] + [OutputType(typeof(Microsoft.PowerShell.MarkdownRender.PSMarkdownOptionInfo))] + public class GetMarkdownOptionCommand : PSCmdlet + { + private const string MarkdownOptionInfoVariableName = "PSMarkdownOptionInfo"; + + /// + /// Override EndProcessing. + /// + protected override void EndProcessing() + { + WriteObject(PSMarkdownOptionInfoCache.Get(this.CommandInfo)); + } + } + + /// + /// The class manages whether we should use a module scope variable or concurrent dictionary for storing the set PSMarkdownOptions. + /// When we have a moduleInfo available we use the module scope variable. + /// In case of built-in modules, they are loaded as snapins when we are hosting PowerShell. + /// We use runspace Id as the key for the concurrent dictionary to have the functionality of separate settings per runspace. + /// Force loading the module does not unload the nested modules and hence we cannot use IModuleAssemblyCleanup to remove items from the dictionary. + /// Because of these reason, we continue using module scope variable when moduleInfo is available. + /// + internal static class PSMarkdownOptionInfoCache + { + private static readonly ConcurrentDictionary markdownOptionInfoCache; + + private const string MarkdownOptionInfoVariableName = "PSMarkdownOptionInfo"; + + static PSMarkdownOptionInfoCache() + { + markdownOptionInfoCache = new ConcurrentDictionary(); + } + + internal static PSMarkdownOptionInfo Get(CommandInfo command) + { + // If we have the moduleInfo then store are module scope variable + if (command.Module != null) + { + return command.Module.SessionState.PSVariable.GetValue(MarkdownOptionInfoVariableName, new PSMarkdownOptionInfo()) as PSMarkdownOptionInfo; + } + + // If we don't have a moduleInfo, like in PowerShell hosting scenarios, use a concurrent dictionary. + if (markdownOptionInfoCache.TryGetValue(Runspace.DefaultRunspace.InstanceId, out PSMarkdownOptionInfo cachedOption)) + { + // return the cached options for the runspaceId + return cachedOption; + } + else + { + // no option cache so cache and return the default PSMarkdownOptionInfo + var newOptionInfo = new PSMarkdownOptionInfo(); + return markdownOptionInfoCache.GetOrAdd(Runspace.DefaultRunspace.InstanceId, newOptionInfo); + } + } + + internal static PSMarkdownOptionInfo Set(CommandInfo command, PSMarkdownOptionInfo optionInfo) + { + // If we have the moduleInfo then store are module scope variable + if (command.Module != null) + { + command.Module.SessionState.PSVariable.Set(MarkdownOptionInfoVariableName, optionInfo); + return optionInfo; + } + + // If we don't have a moduleInfo, like in PowerShell hosting scenarios with modules loaded as snapins, use a concurrent dictionary. + return markdownOptionInfoCache.AddOrUpdate(Runspace.DefaultRunspace.InstanceId, optionInfo, (key, oldvalue) => optionInfo); + } + } +} diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/MatchString.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/MatchString.cs index 5cc6d26738b..262fd44b30f 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/MatchString.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/MatchString.cs @@ -1,17 +1,16 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. using System; -using System.Text.RegularExpressions; -using System.IO; using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; +using System.Globalization; +using System.IO; using System.Management.Automation; using System.Management.Automation.Internal; -using System.Globalization; -using System.Diagnostics.CodeAnalysis; +using System.Text; +using System.Text.RegularExpressions; namespace Microsoft.PowerShell.Commands { @@ -25,48 +24,42 @@ internal MatchInfoContext() } /// - /// Lines found before a match. + /// Gets or sets the lines found before a match. /// - - [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] public string[] PreContext { get; set; } /// - /// Lines found after a match. + /// Gets or sets the lines found after a match. /// - - [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] public string[] PostContext { get; set; } /// - /// Lines found before a match. Does not include + /// Gets or sets the lines found before a match. Does not include /// overlapping context and thus can be used to /// display contiguous match regions. /// - - [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] public string[] DisplayPreContext { get; set; } /// - /// Lines found after a match. Does not include + /// Gets or sets the lines found after a match. Does not include /// overlapping context and thus can be used to /// display contiguous match regions. /// - - [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] public string[] DisplayPostContext { get; set; } /// /// Produce a deep copy of this object. /// + /// A new object that is a copy of this instance. public object Clone() { - MatchInfoContext clone = new MatchInfoContext(); - clone.PreContext = (clone.PreContext != null) ? (string[])PreContext.Clone() : null; - clone.PostContext = (clone.PostContext != null) ? (string[])PostContext.Clone() : null; - clone.DisplayPreContext = (clone.DisplayPreContext != null) ? (string[])DisplayPreContext.Clone() : null; - clone.DisplayPostContext = (clone.DisplayPostContext != null) ? (string[])DisplayPostContext.Clone() : null; - return clone; + return new MatchInfoContext() + { + PreContext = (string[])PreContext?.Clone(), + PostContext = (string[])PostContext?.Clone(), + DisplayPreContext = (string[])DisplayPreContext?.Clone(), + DisplayPostContext = (string[])DisplayPostContext?.Clone() + }; } } @@ -75,98 +68,136 @@ public object Clone() /// public class MatchInfo { - private static string s_inputStream = "InputStream"; + private static readonly string s_inputStream = "InputStream"; /// - /// Indicates if the match was done ignoring case. + /// Gets or sets a value indicating whether the match was done ignoring case. /// /// True if case was ignored. public bool IgnoreCase { get; set; } /// - /// Returns the number of the matching line. + /// Gets or sets the number of the matching line. /// /// The number of the matching line. - public int LineNumber { get; set; } + public ulong LineNumber { get; set; } /// - /// Returns the text of the matching line. + /// Gets or sets the text of the matching line. /// /// The text of the matching line. - public string Line { get; set; } = ""; + public string Line { get; set; } = string.Empty; + + /// + /// Gets or sets a value indicating whether the matched portion of the string is highlighted. + /// + /// Whether the matched portion of the string is highlighted with the negative VT sequence. + private readonly bool _emphasize; + + /// + /// Stores the starting index of each match within the line. + /// + private readonly IReadOnlyList _matchIndexes; + + /// + /// Stores the length of each match within the line. + /// + private readonly IReadOnlyList _matchLengths; + + /// + /// Initializes a new instance of the class with emphasis disabled. + /// + public MatchInfo() + { + this._emphasize = false; + } + + /// + /// Initializes a new instance of the class with emphasized matched text. + /// Used when virtual terminal sequences are supported. + /// + /// Sets the matchIndexes. + /// Sets the matchLengths. + public MatchInfo(IReadOnlyList matchIndexes, IReadOnlyList matchLengths) + { + this._emphasize = true; + this._matchIndexes = matchIndexes; + this._matchLengths = matchLengths; + } /// - /// Returns the base name of the file containing the matching line. + /// Gets the base name of the file containing the matching line. + /// /// /// It will be the string "InputStream" if the object came from the input stream. - /// This is a readonly property calculated from the path. + /// This is a readonly property calculated from the path . /// - /// - /// The file name + /// The file name. public string Filename { get { if (!_pathSet) + { return s_inputStream; - return _filename ?? (_filename = System.IO.Path.GetFileName(_path)); + } + + return _filename ??= System.IO.Path.GetFileName(_path); } } - private string _filename; + private string _filename; /// - /// The full path of the file containing the matching line. + /// Gets or sets the full path of the file containing the matching line. + /// /// /// It will be "InputStream" if the object came from the input stream. /// - /// - /// The path name + /// The path name. public string Path { - get - { - if (!_pathSet) - return s_inputStream; - return _path; - } + get => _pathSet ? _path : s_inputStream; set { _path = value; _pathSet = true; } } + private string _path = s_inputStream; + private bool _pathSet; /// - /// Returns the pattern that was used in the match. + /// Gets or sets the pattern that was used in the match. /// - /// The pattern string + /// The pattern string. public string Pattern { get; set; } /// - /// The context for the match, or null if -context was not - /// specified. + /// Gets or sets context for the match, or null if -context was not specified. /// public MatchInfoContext Context { get; set; } /// /// Returns the path of the matching file truncated relative to the parameter. + /// /// /// For example, if the matching path was c:\foo\bar\baz.c and the directory argument was c:\foo - /// the routine would return bar\baz.c + /// the routine would return bar\baz.c . /// - /// /// The directory base the truncation on. /// The relative path that was produced. public string RelativePath(string directory) { if (!_pathSet) + { return this.Path; + } string relPath = _path; - if (!String.IsNullOrEmpty(directory)) + if (!string.IsNullOrEmpty(directory)) { if (relPath.StartsWith(directory, StringComparison.OrdinalIgnoreCase)) { @@ -174,12 +205,17 @@ public string RelativePath(string directory) if (offset < relPath.Length) { if (directory[offset - 1] == '\\' || directory[offset - 1] == '/') + { relPath = relPath.Substring(offset); + } else if (relPath[offset] == '\\' || relPath[offset] == '/') + { relPath = relPath.Substring(offset + 1); + } } } } + return relPath; } @@ -196,13 +232,13 @@ public string RelativePath(string directory) /// /// Returns the string representation of this object. The format /// depends on whether a path has been set for this object or not. + /// /// /// If the path component is set, as would be the case when matching /// in a file, ToString() would return the path, line number and line text. /// If path is not set, then just the line text is presented. /// - /// - /// The string representation of the match object + /// The string representation of the match object. public override string ToString() { return ToString(null); @@ -212,9 +248,22 @@ public override string ToString() /// Returns the string representation of the match object same format as ToString() /// but trims the path to be relative to the argument. /// - /// Directory to use as the root when calculating the relative path - /// The string representation of the match object + /// Directory to use as the root when calculating the relative path. + /// The string representation of the match object. public string ToString(string directory) + { + return ToString(directory, Line); + } + + /// + /// Returns the string representation of the match object with the matched line passed + /// in as and trims the path to be relative to + /// the argument. + /// + /// Directory to use as the root when calculating the relative path. + /// Line that the match occurs in. + /// The string representation of the match object. + private string ToString(string directory, string line) { string displayPath = (directory != null) ? RelativePath(directory) : _path; @@ -222,26 +271,81 @@ public string ToString(string directory) // enable context-tracking. if (Context == null) { - return FormatLine(Line, this.LineNumber, displayPath, EmptyPrefix); + return FormatLine(line, this.LineNumber, displayPath, EmptyPrefix); } // Otherwise, render the full context. - List lines = new List(Context.DisplayPreContext.Length + Context.DisplayPostContext.Length + 1); + List lines = new(Context.DisplayPreContext.Length + Context.DisplayPostContext.Length + 1); - int displayLineNumber = this.LineNumber - Context.DisplayPreContext.Length; + ulong displayLineNumber = this.LineNumber - (ulong)Context.DisplayPreContext.Length; foreach (string contextLine in Context.DisplayPreContext) { lines.Add(FormatLine(contextLine, displayLineNumber++, displayPath, ContextPrefix)); } - lines.Add(FormatLine(Line, displayLineNumber++, displayPath, MatchPrefix)); + lines.Add(FormatLine(line, displayLineNumber++, displayPath, MatchPrefix)); foreach (string contextLine in Context.DisplayPostContext) { lines.Add(FormatLine(contextLine, displayLineNumber++, displayPath, ContextPrefix)); } - return String.Join(System.Environment.NewLine, lines.ToArray()); + return string.Join(System.Environment.NewLine, lines.ToArray()); + } + + /// + /// Returns the string representation of the match object same format as ToString() + /// and inverts the color of the matched text if virtual terminal is supported. + /// + /// Directory to use as the root when calculating the relative path. + /// The string representation of the match object with matched text inverted. + public string ToEmphasizedString(string directory) + { + if (!_emphasize) + { + return ToString(directory); + } + + return ToString(directory, EmphasizeLine()); + } + + /// + /// Surrounds the matched text with virtual terminal sequences to invert it's color. Used in ToEmphasizedString. + /// + /// The matched line with matched text inverted. + private string EmphasizeLine() + { + string invertColorsVT100 = PSStyle.Instance.Reverse; + string resetVT100 = PSStyle.Instance.Reset; + + char[] chars = new char[(_matchIndexes.Count * (invertColorsVT100.Length + resetVT100.Length)) + Line.Length]; + int lineIndex = 0; + int charsIndex = 0; + for (int i = 0; i < _matchIndexes.Count; i++) + { + // Adds characters before match + Line.CopyTo(lineIndex, chars, charsIndex, _matchIndexes[i] - lineIndex); + charsIndex += _matchIndexes[i] - lineIndex; + lineIndex = _matchIndexes[i]; + + // Adds opening vt sequence + invertColorsVT100.CopyTo(0, chars, charsIndex, invertColorsVT100.Length); + charsIndex += invertColorsVT100.Length; + + // Adds characters being emphasized + Line.CopyTo(lineIndex, chars, charsIndex, _matchLengths[i]); + lineIndex += _matchLengths[i]; + charsIndex += _matchLengths[i]; + + // Adds closing vt sequence + resetVT100.CopyTo(0, chars, charsIndex, resetVT100.Length); + charsIndex += resetVT100.Length; + } + + // Adds remaining characters in line + Line.CopyTo(lineIndex, chars, charsIndex, Line.Length - lineIndex); + + return new string(chars); } /// @@ -252,23 +356,22 @@ public string ToString(string directory) /// The file path, formatted for display. /// The match prefix. /// The formatted line as a string. - private string FormatLine(string lineStr, int displayLineNumber, string displayPath, string prefix) + private string FormatLine(string lineStr, ulong displayLineNumber, string displayPath, string prefix) { - if (_pathSet) - return StringUtil.Format(MatchFormat, prefix, displayPath, displayLineNumber, lineStr); - else - return StringUtil.Format(SimpleFormat, prefix, lineStr); + return _pathSet + ? StringUtil.Format(MatchFormat, prefix, displayPath, displayLineNumber, lineStr) + : StringUtil.Format(SimpleFormat, prefix, lineStr); } /// - /// A list of all Regex matches on the matching line. + /// Gets or sets a list of all Regex matches on the matching line. /// - [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] - public Match[] Matches { get; set; } = new Match[] { }; + public Match[] Matches { get; set; } = Array.Empty(); /// /// Create a deep copy of this MatchInfo instance. /// + /// A new object that is a copy of this instance. internal MatchInfo Clone() { // Just do a shallow copy and then deep-copy the @@ -291,17 +394,27 @@ internal MatchInfo Clone() /// /// A cmdlet to search through strings and files for particular patterns. /// - [Cmdlet(VerbsCommon.Select, "String", DefaultParameterSetName = "File", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113388")] - [OutputType(typeof(MatchInfo), typeof(bool))] + [Cmdlet(VerbsCommon.Select, "String", DefaultParameterSetName = ParameterSetFile, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2097119")] + [OutputType(typeof(bool), typeof(MatchInfo), ParameterSetName = new[] { ParameterSetFile, ParameterSetObject, ParameterSetLiteralFile })] + [OutputType(typeof(string), ParameterSetName = new[] { ParameterSetFileRaw, ParameterSetObjectRaw, ParameterSetLiteralFileRaw })] public sealed class SelectStringCommand : PSCmdlet { + private const string ParameterSetFile = "File"; + private const string ParameterSetFileRaw = "FileRaw"; + private const string ParameterSetObject = "Object"; + private const string ParameterSetObjectRaw = "ObjectRaw"; + private const string ParameterSetLiteralFile = "LiteralFile"; + private const string ParameterSetLiteralFileRaw = "LiteralFileRaw"; + /// /// A generic circular buffer. /// - private class CircularBuffer : ICollection + /// The type of items that are buffered. + private sealed class CircularBuffer : ICollection { // Ring of items - private T[] _items; + private readonly T[] _items; + // Current length, as opposed to the total capacity // Current start of the list. Starts at 0, but may // move forwards or wrap around back to 0 due to @@ -309,59 +422,46 @@ private class CircularBuffer : ICollection private int _firstIndex; /// - /// Construct a new buffer of the specified capacity. + /// Initializes a new instance of the class. /// /// The maximum capacity of the buffer. - /// If is negative. + /// If is negative. public CircularBuffer(int capacity) { - if (capacity < 0) - throw new ArgumentOutOfRangeException("capacity"); + ArgumentOutOfRangeException.ThrowIfNegative(capacity); _items = new T[capacity]; Clear(); } /// - /// The maximum capacity of the buffer. If more items + /// Gets the maximum capacity of the buffer. If more items /// are added than the buffer has capacity for, then /// older items will be removed from the buffer with /// a first-in, first-out policy. /// - public int Capacity - { - get - { - return _items.Length; - } - } + public int Capacity => _items.Length; /// /// Whether or not the buffer is at capacity. /// - public bool IsFull - { - get - { - return Count == Capacity; - } - } + public bool IsFull => Count == Capacity; /// /// Convert from a 0-based index to a buffer index which /// has been properly offset and wrapped. /// /// The index to wrap. - /// If is out of range. + /// If is out of range. /// - /// The actual index that + /// The actual index that /// maps to. /// private int WrapIndex(int zeroBasedIndex) { if (Capacity == 0 || zeroBasedIndex < 0) { - throw new ArgumentOutOfRangeException("zeroBasedIndex"); + throw new ArgumentOutOfRangeException(nameof(zeroBasedIndex)); } return (zeroBasedIndex + _firstIndex) % Capacity; @@ -385,13 +485,7 @@ IEnumerator IEnumerable.GetEnumerator() #region ICollection implementation public int Count { get; private set; } - public bool IsReadOnly - { - get - { - return false; - } - } + public bool IsReadOnly => false; /// /// Adds an item to the buffer. If the buffer is already @@ -433,18 +527,15 @@ public bool Contains(T item) throw new NotImplementedException(); } - - [SuppressMessage("Microsoft.Usage", "CA2208:InstantiateArgumentExceptionsCorrectly")] public void CopyTo(T[] array, int arrayIndex) { - if (array == null) - throw new ArgumentNullException("array"); - - if (arrayIndex < 0) - throw new ArgumentOutOfRangeException("arrayIndex"); + ArgumentNullException.ThrowIfNull(array); + ArgumentOutOfRangeException.ThrowIfNegative(arrayIndex); if (Count > (array.Length - arrayIndex)) + { throw new ArgumentException("arrayIndex"); + } // Iterate through the buffer in correct order. foreach (T item in this) @@ -477,13 +568,14 @@ public T[] ToArray() /// internal ordering the buffer may be maintaining. /// /// The index of the item to access. + /// The buffered item at index . public T this[int index] { get { if (!(index >= 0 && index < Count)) { - throw new ArgumentOutOfRangeException("index"); + throw new ArgumentOutOfRangeException(nameof(index)); } return _items[WrapIndex(index)]; @@ -497,7 +589,7 @@ public T this[int index] private interface IContextTracker { /// - /// Matches with completed context information + /// Gets matches with completed context information /// that are ready to be emitted into the pipeline. /// IList EmitQueue { get; } @@ -525,7 +617,7 @@ private interface IContextTracker /// /// A state machine to track display context for each match. /// - private class DisplayContextTracker : IContextTracker + private sealed class DisplayContextTracker : IContextTracker { private enum ContextState { @@ -535,14 +627,14 @@ private enum ContextState } private ContextState _contextState = ContextState.InitialState; - private int _preContext = 0; - private int _postContext = 0; + private readonly int _preContext; + private readonly int _postContext; // The context leading up to the match. - private CircularBuffer _collectedPreContext = null; + private readonly CircularBuffer _collectedPreContext; // The context after the match. - private List _collectedPostContext = null; + private readonly List _collectedPostContext; // Current match info we are tracking postcontext for. // At any given time, if set, this value will not be @@ -550,10 +642,10 @@ private enum ContextState private MatchInfo _matchInfo = null; /// - /// Constructor for DisplayContextTracker. + /// Initializes a new instance of the class. /// - /// How much precontext to collect at most. - /// How much precontext to collect at most. + /// How much preContext to collect at most. + /// How much postContext to collect at most. public DisplayContextTracker(int preContext, int postContext) { _preContext = preContext; @@ -566,14 +658,9 @@ public DisplayContextTracker(int preContext, int postContext) } #region IContextTracker implementation - public IList EmitQueue - { - get - { - return _emitQueue; - } - } - private List _emitQueue = null; + public IList EmitQueue => _emitQueue; + + private readonly List _emitQueue; // Track non-matching line public void TrackLine(string line) @@ -594,6 +681,7 @@ public void TrackLine(string line) // Now we're done. UpdateQueue(); } + break; } } @@ -604,7 +692,9 @@ public void TrackMatch(MatchInfo match) // Update the queue in case we were in the middle // of collecting postcontext for an older match... if (_contextState == ContextState.CollectPost) + { UpdateQueue(); + } // Update the current matchInfo. _matchInfo = match; @@ -614,9 +704,13 @@ public void TrackMatch(MatchInfo match) // Otherwise, immediately move the match onto the queue // and let UpdateQueue update our state instead. if (_postContext > 0) + { _contextState = ContextState.CollectPost; + } else + { UpdateQueue(); + } } // Track having reached the end of the file. @@ -627,7 +721,9 @@ public void TrackEOF() // early since there are no more lines to track context // for. if (_contextState == ContextState.CollectPost) + { UpdateQueue(); + } } #endregion @@ -646,6 +742,7 @@ private void UpdateQueue() _matchInfo.Context.DisplayPreContext = _collectedPreContext.ToArray(); _matchInfo.Context.DisplayPostContext = _collectedPostContext.ToArray(); } + Reset(); } } @@ -675,17 +772,17 @@ private void Reset() /// a possibly-continuous set of matches by excluding /// overlapping context (lines will only appear once) /// and other matching lines (since they will appear - /// as their own match entries.) + /// as their own match entries.). /// - private class LogicalContextTracker : IContextTracker + private sealed class LogicalContextTracker : IContextTracker { // A union: string | MatchInfo. Needed since // context lines could be either proper matches // or non-matching lines. - private class ContextEntry + private sealed class ContextEntry { - public string Line = null; - public MatchInfo Match = null; + public readonly string Line; + public readonly MatchInfo Match; public ContextEntry(string line) { @@ -697,20 +794,18 @@ public ContextEntry(MatchInfo match) Match = match; } - public override string ToString() - { - return (Match != null) ? Match.Line : Line; - } + public override string ToString() => Match?.Line ?? Line; } // Whether or not early entries found // while still filling up the context buffer // have been added to the emit queue. // Used by UpdateQueue. - private bool _hasProcessedPreEntries = false; + private bool _hasProcessedPreEntries; + + private readonly int _preContext; + private readonly int _postContext; - private int _preContext; - private int _postContext; // A circular buffer tracking both precontext and postcontext. // // Essentially, the buffer is separated into regions: @@ -724,13 +819,13 @@ public override string ToString() // enough context to populate the Context properties of the // match. At that point, we will add the match object // to the emit queue. - private CircularBuffer _collectedContext = null; + private readonly CircularBuffer _collectedContext; /// - /// Constructor for LogicalContextTracker. + /// Initializes a new instance of the class. /// - /// How much precontext to collect at most. - /// How much postcontext to collect at most. + /// How much preContext to collect at most. + /// How much postContext to collect at most. public LogicalContextTracker(int preContext, int postContext) { _preContext = preContext; @@ -740,25 +835,20 @@ public LogicalContextTracker(int preContext, int postContext) } #region IContextTracker implementation - public IList EmitQueue - { - get - { - return _emitQueue; - } - } - private List _emitQueue = null; + public IList EmitQueue => _emitQueue; + + private readonly List _emitQueue; public void TrackLine(string line) { - ContextEntry entry = new ContextEntry(line); + ContextEntry entry = new(line); _collectedContext.Add(entry); UpdateQueue(); } public void TrackMatch(MatchInfo match) { - ContextEntry entry = new ContextEntry(match); + ContextEntry entry = new(match); _collectedContext.Add(entry); UpdateQueue(); } @@ -775,8 +865,7 @@ public void TrackEOF() // If the buffer isn't full, then nothing will have // ever been emitted and everything is still waiting // on postcontext. So process the whole buffer. - - int startIndex = (_collectedContext.IsFull) ? _preContext + 1 : 0; + int startIndex = _collectedContext.IsFull ? _preContext + 1 : 0; EmitAllInRange(startIndex, _collectedContext.Count - 1); } #endregion @@ -784,7 +873,7 @@ public void TrackEOF() /// /// Add all matches found in the specified range /// to the emit queue, collecting as much context - /// as possible up to the limits specified in the ctor. + /// as possible up to the limits specified in the constructor. /// /// /// The range is inclusive; the entries at @@ -847,14 +936,13 @@ private void UpdateQueue() /// and adds it to the emit queue. /// /// - /// Context ranges must be within the bounds of the context - /// buffer. + /// Context ranges must be within the bounds of the context buffer. /// /// The match to operate on. - /// The start index of the precontext range. - /// The length of the precontext range. - /// The start index of the postcontext range. - /// The length of the precontext range. + /// The start index of the preContext range. + /// The length of the preContext range. + /// The start index of the postContext range. + /// The length of the postContext range. private void Emit(MatchInfo match, int preStartIndex, int preLength, int postStartIndex, int postLength) { if (match.Context != null) @@ -874,6 +962,7 @@ private void Emit(MatchInfo match, int preStartIndex, int preLength, int postSta /// /// The index to start at. /// The length of the range. + /// String representation of the collected context at the specified range. private string[] CopyContext(int startIndex, int length) { string[] result = new string[length]; @@ -890,16 +979,16 @@ private string[] CopyContext(int startIndex, int length) /// /// A class to track both logical and display contexts. /// - private class ContextTracker : IContextTracker + private sealed class ContextTracker : IContextTracker { - private IContextTracker _displayTracker; - private IContextTracker _logicalTracker; + private readonly IContextTracker _displayTracker; + private readonly IContextTracker _logicalTracker; /// - /// Constructor for LogicalContextTracker. + /// Initializes a new instance of the class. /// - /// How much precontext to collect at most. - /// How much postcontext to collect at most. + /// How much preContext to collect at most. + /// How much postContext to collect at most. public ContextTracker(int preContext, int postContext) { _displayTracker = new DisplayContextTracker(preContext, postContext); @@ -946,7 +1035,6 @@ private void UpdateQueue() // time as the logical tracker, so we can // be sure the matches will have both logical // and display context already populated. - foreach (MatchInfo match in _logicalTracker.EmitQueue) { EmitQueue.Add(match); @@ -958,264 +1046,317 @@ private void UpdateQueue() } /// - /// This parameter specifies the current pipeline object + /// ContextTracker that does not work for the case when pre- and post context is 0. /// - [Parameter(ValueFromPipeline = true, Mandatory = true, ParameterSetName = "Object")] - [AllowNull] - [AllowEmptyString] - public PSObject InputObject + private sealed class NoContextTracker : IContextTracker + { + private readonly IList _matches = new List(1); + + IList IContextTracker.EmitQueue => _matches; + + void IContextTracker.TrackLine(string line) + { + } + + void IContextTracker.TrackMatch(MatchInfo match) => _matches.Add(match); + + void IContextTracker.TrackEOF() + { + } + } + + /// + /// Gets or sets a culture name. + /// + [Parameter] + [ValidateSet(typeof(ValidateMatchStringCultureNamesGenerator))] + [ValidateNotNull] + public string Culture { get { - return _inputObject; + switch (_stringComparison) + { + case StringComparison.Ordinal: + case StringComparison.OrdinalIgnoreCase: + { + return OrdinalCultureName; + } + + case StringComparison.InvariantCulture: + case StringComparison.InvariantCultureIgnoreCase: + { + return InvariantCultureName; + } + + case StringComparison.CurrentCulture: + case StringComparison.CurrentCultureIgnoreCase: + { + return CurrentCultureName; + } + + default: + { + break; + } + } + + return _cultureName; } + set { - _inputObject = LanguagePrimitives.IsNull(value) ? PSObject.AsPSObject("") : value; + _cultureName = value; + InitCulture(); } } + + internal const string OrdinalCultureName = "Ordinal"; + internal const string InvariantCultureName = "Invariant"; + internal const string CurrentCultureName = "Current"; + + private string _cultureName = CultureInfo.CurrentCulture.Name; + private StringComparison _stringComparison = StringComparison.CurrentCultureIgnoreCase; + private CompareOptions _compareOptions = CompareOptions.IgnoreCase; + + private delegate int CultureInfoIndexOf(string source, string value, int startIndex, int count, CompareOptions options); + + private CultureInfoIndexOf _cultureInfoIndexOf = CultureInfo.CurrentCulture.CompareInfo.IndexOf; + + private void InitCulture() + { + _stringComparison = default; + + switch (_cultureName) + { + case OrdinalCultureName: + { + _stringComparison = CaseSensitive ? StringComparison.Ordinal : StringComparison.OrdinalIgnoreCase; + _compareOptions = CaseSensitive ? CompareOptions.Ordinal : CompareOptions.OrdinalIgnoreCase; + _cultureInfoIndexOf = CultureInfo.InvariantCulture.CompareInfo.IndexOf; + break; + } + + case InvariantCultureName: + { + _stringComparison = CaseSensitive ? StringComparison.InvariantCulture : StringComparison.InvariantCultureIgnoreCase; + _compareOptions = CaseSensitive ? CompareOptions.None : CompareOptions.IgnoreCase; + _cultureInfoIndexOf = CultureInfo.InvariantCulture.CompareInfo.IndexOf; + break; + } + + case CurrentCultureName: + { + _stringComparison = CaseSensitive ? StringComparison.CurrentCulture : StringComparison.CurrentCultureIgnoreCase; + _compareOptions = CaseSensitive ? CompareOptions.None : CompareOptions.IgnoreCase; + _cultureInfoIndexOf = CultureInfo.CurrentCulture.CompareInfo.IndexOf; + break; + } + + default: + { + var _cultureInfo = CultureInfo.GetCultureInfo(_cultureName); + _compareOptions = CaseSensitive ? CompareOptions.None : CompareOptions.IgnoreCase; + _cultureInfoIndexOf = _cultureInfo.CompareInfo.IndexOf; + break; + } + } + } + + /// + /// Gets or sets the current pipeline object. + /// + [Parameter(ValueFromPipeline = true, Mandatory = true, ParameterSetName = ParameterSetObject)] + [Parameter(ValueFromPipeline = true, Mandatory = true, ParameterSetName = ParameterSetObjectRaw)] + [AllowNull] + [AllowEmptyString] + public PSObject InputObject + { + get => _inputObject; + set => _inputObject = LanguagePrimitives.IsNull(value) ? PSObject.AsPSObject(string.Empty) : value; + } + private PSObject _inputObject = AutomationNull.Value; /// - /// String index to start from the beginning. - /// - /// If the value is negative, the length is counted from the - /// end of the string. + /// Gets or sets the patterns to find. /// - /// [Parameter(Mandatory = true, Position = 0)] public string[] Pattern { get; set; } private Regex[] _regexPattern; /// - /// file to read from - /// Globbing is done on these + /// Gets or sets files to read from. + /// Globbing is done on these. /// - [Parameter(Position = 1, Mandatory = true, ValueFromPipelineByPropertyName = true, ParameterSetName = "File")] + [Parameter(Position = 1, Mandatory = true, ValueFromPipelineByPropertyName = true, ParameterSetName = ParameterSetFile)] + [Parameter(Position = 1, Mandatory = true, ValueFromPipelineByPropertyName = true, ParameterSetName = ParameterSetFileRaw)] [FileinfoToString] public string[] Path { get; set; } /// - /// Literal file to read from - /// Globbing is not done on these + /// Gets or sets literal files to read from. + /// Globbing is not done on these. /// - [Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true, ParameterSetName = "LiteralFile")] + [Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true, ParameterSetName = ParameterSetLiteralFile)] + [Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true, ParameterSetName = ParameterSetLiteralFileRaw)] [FileinfoToString] - [Alias("PSPath")] - [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] + [Alias("PSPath", "LP")] public string[] LiteralPath { - get - { - return Path; - } + get => Path; set { Path = value; _isLiteralPath = true; } } - private bool _isLiteralPath = false; - /// If set, match pattern string literally. - /// If not (default) search using pattern as a Regular Expression + private bool _isLiteralPath; + + /// + /// Gets or sets a value indicating if only string values containing matched lines should be returned. + /// If not (default) return MatchInfo (or bool objects, when Quiet is passed). + /// + [Parameter(Mandatory = true, ParameterSetName = ParameterSetObjectRaw)] + [Parameter(Mandatory = true, ParameterSetName = ParameterSetFileRaw)] + [Parameter(Mandatory = true, ParameterSetName = ParameterSetLiteralFileRaw)] + public SwitchParameter Raw { get; set; } + + /// + /// Gets or sets a value indicating if a pattern string should be matched literally. + /// If not (default) search using pattern as a Regular Expression. /// [Parameter] - public SwitchParameter SimpleMatch - { - get - { - return _simpleMatch; - } - set - { - _simpleMatch = value; - } - } - private bool _simpleMatch; + public SwitchParameter SimpleMatch { get; set; } - /// - /// If true, then do case-sensitive searches... + /// + /// Gets or sets a value indicating if the search is case sensitive.If true, then do case-sensitive searches. /// [Parameter] - public SwitchParameter CaseSensitive - { - get - { - return _caseSensitive; - } - set - { - _caseSensitive = value; - } - } - private bool _caseSensitive; + public SwitchParameter CaseSensitive { get; set; } /// - /// If true the cmdlet will stop processing at the first successful match and + /// Gets or sets a value indicating if the cmdlet will stop processing at the first successful match and /// return true. If both List and Quiet parameters are given, an exception is thrown. /// - [Parameter] - public SwitchParameter Quiet - { - get - { - return _quiet; - } - set - { - _quiet = value; - } - } - private bool _quiet; + [Parameter(ParameterSetName = ParameterSetObject)] + [Parameter(ParameterSetName = ParameterSetFile)] + [Parameter(ParameterSetName = ParameterSetLiteralFile)] + public SwitchParameter Quiet { get; set; } /// - /// list files where a match is found + /// Gets or sets a value indicating if matching files should be listed. /// This is the Unix functionality this switch is intended to mimic; /// the actual action of this option is to stop after the first match /// is found and returned from any particular file. /// [Parameter] - public SwitchParameter List - { - get - { - return _list; - } - set - { - _list = value; - } - } - private bool _list; + public SwitchParameter List { get; set; } /// - /// Lets you include particular files. Files not matching - /// one of these (if specified) are excluded. + /// Gets or sets a value indicating if highlighting should be disabled. + /// + [Parameter] + public SwitchParameter NoEmphasis { get; set; } + + /// + /// Gets or sets files to include. Files matching + /// one of these (if specified) are included. /// /// Invalid wildcard pattern was specified. [Parameter] [ValidateNotNullOrEmpty] public string[] Include { - get - { - return includeStrings; - } + get => _includeStrings; set { // null check is not needed (because of ValidateNotNullOrEmpty), // but we have to include it to silence OACR - if (value == null) - { - throw PSTraceSource.NewArgumentNullException("value"); - } - - includeStrings = value; + _includeStrings = value ?? throw PSTraceSource.NewArgumentNullException(nameof(value)); - this.include = new WildcardPattern[includeStrings.Length]; - for (int i = 0; i < includeStrings.Length; i++) + _include = new WildcardPattern[_includeStrings.Length]; + for (int i = 0; i < _includeStrings.Length; i++) { - this.include[i] = WildcardPattern.Get(includeStrings[i], WildcardOptions.IgnoreCase); + _include[i] = WildcardPattern.Get(_includeStrings[i], WildcardOptions.IgnoreCase); } } } - internal string[] includeStrings = null; - internal WildcardPattern[] include = null; + + private string[] _includeStrings; + + private WildcardPattern[] _include; /// - /// Lets you exclude particular files. Files matching + /// Gets or sets files to exclude. Files matching /// one of these (if specified) are excluded. /// [Parameter] [ValidateNotNullOrEmpty] public string[] Exclude { - get - { - return excludeStrings; - } + get => _excludeStrings; set { // null check is not needed (because of ValidateNotNullOrEmpty), // but we have to include it to silence OACR - if (value == null) - { - throw PSTraceSource.NewArgumentNullException("value"); - } - - excludeStrings = value; + _excludeStrings = value ?? throw PSTraceSource.NewArgumentNullException("value"); - this.exclude = new WildcardPattern[excludeStrings.Length]; - for (int i = 0; i < excludeStrings.Length; i++) + _exclude = new WildcardPattern[_excludeStrings.Length]; + for (int i = 0; i < _excludeStrings.Length; i++) { - this.exclude[i] = WildcardPattern.Get(excludeStrings[i], WildcardOptions.IgnoreCase); + _exclude[i] = WildcardPattern.Get(_excludeStrings[i], WildcardOptions.IgnoreCase); } } } - internal string[] excludeStrings; - internal WildcardPattern[] exclude; + + private string[] _excludeStrings; + + private WildcardPattern[] _exclude; /// - /// Only show lines which do not match. + /// Gets or sets a value indicating whether to only show lines which do not match. /// Equivalent to grep -v/findstr -v. /// [Parameter] - public SwitchParameter NotMatch - { - get - { - return _notMatch; - } - set - { - _notMatch = value; - } - } - private bool _notMatch; + public SwitchParameter NotMatch { get; set; } /// - /// If set, sets the Matches property of MatchInfo to the result - /// of calling System.Text.RegularExpressions.Regex.Matches() on + /// Gets or sets a value indicating whether the Matches property of MatchInfo should be set + /// to the result of calling System.Text.RegularExpressions.Regex.Matches() on /// the corresponding line. - /// /// Has no effect if -SimpleMatch is also specified. /// [Parameter] - public SwitchParameter AllMatches + public SwitchParameter AllMatches { get; set; } + + /// + /// Gets or sets the text encoding to process each file as. + /// + [Parameter] + [ArgumentToEncodingTransformation] + [ArgumentEncodingCompletions] + [ValidateNotNullOrEmpty] + public Encoding Encoding { get { - return _allMatches; + return _encoding; } + set { - _allMatches = value; + EncodingConversion.WarnIfObsolete(this, value); + _encoding = value; } } - private bool _allMatches; - /// - /// The text encoding to process each file as. - /// - [Parameter] - [ValidateNotNullOrEmpty] - [ValidateSetAttribute(new string[] { - EncodingConversion.Unicode, - EncodingConversion.Utf7, - EncodingConversion.Utf8, - EncodingConversion.Utf32, - EncodingConversion.Ascii, - EncodingConversion.BigEndianUnicode, - EncodingConversion.Default, - EncodingConversion.OEM })] - public string Encoding { get; set; } - - private System.Text.Encoding _textEncoding; + private Encoding _encoding = Encoding.Default; /// - /// The number of context lines to collect. If set to a + /// Gets or sets the number of context lines to collect. If set to a /// single integer value N, collects N lines each of pre- /// and post- context. If set to a 2-tuple B,A, collects B /// lines of pre- and A lines of post- context. @@ -1225,23 +1366,15 @@ public SwitchParameter AllMatches [Parameter] [ValidateNotNullOrEmpty] [ValidateCount(1, 2)] - [ValidateRange(0, Int32.MaxValue)] + [ValidateRange(0, int.MaxValue)] public new int[] Context { - get - { - return _context; - } + get => _context; set { // null check is not needed (because of ValidateNotNullOrEmpty), // but we have to include it to silence OACR - if (value == null) - { - throw PSTraceSource.NewArgumentNullException("value"); - } - - _context = value; + _context = value ?? throw PSTraceSource.NewArgumentNullException("value"); if (_context.Length == 1) { @@ -1255,10 +1388,18 @@ public SwitchParameter AllMatches } } } + private int[] _context; + private int _preContext = 0; + private int _postContext = 0; + // When we are in Raw mode or pre- and postcontext are zero, use the _noContextTracker, since we will not be needing trackedLines. + private IContextTracker GetContextTracker() => (Raw || (_preContext == 0 && _postContext == 0)) + ? _noContextTracker + : new ContextTracker(_preContext, _postContext); + // This context tracker is only used for strings which are piped // directly into the cmdlet. File processing doesn't need // to track state between calls to ProcessRecord, and so @@ -1266,7 +1407,9 @@ public SwitchParameter AllMatches // use a single global tracker for both is that in the case of // a mixed list of strings and FileInfo, the context tracker // would get reset after each file. - private ContextTracker _globalContextTracker = null; + private IContextTracker _globalContextTracker; + + private IContextTracker _noContextTracker; /// /// This is used to handle the case were we're done processing input objects. @@ -1274,26 +1417,31 @@ public SwitchParameter AllMatches /// private bool _doneProcessing; - private int _inputRecordNumber; + private ulong _inputRecordNumber; /// /// Read command line parameters. /// protected override void BeginProcessing() { - // Process encoding switch. - if (Encoding != null) + if (this.MyInvocation.BoundParameters.ContainsKey(nameof(Culture)) && !this.MyInvocation.BoundParameters.ContainsKey(nameof(SimpleMatch))) { - _textEncoding = EncodingConversion.Convert(this, Encoding); + InvalidOperationException exception = new(MatchStringStrings.CannotSpecifyCultureWithoutSimpleMatch); + ErrorRecord errorRecord = new(exception, "CannotSpecifyCultureWithoutSimpleMatch", ErrorCategory.InvalidData, null); + this.ThrowTerminatingError(errorRecord); } - else + + InitCulture(); + + string suppressVt = Environment.GetEnvironmentVariable("__SuppressAnsiEscapeSequences"); + if (!string.IsNullOrEmpty(suppressVt)) { - _textEncoding = new System.Text.UTF8Encoding(); + NoEmphasis = true; } - if (!_simpleMatch) + if (!SimpleMatch) { - RegexOptions regexOptions = (_caseSensitive) ? RegexOptions.None : RegexOptions.IgnoreCase; + RegexOptions regexOptions = CaseSensitive ? RegexOptions.None : RegexOptions.IgnoreCase; _regexPattern = new Regex[Pattern.Length]; for (int i = 0; i < Pattern.Length; i++) { @@ -1309,56 +1457,68 @@ protected override void BeginProcessing() } } - _globalContextTracker = new ContextTracker(_preContext, _postContext); + _noContextTracker = new NoContextTracker(); + _globalContextTracker = GetContextTracker(); } + private readonly List _inputObjectFileList = new(1) { string.Empty }; + /// - /// process the input + /// Process the input. /// - /// - /// Does not return a value - /// - /// Regular expression parsing error, path error + /// Regular expression parsing error, path error. /// A file cannot be found. /// A file cannot be found. protected override void ProcessRecord() { if (_doneProcessing) + { return; + } + // We may only have directories when we have resolved wildcards + var expandedPathsMaybeDirectory = false; List expandedPaths = null; if (Path != null) { expandedPaths = ResolveFilePaths(Path, _isLiteralPath); if (expandedPaths == null) + { return; + } + + expandedPathsMaybeDirectory = true; } else { - FileInfo fileInfo = _inputObject.BaseObject as FileInfo; - if (fileInfo != null) + if (_inputObject.BaseObject is FileInfo fileInfo) { - expandedPaths = new List(); - expandedPaths.Add(fileInfo.FullName); + _inputObjectFileList[0] = fileInfo.FullName; + expandedPaths = _inputObjectFileList; } } if (expandedPaths != null) { - foreach (string filename in expandedPaths) + foreach (var filename in expandedPaths) { - bool foundMatch = ProcessFile(filename); - if (_quiet && foundMatch) + if (expandedPathsMaybeDirectory && Directory.Exists(filename)) + { + continue; + } + + var foundMatch = ProcessFile(filename); + if (Quiet && foundMatch) + { return; + } } // No results in any files. - if (_quiet) + if (Quiet) { - if (_list) - WriteObject(null); - else - WriteObject(false); + var res = List ? null : Boxed.False; + WriteObject(res); } } else @@ -1369,16 +1529,15 @@ protected override void ProcessRecord() bool matched; MatchInfo result; MatchInfo matchInfo = null; - var line = _inputObject.BaseObject as string; - if (line != null) + if (_inputObject.BaseObject is string line) { - matched = doMatch(line, out result); + matched = DoMatch(line, out result); } else { matchInfo = _inputObject.BaseObject as MatchInfo; object objectToCheck = matchInfo ?? (object)_inputObject; - matched = doMatch(objectToCheck, out result, out line); + matched = DoMatch(objectToCheck, out result, out line); } if (matched) @@ -1388,6 +1547,7 @@ protected override void ProcessRecord() { result.LineNumber = _inputRecordNumber; } + // doMatch will have already set the pattern and line text... _globalContextTracker.TrackMatch(result); } @@ -1401,8 +1561,10 @@ protected override void ProcessRecord() { // If we're in quiet mode, go ahead and stop processing // now. - if (_quiet) + if (Quiet) + { _doneProcessing = true; + } } } } @@ -1415,7 +1577,7 @@ protected override void ProcessRecord() /// True if a match was found; otherwise false. private bool ProcessFile(string filename) { - ContextTracker contextTracker = new ContextTracker(_preContext, _postContext); + var contextTracker = GetContextTracker(); bool foundMatch = false; @@ -1423,15 +1585,17 @@ private bool ProcessFile(string filename) try { // see if the file is one the include exclude list... - if (!meetsIncludeExcludeCriteria(filename)) + if (!MeetsIncludeExcludeCriteria(filename)) + { return false; + } - using (FileStream fs = new FileStream(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) + using (FileStream fs = new(filename, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) { - using (StreamReader sr = new StreamReader(fs, _textEncoding)) + using (StreamReader sr = new(fs, Encoding)) { - String line; - int lineNo = 0; + string line; + ulong lineNo = 0; // Read and display lines from the file until the end of // the file is reached. @@ -1439,9 +1603,7 @@ private bool ProcessFile(string filename) { lineNo++; - MatchInfo result; - - if (doMatch(line, out result)) + if (DoMatch(line, out MatchInfo result)) { result.Path = filename; result.LineNumber = lineNo; @@ -1462,15 +1624,12 @@ private bool ProcessFile(string filename) // this file. It's done this way so the file is closed before emitting // the result so the downstream cmdlet can actually manipulate the file // that was found. - - if (_quiet || _list) + if (Quiet || List) { break; } - else - { - FlushTrackerQueue(contextTracker); - } + + FlushTrackerQueue(contextTracker); } } } @@ -1482,7 +1641,9 @@ private bool ProcessFile(string filename) // our postcontext. contextTracker.TrackEOF(); if (FlushTrackerQueue(contextTracker)) + { foundMatch = true; + } } catch (System.NotSupportedException nse) { @@ -1505,23 +1666,30 @@ private bool ProcessFile(string filename) } /// - /// Emit any objects which have been queued up, and clear - /// the queue. + /// Emit any objects which have been queued up, and clear the queue. /// /// The context tracker to operate on. /// Whether or not any objects were emitted. - private bool FlushTrackerQueue(ContextTracker contextTracker) + private bool FlushTrackerQueue(IContextTracker contextTracker) { // Do we even have any matches to emit? if (contextTracker.EmitQueue.Count < 1) + { return false; + } - // If -quiet is specified but not -list return true on first match - if (_quiet && !_list) + if (Raw) + { + foreach (MatchInfo match in contextTracker.EmitQueue) + { + WriteObject(match.Line); + } + } + else if (Quiet && !List) { WriteObject(true); } - else if (_list) + else if (List) { WriteObject(contextTracker.EmitQueue[0]); } @@ -1546,15 +1714,17 @@ protected override void EndProcessing() // Check for a leftover match that was still tracking context. _globalContextTracker.TrackEOF(); if (!_doneProcessing) + { FlushTrackerQueue(_globalContextTracker); + } } - private bool doMatch(string operandString, out MatchInfo matchResult) + private bool DoMatch(string operandString, out MatchInfo matchResult) { - return doMatchWorker(operandString, null, out matchResult); + return DoMatchWorker(operandString, null, out matchResult); } - private bool doMatch(object operand, out MatchInfo matchResult, out string operandString) + private bool DoMatch(object operand, out MatchInfo matchResult, out string operandString) { MatchInfo matchInfo = operand as MatchInfo; if (matchInfo != null) @@ -1581,28 +1751,39 @@ private bool doMatch(object operand, out MatchInfo matchResult, out string opera operandString = (string)LanguagePrimitives.ConvertTo(operand, typeof(string), CultureInfo.InvariantCulture); } - return doMatchWorker(operandString, matchInfo, out matchResult); + return DoMatchWorker(operandString, matchInfo, out matchResult); } /// /// Check the operand and see if it matches, if this.quiet is not set, then - /// return a partially populated MatchInfo object with Line, Pattern, IgnoreCase - /// set. + /// return a partially populated MatchInfo object with Line, Pattern, IgnoreCase set. /// - /// - /// the match info object - this will be - /// null if this.quiet is set. - /// the result of converting operand to - /// a string. - /// true if the input object matched - private bool doMatchWorker(string operandString, MatchInfo matchInfo, out MatchInfo matchResult) + /// The result of converting operand to a string. + /// The input object in filter mode. + /// The match info object - this will be null if this.quiet is set. + /// True if the input object matched. + private bool DoMatchWorker(string operandString, MatchInfo matchInfo, out MatchInfo matchResult) { bool gotMatch = false; Match[] matches = null; int patternIndex = 0; matchResult = null; - if (!_simpleMatch) + List indexes = null; + List lengths = null; + + bool shouldEmphasize = !NoEmphasis && Host.UI.SupportsVirtualTerminal; + + // If Emphasize is set and VT is supported, + // the lengths and starting indexes of regex matches + // need to be passed in to the matchInfo object. + if (shouldEmphasize) + { + indexes = new List(); + lengths = new List(); + } + + if (!SimpleMatch) { while (patternIndex < Pattern.Length) { @@ -1611,13 +1792,23 @@ private bool doMatchWorker(string operandString, MatchInfo matchInfo, out MatchI // Only honor allMatches if notMatch is not set, // since it's a fairly expensive operation and // notMatch takes precedent over allMatch. - if (_allMatches && !_notMatch) + if (AllMatches && !NotMatch) { MatchCollection mc = r.Matches(operandString); if (mc.Count > 0) { matches = new Match[mc.Count]; ((ICollection)mc).CopyTo(matches, 0); + + if (shouldEmphasize) + { + foreach (Match match in matches) + { + indexes.Add(match.Index); + lengths.Add(match.Length); + } + } + gotMatch = true; } } @@ -1628,26 +1819,39 @@ private bool doMatchWorker(string operandString, MatchInfo matchInfo, out MatchI if (match.Success) { + if (shouldEmphasize) + { + indexes.Add(match.Index); + lengths.Add(match.Length); + } + matches = new Match[] { match }; } } if (gotMatch) + { break; + } patternIndex++; } } else { - StringComparison compareOption = _caseSensitive ? - StringComparison.CurrentCulture : StringComparison.CurrentCultureIgnoreCase; while (patternIndex < Pattern.Length) { string pat = Pattern[patternIndex]; - if (operandString.IndexOf(pat, compareOption) >= 0) + int index = _cultureInfoIndexOf(operandString, pat, 0, operandString.Length, _compareOptions); + if (index >= 0) { + if (shouldEmphasize) + { + indexes.Add(index); + lengths.Add(pat.Length); + } + gotMatch = true; break; } @@ -1656,9 +1860,10 @@ private bool doMatchWorker(string operandString, MatchInfo matchInfo, out MatchI } } - if (_notMatch) + if (NotMatch) { gotMatch = !gotMatch; + // If notMatch was specified with multiple // patterns, then *none* of the patterns // matched and any pattern could be picked @@ -1682,8 +1887,8 @@ private bool doMatchWorker(string operandString, MatchInfo matchInfo, out MatchI if (matchInfo.Context != null) { matchResult = matchInfo.Clone(); - matchResult.Context.DisplayPreContext = new string[] { }; - matchResult.Context.DisplayPostContext = new string[] { }; + matchResult.Context.DisplayPreContext = Array.Empty(); + matchResult.Context.DisplayPostContext = Array.Empty(); } else { @@ -1695,8 +1900,10 @@ private bool doMatchWorker(string operandString, MatchInfo matchInfo, out MatchI } // otherwise construct and populate a new MatchInfo object - matchResult = new MatchInfo(); - matchResult.IgnoreCase = !_caseSensitive; + matchResult = shouldEmphasize + ? new MatchInfo(indexes, lengths) + : new MatchInfo(); + matchResult.IgnoreCase = !CaseSensitive; matchResult.Line = operandString; matchResult.Pattern = Pattern[patternIndex]; @@ -1707,27 +1914,32 @@ private bool doMatchWorker(string operandString, MatchInfo matchInfo, out MatchI // Matches should be an empty list, rather than null, // in the cases of notMatch and simpleMatch. - matchResult.Matches = matches ?? new Match[] { }; + matchResult.Matches = matches ?? Array.Empty(); return true; } + return false; - } // end doMatch + } + /// /// Get a list or resolved file paths. + /// + /// The filePaths to resolve. + /// True if the wildcard resolution should not be attempted. + /// The resolved (absolute) paths. private List ResolveFilePaths(string[] filePaths, bool isLiteralPath) { - ProviderInfo provider; - List allPaths = new List(); + List allPaths = new(); foreach (string path in filePaths) { Collection resolvedPaths; + ProviderInfo provider; if (isLiteralPath) { resolvedPaths = new Collection(); - PSDriveInfo drive; - string resolvedPath = SessionState.Path.GetUnresolvedProviderPathFromPSPath(path, out provider, out drive); + string resolvedPath = SessionState.Path.GetUnresolvedProviderPathFromPSPath(path, out provider, out _); resolvedPaths.Add(resolvedPath); } else @@ -1735,12 +1947,13 @@ private List ResolveFilePaths(string[] filePaths, bool isLiteralPath) resolvedPaths = GetResolvedProviderPathFromPSPath(path, out provider); } - if (!provider.NameEquals(((PSCmdlet)this).Context.ProviderNames.FileSystem)) + if (!provider.NameEquals(base.Context.ProviderNames.FileSystem)) { // "The current provider ({0}) cannot open a file" WriteError(BuildErrorRecord(MatchStringStrings.FileOpenError, provider.FullName, "ProcessingFile", null)); continue; } + allPaths.AddRange(resolvedPaths); } @@ -1760,7 +1973,7 @@ private static ErrorRecord BuildErrorRecord(string messageId, string arg0, strin private static ErrorRecord BuildErrorRecord(string messageId, object[] arguments, string errorId, Exception innerException) { string fmtedMsg = StringUtil.Format(messageId, arguments); - ArgumentException e = new ArgumentException(fmtedMsg, innerException); + ArgumentException e = new(fmtedMsg, innerException); return new ErrorRecord(e, errorId, ErrorCategory.InvalidArgument, null); } @@ -1773,21 +1986,21 @@ private void WarnFilterContext() /// /// Magic class that works around the limitations on ToString() for FileInfo. /// - private class FileinfoToStringAttribute : ArgumentTransformationAttribute + private sealed class FileinfoToStringAttribute : ArgumentTransformationAttribute { public override object Transform(EngineIntrinsics engineIntrinsics, object inputData) { object result = inputData; - PSObject mso = result as PSObject; - if (mso != null) + if (result is PSObject mso) + { result = mso.BaseObject; + } - IList argList = result as IList; FileInfo fileInfo; // Handle an array of elements... - if (argList != null) + if (result is IList argList) { object[] resultList = new object[argList.Count]; @@ -1797,21 +2010,23 @@ public override object Transform(EngineIntrinsics engineIntrinsics, object input mso = element as PSObject; if (mso != null) + { element = mso.BaseObject; + } fileInfo = element as FileInfo; - - if (fileInfo != null) - resultList[i] = fileInfo.FullName; - else resultList[i] = element; + resultList[i] = fileInfo?.FullName ?? element; } + return resultList; } // Handle the singleton case... fileInfo = result as FileInfo; if (fileInfo != null) + { return fileInfo.FullName; + } return inputData; } @@ -1822,16 +2037,16 @@ public override object Transform(EngineIntrinsics engineIntrinsics, object input /// That is - it's on the include list if there is one and not on /// the exclude list if there was one of those. /// - /// + /// The filename to test. /// True if the filename is acceptable. - private bool meetsIncludeExcludeCriteria(string filename) + private bool MeetsIncludeExcludeCriteria(string filename) { bool ok = false; // see if the file is on the include list... - if (this.include != null) + if (_include != null) { - foreach (WildcardPattern patternItem in this.include) + foreach (WildcardPattern patternItem in _include) { if (patternItem.IsMatch(filename)) { @@ -1846,12 +2061,14 @@ private bool meetsIncludeExcludeCriteria(string filename) } if (!ok) + { return false; + } // now see if it's on the exclude list... - if (this.exclude != null) + if (_exclude != null) { - foreach (WildcardPattern patternItem in this.exclude) + foreach (WildcardPattern patternItem in _exclude) { if (patternItem.IsMatch(filename)) { @@ -1863,6 +2080,26 @@ private bool meetsIncludeExcludeCriteria(string filename) return ok; } - } // end class SelectStringCommand -} + } + /// + /// Get list of valid culture names for ValidateSet attribute. + /// + public class ValidateMatchStringCultureNamesGenerator : IValidateSetValuesGenerator + { + string[] IValidateSetValuesGenerator.GetValidValues() + { + var cultures = CultureInfo.GetCultures(CultureTypes.AllCultures); + var result = new List(cultures.Length + 3); + result.Add(SelectStringCommand.OrdinalCultureName); + result.Add(SelectStringCommand.InvariantCultureName); + result.Add(SelectStringCommand.CurrentCultureName); + foreach (var cultureInfo in cultures) + { + result.Add(cultureInfo.Name); + } + + return result.ToArray(); + } + } +} diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Measure-Object.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Measure-Object.cs index 30cb887ce0f..1e741758270 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Measure-Object.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Measure-Object.cs @@ -1,6 +1,5 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. using System; using System.Collections.Generic; @@ -8,78 +7,69 @@ using System.Globalization; using System.Management.Automation; using System.Management.Automation.Internal; -using Microsoft.PowerShell.Commands.Internal.Format; namespace Microsoft.PowerShell.Commands { /// - /// Class output by Measure-Object + /// Class output by Measure-Object. /// public abstract class MeasureInfo { /// - /// - /// property name - /// + /// Property name. /// - public string Property { get; set; } = null; + public string Property { get; set; } } /// - /// Class output by Measure-Object + /// Class output by Measure-Object. /// public sealed class GenericMeasureInfo : MeasureInfo { /// - /// default ctor + /// Initializes a new instance of the class. /// public GenericMeasureInfo() { - Average = Sum = Maximum = Minimum = null; + Average = Sum = Maximum = Minimum = StandardDeviation = null; } /// - /// - /// Keeping track of number of objects with a certain property - /// + /// Keeping track of number of objects with a certain property. /// public int Count { get; set; } /// - /// - /// The average of property values - /// + /// The average of property values. /// public double? Average { get; set; } /// - /// - /// The sum of property values - /// + /// The sum of property values. /// public double? Sum { get; set; } /// - /// - /// The max of property values - /// + /// The max of property values. /// public double? Maximum { get; set; } /// - /// - /// The min of property values - /// + /// The min of property values. /// public double? Minimum { get; set; } + + /// + /// The Standard Deviation of property values. + /// + public double? StandardDeviation { get; set; } } /// /// Class output by Measure-Object. /// /// - /// This class is created for fixing "Measure-Object -MAX -MIN should work with ANYTHING that supports CompareTo" - /// bug (Win8:343911). + /// This class is created to make 'Measure-Object -MAX -MIN' work with ANYTHING that supports 'CompareTo'. /// GenericMeasureInfo class is shipped with PowerShell V2. Fixing this bug requires, changing the type of /// Maximum and Minimum properties which would be a breaking change. Hence created a new class to not /// have an appcompat issues with PS V2. @@ -87,58 +77,54 @@ public GenericMeasureInfo() public sealed class GenericObjectMeasureInfo : MeasureInfo { /// - /// default ctor + /// Initializes a new instance of the class. + /// Default ctor. /// public GenericObjectMeasureInfo() { - Average = Sum = null; + Average = Sum = StandardDeviation = null; Maximum = Minimum = null; } /// - /// - /// Keeping track of number of objects with a certain property - /// + /// Keeping track of number of objects with a certain property. /// public int Count { get; set; } /// - /// - /// The average of property values - /// + /// The average of property values. /// public double? Average { get; set; } /// - /// - /// The sum of property values - /// + /// The sum of property values. /// public double? Sum { get; set; } /// - /// - /// The max of property values - /// + /// The max of property values. /// public object Maximum { get; set; } /// - /// - /// The min of property values - /// + /// The min of property values. /// public object Minimum { get; set; } - } + /// + /// The Standard Deviation of property values. + /// + public double? StandardDeviation { get; set; } + } /// - /// Class output by Measure-Object + /// Class output by Measure-Object. /// public sealed class TextMeasureInfo : MeasureInfo { /// - /// default ctor + /// Initializes a new instance of the class. + /// Default ctor. /// public TextMeasureInfo() { @@ -146,45 +132,40 @@ public TextMeasureInfo() } /// - /// - /// Keeping track of number of objects with a certain property - /// + /// Keeping track of number of objects with a certain property. /// public int? Lines { get; set; } /// - /// - /// The average of property values - /// + /// The average of property values. /// public int? Words { get; set; } /// - /// - /// The sum of property values - /// + /// The sum of property values. /// public int? Characters { get; set; } } /// - /// measure object cmdlet + /// Measure object cmdlet. /// [Cmdlet(VerbsDiagnostic.Measure, "Object", DefaultParameterSetName = GenericParameterSet, - HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113349", RemotingCapability = RemotingCapability.None)] + HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2096617", RemotingCapability = RemotingCapability.None)] [OutputType(typeof(GenericMeasureInfo), typeof(TextMeasureInfo), typeof(GenericObjectMeasureInfo))] public sealed class MeasureObjectCommand : PSCmdlet { /// - /// Dictionary to be used by Measure-Object implementation + /// Dictionary to be used by Measure-Object implementation. /// Keys are strings. Keys are compared with OrdinalIgnoreCase. /// - /// Value type. - private class MeasureObjectDictionary : Dictionary - where V : new() + /// Value type. + private sealed class MeasureObjectDictionary : Dictionary + where TValue : new() { /// - /// default ctor + /// Initializes a new instance of the class. + /// Default ctor. /// internal MeasureObjectDictionary() : base(StringComparer.OrdinalIgnoreCase) { @@ -196,16 +177,16 @@ internal MeasureObjectDictionary() : base(StringComparer.OrdinalIgnoreCase) /// the key with a new value created via the value type's /// default constructor. /// - /// The key to look up + /// The key to look up. /// /// The existing value, or a newly-created value. /// - public V EnsureEntry(string key) + public TValue EnsureEntry(string key) { - V val; + TValue val; if (!TryGetValue(key, out val)) { - val = new V(); + val = new TValue(); this[key] = val; } @@ -218,15 +199,16 @@ public V EnsureEntry(string key) /// to maintain two sets of MeasureInfo and constantly checking /// what mode we're in. /// - [SuppressMessage("Microsoft.Performance", "CA1812:AvoidUninstantiatedInternalClasses")] - private class Statistics + private sealed class Statistics { // Common properties internal int count = 0; // Generic/Numeric statistics internal double sum = 0.0; + internal double sumPrevious = 0.0; + internal double variance = 0.0; internal object max = null; internal object min = null; @@ -237,7 +219,8 @@ private class Statistics } /// - /// default constructor + /// Initializes a new instance of the class. + /// Default constructor. /// public MeasureObjectCommand() : base() @@ -249,24 +232,43 @@ public MeasureObjectCommand() #region Common parameters in both sets /// - /// incoming object + /// Incoming object. /// /// [Parameter(ValueFromPipeline = true)] - public PSObject InputObject { set; get; } = AutomationNull.Value; + public PSObject InputObject { get; set; } = AutomationNull.Value; /// - /// Properties to be examined + /// Properties to be examined. /// /// [ValidateNotNullOrEmpty] [Parameter(Position = 0)] - public string[] Property { get; set; } = null; + public PSPropertyExpression[] Property { get; set; } #endregion Common parameters in both sets /// - /// Set to true is Sum is to be returned + /// Set to true if Standard Deviation is to be returned. + /// + [Parameter(ParameterSetName = GenericParameterSet)] + public SwitchParameter StandardDeviation + { + get + { + return _measureStandardDeviation; + } + + set + { + _measureStandardDeviation = value; + } + } + + private bool _measureStandardDeviation; + + /// + /// Set to true is Sum is to be returned. /// /// [Parameter(ParameterSetName = GenericParameterSet)] @@ -276,15 +278,37 @@ public SwitchParameter Sum { return _measureSum; } + set { _measureSum = value; } } + private bool _measureSum; /// - /// Set to true is Average is to be returned + /// Gets or sets the value indicating if all statistics should be returned. + /// + /// + [Parameter(ParameterSetName = GenericParameterSet)] + public SwitchParameter AllStats + { + get + { + return _allStats; + } + + set + { + _allStats = value; + } + } + + private bool _allStats; + + /// + /// Set to true is Average is to be returned. /// /// [Parameter(ParameterSetName = GenericParameterSet)] @@ -294,15 +318,17 @@ public SwitchParameter Average { return _measureAverage; } + set { _measureAverage = value; } } + private bool _measureAverage; /// - /// Set to true is Max is to be returned + /// Set to true is Max is to be returned. /// /// [Parameter(ParameterSetName = GenericParameterSet)] @@ -312,15 +338,17 @@ public SwitchParameter Maximum { return _measureMax; } + set { _measureMax = value; } } + private bool _measureMax; /// - /// Set to true is Min is to be returned + /// Set to true is Min is to be returned. /// /// [Parameter(ParameterSetName = GenericParameterSet)] @@ -330,16 +358,17 @@ public SwitchParameter Minimum { return _measureMin; } + set { _measureMin = value; } } + private bool _measureMin; #region TextMeasure ParameterSet /// - /// /// /// [Parameter(ParameterSetName = TextParameterSet)] @@ -349,15 +378,16 @@ public SwitchParameter Line { return _measureLines; } + set { _measureLines = value; } } + private bool _measureLines = false; /// - /// /// /// [Parameter(ParameterSetName = TextParameterSet)] @@ -367,15 +397,16 @@ public SwitchParameter Word { return _measureWords; } + set { _measureWords = value; } } + private bool _measureWords = false; /// - /// /// /// [Parameter(ParameterSetName = TextParameterSet)] @@ -385,15 +416,16 @@ public SwitchParameter Character { return _measureCharacters; } + set { _measureCharacters = value; } } + private bool _measureCharacters = false; /// - /// /// /// [Parameter(ParameterSetName = TextParameterSet)] @@ -403,17 +435,18 @@ public SwitchParameter IgnoreWhiteSpace { return _ignoreWhiteSpace; } + set { _ignoreWhiteSpace = value; } } + private bool _ignoreWhiteSpace; #endregion TextMeasure ParameterSet #endregion Command Line Switches - /// /// Which parameter set the Cmdlet is in. /// @@ -421,10 +454,25 @@ private bool IsMeasuringGeneric { get { - return String.Compare(ParameterSetName, GenericParameterSet, StringComparison.Ordinal) == 0; + return string.Equals(ParameterSetName, GenericParameterSet, StringComparison.Ordinal); } } + /// + /// Does the begin part of the cmdlet. + /// + protected override void BeginProcessing() + { + // Sets all other generic parameters to true to get all statistics. + if (_allStats) + { + _measureSum = _measureStandardDeviation = _measureAverage = _measureMax = _measureMin = true; + } + + // finally call the base class. + base.BeginProcessing(); + } + /// /// Collect data about each record that comes in. /// Side effects: Updates totalRecordCount. @@ -448,21 +496,20 @@ protected override void ProcessRecord() /// Analyze an object on a property-by-property basis instead /// of as a simple value. /// Side effects: Updates statistics. - /// The object to analyze. /// + /// The object to analyze. private void AnalyzeObjectProperties(PSObject inObj) { // Keep track of which properties are counted for an // input object so that repeated properties won't be // counted twice. - MeasureObjectDictionary countedProperties = new MeasureObjectDictionary(); + MeasureObjectDictionary countedProperties = new(); // First iterate over the user-specified list of // properties... - foreach (string p in Property) + foreach (var expression in Property) { - MshExpression expression = new MshExpression(p); - List resolvedNames = expression.ResolveNames(inObj); + List resolvedNames = expression.ResolveNames(inObj); if (resolvedNames == null || resolvedNames.Count == 0) { // Insert a blank entry so we can track @@ -479,7 +526,7 @@ private void AnalyzeObjectProperties(PSObject inObj) // Each property value can potentially refer // to multiple properties via globbing. Iterate over // the actual property names. - foreach (MshExpression resolvedName in resolvedNames) + foreach (PSPropertyExpression resolvedName in resolvedNames) { string propertyName = resolvedName.ToString(); // skip duplicated properties @@ -488,7 +535,7 @@ private void AnalyzeObjectProperties(PSObject inObj) continue; } - List tempExprRes = resolvedName.GetValues(inObj); + List tempExprRes = resolvedName.GetValues(inObj); if (tempExprRes == null || tempExprRes.Count == 0) { // Shouldn't happen - would somehow mean @@ -509,13 +556,12 @@ private void AnalyzeObjectProperties(PSObject inObj) /// /// Analyze a value for generic/text statistics. /// Side effects: Updates statistics. May set nonNumericError. + /// /// The property this value corresponds to. /// The value to analyze. - /// private void AnalyzeValue(string propertyName, object objValue) { - if (propertyName == null) - propertyName = thisObject; + propertyName ??= thisObject; Statistics stat = _statistics.EnsureEntry(propertyName); @@ -524,17 +570,17 @@ private void AnalyzeValue(string propertyName, object objValue) if (_measureCharacters || _measureWords || _measureLines) { - string strValue = (objValue == null) ? "" : objValue.ToString(); + string strValue = (objValue == null) ? string.Empty : objValue.ToString(); AnalyzeString(strValue, stat); } - if (_measureAverage || _measureSum) + if (_measureAverage || _measureSum || _measureStandardDeviation) { double numValue = 0.0; if (!LanguagePrimitives.TryConvertTo(objValue, out numValue)) { _nonNumericError = true; - ErrorRecord errorRecord = new ErrorRecord( + ErrorRecord errorRecord = new( PSTraceSource.NewInvalidOperationException(MeasureObjectStrings.NonNumericInputObject, objValue), "NonNumericInputObject", ErrorCategory.InvalidType, @@ -546,7 +592,7 @@ private void AnalyzeValue(string propertyName, object objValue) AnalyzeNumber(numValue, stat); } - // Win8:343911 Measure-Object -MAX -MIN should work with ANYTHING that supports CompareTo + // Measure-Object -MAX -MIN should work with ANYTHING that supports CompareTo if (_measureMin) { stat.min = Compare(objValue, stat.min, true); @@ -572,11 +618,10 @@ private void AnalyzeValue(string propertyName, object objValue) /// If true is passed in then the minimum of the two values would be returned. /// If false is passed in then maximum of the two values will be returned. /// - private object Compare(object objValue, object statMinOrMaxValue, bool isMin) + private static object Compare(object objValue, object statMinOrMaxValue, bool isMin) { object currentValue = objValue; object statValue = statMinOrMaxValue; - int factor = isMin ? 1 : -1; double temp; currentValue = ((objValue != null) && LanguagePrimitives.TryConvertTo(objValue, out temp)) ? temp : currentValue; @@ -588,36 +633,40 @@ private object Compare(object objValue, object statMinOrMaxValue, bool isMin) statValue = PSObject.AsPSObject(statValue).ToString(); } - if ((statValue == null) || - ((LanguagePrimitives.Compare(statValue, currentValue, false, CultureInfo.CurrentCulture) * factor) > 0)) + if (statValue == null) { return objValue; } - return statMinOrMaxValue; + int comparisonResult = LanguagePrimitives.Compare(statValue, currentValue, ignoreCase: false, CultureInfo.CurrentCulture); + return (isMin ? comparisonResult : -comparisonResult) > 0 + ? objValue + : statMinOrMaxValue; } /// - /// Class contains util static functions + /// Class contains util static functions. /// private static class TextCountUtilities { /// - /// count chars in inStr + /// Count chars in inStr. /// - /// string whose chars are counted - /// true to discount white space - /// number of chars in inStr + /// String whose chars are counted. + /// True to discount white space. + /// Number of chars in inStr. internal static int CountChar(string inStr, bool ignoreWhiteSpace) { - if (String.IsNullOrEmpty(inStr)) + if (string.IsNullOrEmpty(inStr)) { return 0; } + if (!ignoreWhiteSpace) { return inStr.Length; } + int len = 0; foreach (char c in inStr) { @@ -626,20 +675,22 @@ internal static int CountChar(string inStr, bool ignoreWhiteSpace) len++; } } + return len; } /// - /// count words in inStr + /// Count words in inStr. /// - /// string whose words are counted - /// number of words in inStr + /// String whose words are counted. + /// Number of words in inStr. internal static int CountWord(string inStr) { - if (String.IsNullOrEmpty(inStr)) + if (string.IsNullOrEmpty(inStr)) { return 0; } + int wordCount = 0; bool wasAWhiteSpace = true; foreach (char c in inStr) @@ -654,23 +705,26 @@ internal static int CountWord(string inStr) { wordCount++; } + wasAWhiteSpace = false; } } + return wordCount; } /// - /// count lines in inStr + /// Count lines in inStr. /// - /// string whose lines are counted - /// number of lines in inStr + /// String whose lines are counted. + /// Number of lines in inStr. internal static int CountLine(string inStr) { - if (String.IsNullOrEmpty(inStr)) + if (string.IsNullOrEmpty(inStr)) { return 0; } + int numberOfLines = 0; foreach (char c in inStr) { @@ -685,15 +739,16 @@ internal static int CountLine(string inStr) { numberOfLines++; } + return numberOfLines; } } /// /// Update text statistics. + /// /// The text to analyze. /// The Statistics object to update. - /// private void AnalyzeString(string strValue, Statistics stat) { if (_measureCharacters) @@ -706,27 +761,39 @@ private void AnalyzeString(string strValue, Statistics stat) /// /// Update number statistics. + /// /// The number to analyze. /// The Statistics object to update. - /// private void AnalyzeNumber(double numValue, Statistics stat) { - if (_measureSum || _measureAverage) + if (_measureSum || _measureAverage || _measureStandardDeviation) + { + stat.sumPrevious = stat.sum; stat.sum += numValue; + } + + if (_measureStandardDeviation && stat.count > 1) + { + // Based off of iterative method of calculating variance on + // https://en.wikipedia.org/wiki/Algorithms_for_calculating_variance#Online_algorithm + double avgPrevious = stat.sumPrevious / (stat.count - 1); + stat.variance *= (stat.count - 2.0) / (stat.count - 1); + stat.variance += (numValue - avgPrevious) * (numValue - avgPrevious) / stat.count; + } } /// - /// WriteError when a property is not found + /// WriteError when a property is not found. /// /// The missing property. /// The error ID to write. private void WritePropertyNotFoundError(string propertyName, string errorId) { Diagnostics.Assert(Property != null, "no property and no InputObject should have been addressed"); - ErrorRecord errorRecord = new ErrorRecord( - PSTraceSource.NewArgumentException("Property"), + ErrorRecord errorRecord = new( + PSTraceSource.NewArgumentException(propertyName), errorId, - ErrorCategory.InvalidArgument, + ErrorCategory.ObjectNotFound, null); errorRecord.ErrorDetails = new ErrorDetails( this, "MeasureObjectStrings", "PropertyNotFound", propertyName); @@ -752,9 +819,12 @@ protected override void EndProcessing() Statistics stat = _statistics[propertyName]; if (stat.count == 0 && Property != null) { - // Why are there two different ids for this error? - string errorId = (IsMeasuringGeneric) ? "GenericMeasurePropertyNotFound" : "TextMeasurePropertyNotFound"; - WritePropertyNotFoundError(propertyName, errorId); + if (Context.IsStrictVersion(2)) + { + string errorId = (IsMeasuringGeneric) ? "GenericMeasurePropertyNotFound" : "TextMeasurePropertyNotFound"; + WritePropertyNotFoundError(propertyName, errorId); + } + continue; } @@ -785,14 +855,15 @@ protected override void EndProcessing() /// /// Create a MeasureInfo object for generic stats. - /// The statistics to use. - /// A new GenericMeasureInfo object. /// + /// The statistics to use. /// + /// A new GenericMeasureInfo object. private MeasureInfo CreateGenericMeasureInfo(Statistics stat, bool shouldUseGenericMeasureInfo) { double? sum = null; double? average = null; + double? StandardDeviation = null; object max = null; object min = null; @@ -800,8 +871,14 @@ private MeasureInfo CreateGenericMeasureInfo(Statistics stat, bool shouldUseGene { if (_measureSum) sum = stat.sum; + if (_measureAverage && stat.count > 0) average = stat.sum / stat.count; + + if (_measureStandardDeviation) + { + StandardDeviation = Math.Sqrt(stat.variance); + } } if (_measureMax) @@ -834,15 +911,17 @@ private MeasureInfo CreateGenericMeasureInfo(Statistics stat, bool shouldUseGene if (shouldUseGenericMeasureInfo) { - GenericMeasureInfo gmi = new GenericMeasureInfo(); + GenericMeasureInfo gmi = new(); gmi.Count = stat.count; gmi.Sum = sum; gmi.Average = average; - if (null != max) + gmi.StandardDeviation = StandardDeviation; + if (max != null) { gmi.Maximum = (double)max; } - if (null != min) + + if (min != null) { gmi.Minimum = (double)min; } @@ -851,7 +930,7 @@ private MeasureInfo CreateGenericMeasureInfo(Statistics stat, bool shouldUseGene } else { - GenericObjectMeasureInfo gomi = new GenericObjectMeasureInfo(); + GenericObjectMeasureInfo gomi = new(); gomi.Count = stat.count; gomi.Sum = sum; gomi.Average = average; @@ -864,12 +943,12 @@ private MeasureInfo CreateGenericMeasureInfo(Statistics stat, bool shouldUseGene /// /// Create a MeasureInfo object for text stats. + /// /// The statistics to use. /// A new TextMeasureInfo object. - /// private TextMeasureInfo CreateTextMeasureInfo(Statistics stat) { - TextMeasureInfo tmi = new TextMeasureInfo(); + TextMeasureInfo tmi = new(); if (_measureCharacters) tmi.Characters = stat.characters; @@ -882,15 +961,14 @@ private TextMeasureInfo CreateTextMeasureInfo(Statistics stat) } /// - /// The observed statistics keyed by property name. If - /// Property is not set, then the key used will be the - /// value of thisObject. + /// The observed statistics keyed by property name. + /// If Property is not set, then the key used will be the value of thisObject. /// - private MeasureObjectDictionary _statistics = new MeasureObjectDictionary(); + private readonly MeasureObjectDictionary _statistics = new(); /// /// Whether or not a numeric conversion error occurred. - /// If true, then average/sum will not be output. + /// If true, then average/sum/standard deviation will not be output. /// private bool _nonNumericError = false; @@ -915,4 +993,3 @@ private TextMeasureInfo CreateTextMeasureInfo(Statistics stat) private const string thisObject = "$_"; } } - diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/New-Object.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/New-Object.cs new file mode 100644 index 00000000000..0ea312f2963 --- /dev/null +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/New-Object.cs @@ -0,0 +1,551 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#region Using directives + +using System; +using System.Collections; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Management.Automation; +using System.Management.Automation.Internal; +using System.Management.Automation.Language; +using System.Management.Automation.Security; +using System.Reflection; +using System.Runtime.InteropServices; +#if !UNIX +using System.Threading; +#endif + +using Dbg = System.Management.Automation.Diagnostics; + +#endregion + +namespace Microsoft.PowerShell.Commands +{ + /// Create a new .net object + [Cmdlet(VerbsCommon.New, "Object", DefaultParameterSetName = netSetName, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2096620")] + public sealed class NewObjectCommand : PSCmdlet + { + #region parameters + + /// the number + [Parameter(ParameterSetName = netSetName, Mandatory = true, Position = 0)] + [ValidateTrustedData] + public string TypeName { get; set; } + +#if !UNIX + private Guid _comObjectClsId = Guid.Empty; + /// + /// The ProgID of the Com object. + /// + [Parameter(ParameterSetName = "Com", Mandatory = true, Position = 0)] + [ValidateTrustedData] + public string ComObject { get; set; } +#endif + + /// + /// The parameters for the constructor. + /// + /// + [Parameter(ParameterSetName = netSetName, Mandatory = false, Position = 1)] + [ValidateTrustedData] + [Alias("Args")] + public object[] ArgumentList { get; set; } + + /// + /// True if we should have an error when Com objects will use an interop assembly. + /// + [Parameter(ParameterSetName = "Com")] + public SwitchParameter Strict { get; set; } + + // Updated from Hashtable to IDictionary to support the work around ordered hashtables. + /// + /// Gets the properties to be set. + /// + [Parameter] + [ValidateTrustedData] + [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")] + public IDictionary Property { get; set; } + + #endregion parameters + + #region private + private object CallConstructor(Type type, ConstructorInfo[] constructors, object[] args) + { + object result = null; + try + { + result = DotNetAdapter.ConstructorInvokeDotNet(type, constructors, args); + } + catch (MethodException e) + { + ThrowTerminatingError( + new ErrorRecord( + e, + "ConstructorInvokedThrowException", + ErrorCategory.InvalidOperation, null)); + } + // let other exceptions propagate + return result; + } + + private void CreateMemberNotFoundError(PSObject pso, DictionaryEntry property, Type resultType) + { + string message = StringUtil.Format(NewObjectStrings.MemberNotFound, null, property.Key.ToString(), ParameterSet2ResourceString(ParameterSetName)); + + ThrowTerminatingError( + new ErrorRecord( + new InvalidOperationException(message), + "InvalidOperationException", + ErrorCategory.InvalidOperation, + null)); + } + + private void CreateMemberSetValueError(SetValueException e) + { + Exception ex = new(StringUtil.Format(NewObjectStrings.InvalidValue, e)); + ThrowTerminatingError( + new ErrorRecord(ex, "SetValueException", ErrorCategory.InvalidData, null)); + } + + private static string ParameterSet2ResourceString(string parameterSet) + { + if (parameterSet.Equals(netSetName, StringComparison.OrdinalIgnoreCase)) + { + return ".NET"; + } + else if (parameterSet.Equals("Com", StringComparison.OrdinalIgnoreCase)) + { + return "COM"; + } + else + { + Dbg.Assert(false, "Should never get here - unknown parameter set"); + return parameterSet; + } + } + + #endregion private + + #region Overrides + /// Create the object + protected override void BeginProcessing() + { + Type type = null; + PSArgumentException mshArgE = null; + + if (string.Equals(ParameterSetName, netSetName, StringComparison.Ordinal)) + { + object _newObject = null; + try + { + type = LanguagePrimitives.ConvertTo(TypeName, typeof(Type), CultureInfo.InvariantCulture) as Type; + } + catch (Exception e) + { + // these complications in Exception handling are aim to make error messages better. + if (e is InvalidCastException || e is ArgumentException) + { + if (e.InnerException != null && e.InnerException is TypeResolver.AmbiguousTypeException) + { + ThrowTerminatingError( + new ErrorRecord( + e, + "AmbiguousTypeReference", + ErrorCategory.InvalidType, + targetObject: null)); + } + + mshArgE = PSTraceSource.NewArgumentException( + "TypeName", + NewObjectStrings.TypeNotFound, + TypeName); + + ThrowTerminatingError( + new ErrorRecord( + mshArgE, + "TypeNotFound", + ErrorCategory.InvalidType, + targetObject: null)); + } + + throw; + } + + Diagnostics.Assert(type != null, "LanguagePrimitives.TryConvertTo failed but returned true"); + + if (type.IsByRefLike) + { + ThrowTerminatingError( + new ErrorRecord( + PSTraceSource.NewInvalidOperationException( + NewObjectStrings.CannotInstantiateBoxedByRefLikeType, + type), + nameof(NewObjectStrings.CannotInstantiateBoxedByRefLikeType), + ErrorCategory.InvalidOperation, + targetObject: null)); + } + + switch (Context.LanguageMode) + { + case PSLanguageMode.ConstrainedLanguage: + if (!CoreTypes.Contains(type)) + { + if (SystemPolicy.GetSystemLockdownPolicy() != SystemEnforcementMode.Audit) + { + ThrowTerminatingError( + new ErrorRecord( + new PSNotSupportedException(NewObjectStrings.CannotCreateTypeConstrainedLanguage), + "CannotCreateTypeConstrainedLanguage", + ErrorCategory.PermissionDenied, + targetObject: null)); + } + + SystemPolicy.LogWDACAuditMessage( + context: Context, + title: NewObjectStrings.TypeWDACLogTitle, + message: StringUtil.Format(NewObjectStrings.TypeWDACLogMessage, type.FullName), + fqid: "NewObjectCmdletCannotCreateType", + dropIntoDebugger: true); + } + break; + + case PSLanguageMode.NoLanguage: + case PSLanguageMode.RestrictedLanguage: + if (SystemPolicy.GetSystemLockdownPolicy() == SystemEnforcementMode.Enforce + && !CoreTypes.Contains(type)) + { + ThrowTerminatingError( + new ErrorRecord( + new PSNotSupportedException( + string.Format(NewObjectStrings.CannotCreateTypeLanguageMode, Context.LanguageMode.ToString())), + nameof(NewObjectStrings.CannotCreateTypeLanguageMode), + ErrorCategory.PermissionDenied, + targetObject: null)); + } + break; + } + + // WinRT does not support creating instances of attribute & delegate WinRT types. + if (WinRTHelper.IsWinRTType(type) && ((typeof(System.Attribute)).IsAssignableFrom(type) || (typeof(System.Delegate)).IsAssignableFrom(type))) + { + ThrowTerminatingError(new ErrorRecord(new InvalidOperationException(NewObjectStrings.CannotInstantiateWinRTType), + "CannotInstantiateWinRTType", ErrorCategory.InvalidOperation, null)); + } + + if (ArgumentList == null || ArgumentList.Length == 0) + { + ConstructorInfo ci = type.GetConstructor(Type.EmptyTypes); + if (ci != null && ci.IsPublic) + { + _newObject = CallConstructor(type, new ConstructorInfo[] { ci }, Array.Empty()); + if (_newObject != null && Property != null) + { + // The method invocation is disabled for "Hashtable to Object conversion" (Win8:649519), but we need to keep it enabled for New-Object for compatibility to PSv2 + _newObject = LanguagePrimitives.SetObjectProperties(_newObject, Property, type, CreateMemberNotFoundError, CreateMemberSetValueError, enableMethodCall: true); + } + + WriteObject(_newObject); + return; + } + else if (type.IsValueType) + { + // This is for default parameterless struct ctor which is not returned by + // Type.GetConstructor(System.Type.EmptyTypes). + try + { + _newObject = Activator.CreateInstance(type); + if (_newObject != null && Property != null) + { + // Win8:649519 + _newObject = LanguagePrimitives.SetObjectProperties(_newObject, Property, type, CreateMemberNotFoundError, CreateMemberSetValueError, enableMethodCall: true); + } + } + catch (TargetInvocationException e) + { + ThrowTerminatingError( + new ErrorRecord( + e.InnerException ?? e, + "ConstructorCalledThrowException", + ErrorCategory.InvalidOperation, null)); + } + + WriteObject(_newObject); + return; + } + } + else + { + ConstructorInfo[] ctorInfos = type.GetConstructors(); + + if (ctorInfos.Length != 0) + { + _newObject = CallConstructor(type, ctorInfos, ArgumentList); + if (_newObject != null && Property != null) + { + // Win8:649519 + _newObject = LanguagePrimitives.SetObjectProperties(_newObject, Property, type, CreateMemberNotFoundError, CreateMemberSetValueError, enableMethodCall: true); + } + + WriteObject(_newObject); + return; + } + } + + mshArgE = PSTraceSource.NewArgumentException( + "TypeName", NewObjectStrings.CannotFindAppropriateCtor, TypeName); + ThrowTerminatingError( + new ErrorRecord( + mshArgE, + "CannotFindAppropriateCtor", + ErrorCategory.ObjectNotFound, null)); + } +#if !UNIX + else // Parameterset -Com + { + int result = NewObjectNativeMethods.CLSIDFromProgID(ComObject, out _comObjectClsId); + + // If we're in ConstrainedLanguage, do additional restrictions + if (Context.LanguageMode == PSLanguageMode.ConstrainedLanguage) + { + bool isAllowed = false; + + // If it's a system-wide lockdown, we may allow additional COM types + var systemLockdownPolicy = SystemPolicy.GetSystemLockdownPolicy(); + if (systemLockdownPolicy == SystemEnforcementMode.Enforce || systemLockdownPolicy == SystemEnforcementMode.Audit) + { + isAllowed = (result >= 0) && SystemPolicy.IsClassInApprovedList(_comObjectClsId); + } + + if (!isAllowed) + { + if (SystemPolicy.GetSystemLockdownPolicy() != SystemEnforcementMode.Audit) + { + ThrowTerminatingError( + new ErrorRecord( + new PSNotSupportedException(NewObjectStrings.CannotCreateTypeConstrainedLanguage), + "CannotCreateComTypeConstrainedLanguage", + ErrorCategory.PermissionDenied, + targetObject: null)); + return; + } + + SystemPolicy.LogWDACAuditMessage( + context: Context, + title: NewObjectStrings.ComWDACLogTitle, + message: StringUtil.Format(NewObjectStrings.ComWDACLogMessage, ComObject ?? string.Empty), + fqid: "NewObjectCmdletCannotCreateCOM", + dropIntoDebugger: true); + } + } + + object comObject = CreateComObject(); + string comObjectTypeName = comObject.GetType().FullName; + if (!comObjectTypeName.Equals("System.__ComObject")) + { + mshArgE = PSTraceSource.NewArgumentException( + "TypeName", NewObjectStrings.ComInteropLoaded, comObjectTypeName); + WriteVerbose(mshArgE.Message); + if (Strict) + { + WriteError(new ErrorRecord( + mshArgE, + "ComInteropLoaded", + ErrorCategory.InvalidArgument, comObject)); + } + } + + if (comObject != null && Property != null) + { + // Win8:649519 + comObject = LanguagePrimitives.SetObjectProperties(comObject, Property, type, CreateMemberNotFoundError, CreateMemberSetValueError, enableMethodCall: true); + } + + WriteObject(comObject); + } +#endif + } + + #endregion Overrides + +#if !UNIX + #region Com + + private object SafeCreateInstance(Type t) + { + object result = null; + try + { + result = Activator.CreateInstance(t); + } + // Does not catch InvalidComObjectException because ComObject is obtained from GetTypeFromProgID + catch (ArgumentException e) + { + ThrowTerminatingError( + new ErrorRecord( + e, + "CannotNewNonRuntimeType", + ErrorCategory.InvalidOperation, null)); + } + catch (NotSupportedException e) + { + ThrowTerminatingError( + new ErrorRecord( + e, + "CannotNewTypeBuilderTypedReferenceArgIteratorRuntimeArgumentHandle", + ErrorCategory.InvalidOperation, null)); + } + catch (MethodAccessException e) + { + ThrowTerminatingError( + new ErrorRecord( + e, + "CtorAccessDenied", + ErrorCategory.PermissionDenied, null)); + } + catch (MissingMethodException e) + { + ThrowTerminatingError( + new ErrorRecord( + e, + "NoPublicCtorMatch", + ErrorCategory.InvalidOperation, null)); + } + catch (MemberAccessException e) + { + ThrowTerminatingError( + new ErrorRecord( + e, + "CannotCreateAbstractClass", + ErrorCategory.InvalidOperation, null)); + } + catch (COMException e) + { + if (e.HResult == RPC_E_CHANGED_MODE) + { + throw; + } + + ThrowTerminatingError( + new ErrorRecord( + e, + "NoCOMClassIdentified", + ErrorCategory.ResourceUnavailable, null)); + } + + return result; + } + + private sealed class ComCreateInfo + { + public object objectCreated; + public bool success; + public Exception e; + } + + private ComCreateInfo createInfo; + + private void STAComCreateThreadProc(object createstruct) + { + ComCreateInfo info = (ComCreateInfo)createstruct; + try + { + Type type = Type.GetTypeFromCLSID(_comObjectClsId); + if (type == null) + { + PSArgumentException mshArgE = PSTraceSource.NewArgumentException( + "ComObject", + NewObjectStrings.CannotLoadComObjectType, + ComObject); + + info.e = mshArgE; + info.success = false; + return; + } + + info.objectCreated = SafeCreateInstance(type); + info.success = true; + } + catch (Exception e) + { + info.e = e; + info.success = false; + } + } + + private object CreateComObject() + { + try + { + Type type = Marshal.GetTypeFromCLSID(_comObjectClsId); + if (type == null) + { + PSArgumentException mshArgE = PSTraceSource.NewArgumentException( + "ComObject", + NewObjectStrings.CannotLoadComObjectType, + ComObject); + + ThrowTerminatingError( + new ErrorRecord( + mshArgE, + "CannotLoadComObjectType", + ErrorCategory.InvalidType, + targetObject: null)); + } + + return SafeCreateInstance(type); + } + catch (COMException e) + { + // Check Error Code to see if Error is because of Com apartment Mismatch. + if (e.HResult == RPC_E_CHANGED_MODE) + { + createInfo = new ComCreateInfo(); + + Thread thread = new(new ParameterizedThreadStart(STAComCreateThreadProc)); + thread.SetApartmentState(ApartmentState.STA); + thread.Start(createInfo); + + thread.Join(); + + if (createInfo.success) + { + return createInfo.objectCreated; + } + + ThrowTerminatingError( + new ErrorRecord(createInfo.e, "NoCOMClassIdentified", + ErrorCategory.ResourceUnavailable, null)); + } + else + { + ThrowTerminatingError( + new ErrorRecord( + e, + "NoCOMClassIdentified", + ErrorCategory.ResourceUnavailable, null)); + } + + return null; + } + } + + #endregion Com +#endif + + // HResult code '-2147417850' - Cannot change thread mode after it is set. + private const int RPC_E_CHANGED_MODE = unchecked((int)0x80010106); + private const string netSetName = "Net"; + } + + /// + /// Native methods for dealing with COM objects. + /// + internal static class NewObjectNativeMethods + { + /// Return Type: HRESULT->LONG->int + [DllImport(PinvokeDllNames.CLSIDFromProgIDDllName)] + internal static extern int CLSIDFromProgID([MarshalAs(UnmanagedType.LPWStr)] string lpszProgID, out Guid pclsid); + } +} diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/NewAliasCommand.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/NewAliasCommand.cs index d53cd1d2823..8ae43a6e4fd 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/NewAliasCommand.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/NewAliasCommand.cs @@ -1,18 +1,16 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. -using System; using System.Management.Automation; using System.Management.Automation.Internal; namespace Microsoft.PowerShell.Commands { /// - /// The implementation of the "new-alias" cmdlet + /// The implementation of the "new-alias" cmdlet. /// - /// - [Cmdlet(VerbsCommon.New, "Alias", SupportsShouldProcess = true, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113352")] + [Cmdlet(VerbsCommon.New, "Alias", SupportsShouldProcess = true, ConfirmImpact = ConfirmImpact.Low, + HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2097022")] [OutputType(typeof(AliasInfo))] public class NewAliasCommand : WriteAliasCommandBase { @@ -21,7 +19,6 @@ public class NewAliasCommand : WriteAliasCommandBase /// /// The main processing loop of the command. /// - /// protected override void ProcessRecord() { // If not force, then see if the alias already exists @@ -29,7 +26,7 @@ protected override void ProcessRecord() if (!Force) { AliasInfo existingAlias = null; - if (String.IsNullOrEmpty(Scope)) + if (string.IsNullOrEmpty(Scope)) { existingAlias = SessionState.Internal.GetAlias(Name); } @@ -38,7 +35,6 @@ protected override void ProcessRecord() existingAlias = SessionState.Internal.GetAliasAtScope(Name, Scope); } - if (existingAlias != null) { // Throw if alias exists and is private... @@ -47,7 +43,7 @@ protected override void ProcessRecord() // Since the alias already exists, write an error. SessionStateException aliasExists = - new SessionStateException( + new( Name, SessionStateCategory.Alias, "AliasAlreadyExists", @@ -65,7 +61,7 @@ protected override void ProcessRecord() // Create the alias info AliasInfo newAlias = - new AliasInfo( + new( Name, Value, Context, @@ -88,7 +84,7 @@ protected override void ProcessRecord() try { - if (String.IsNullOrEmpty(Scope)) + if (string.IsNullOrEmpty(Scope)) { result = SessionState.Internal.SetAliasItem(newAlias, Force, MyInvocation.CommandOrigin); } @@ -129,8 +125,7 @@ protected override void ProcessRecord() WriteObject(result); } } - } // ProcessRecord + } #endregion Command code - } // class NewAliasCommand -}//Microsoft.PowerShell.Commands - + } +} diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/NewEventCommand.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/NewEventCommand.cs new file mode 100644 index 00000000000..1e46241b2d8 --- /dev/null +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/NewEventCommand.cs @@ -0,0 +1,133 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Diagnostics.CodeAnalysis; +using System.Management.Automation; + +namespace Microsoft.PowerShell.Commands +{ + /// + /// Generates a new event notification. + /// + [Cmdlet(VerbsCommon.New, "Event", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2096708")] + [OutputType(typeof(PSEventArgs))] + public class NewEventCommand : PSCmdlet + { + #region parameters + + /// + /// Adds an event to the event queue. + /// + [Parameter(Position = 0, Mandatory = true)] + public string SourceIdentifier + { + get + { + return _sourceIdentifier; + } + + set + { + _sourceIdentifier = value; + } + } + + private string _sourceIdentifier = null; + + /// + /// Data relating to this event. + /// + [Parameter(Position = 1)] + [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] + public PSObject Sender + { + get + { + return _sender; + } + + set + { + _sender = value; + } + } + + private PSObject _sender = null; + + /// + /// Data relating to this event. + /// + [Parameter(Position = 2)] + [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] + public PSObject[] EventArguments + { + get + { + return _eventArguments; + } + + set + { + if (_eventArguments != null) + { + _eventArguments = value; + } + } + } + + private PSObject[] _eventArguments = Array.Empty(); + + /// + /// Data relating to this event. + /// + [Parameter(Position = 3)] + public PSObject MessageData + { + get + { + return _messageData; + } + + set + { + _messageData = value; + } + } + + private PSObject _messageData = null; + + #endregion parameters + + /// + /// Add the event to the event queue. + /// + protected override void EndProcessing() + { + object[] baseEventArgs = null; + + // Get the BaseObject from the event arguments + if (_eventArguments != null) + { + baseEventArgs = new object[_eventArguments.Length]; + int loopCounter = 0; + foreach (PSObject eventArg in _eventArguments) + { + if (eventArg != null) + baseEventArgs[loopCounter] = eventArg.BaseObject; + + loopCounter++; + } + } + + object messageSender = null; + if (_sender != null) + { + messageSender = _sender.BaseObject; + } + + // And then generate the event + WriteObject(Events.GenerateEvent(_sourceIdentifier, messageSender, baseEventArgs, _messageData, true, false)); + } + } +} diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/NewGuidCommand.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/NewGuidCommand.cs index 38beda8c638..0e466f86d3f 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/NewGuidCommand.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/NewGuidCommand.cs @@ -1,7 +1,7 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +#nullable enable using System; using System.Management.Automation; @@ -9,18 +9,50 @@ namespace Microsoft.PowerShell.Commands { /// - /// The implementation of the "new-guid" cmdlet + /// The implementation of the "new-guid" cmdlet. /// - [Cmdlet(VerbsCommon.New, "Guid", HelpUri = "https://go.microsoft.com/fwlink/?LinkId=526920")] + [Cmdlet(VerbsCommon.New, "Guid", DefaultParameterSetName = "Default", HelpUri = "https://go.microsoft.com/fwlink/?LinkId=2097130")] [OutputType(typeof(Guid))] - public class NewGuidCommand : Cmdlet + public class NewGuidCommand : PSCmdlet { /// - /// returns a guid + /// Gets or sets a value indicating that the cmdlet should return a Guid structure whose value is all zeros. /// - protected override void EndProcessing() + [Parameter(ParameterSetName = "Empty")] + public SwitchParameter Empty { get; set; } + + /// + /// Gets or sets the value to be converted to a Guid. + /// + [Parameter(Position = 0, ValueFromPipeline = true, ParameterSetName = "InputObject")] + [System.Diagnostics.CodeAnalysis.AllowNull] + public string InputObject { get; set; } + + /// + /// Returns a Guid. + /// + protected override void ProcessRecord() { - WriteObject(Guid.NewGuid()); + Guid? guid = null; + + if (ParameterSetName is "InputObject") + { + try + { + guid = new(InputObject); + } + catch (Exception ex) + { + ErrorRecord error = new(ex, "StringNotRecognizedAsGuid", ErrorCategory.InvalidArgument, null); + WriteError(error); + } + } + else + { + guid = Empty.ToBool() ? Guid.Empty : Guid.NewGuid(); + } + + WriteObject(guid); } } } diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/NewTemporaryFileCommand.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/NewTemporaryFileCommand.cs index 2ea91339037..93b3eb209af 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/NewTemporaryFileCommand.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/NewTemporaryFileCommand.cs @@ -1,43 +1,46 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. -using System; using System.IO; using System.Management.Automation; namespace Microsoft.PowerShell.Commands { /// - /// The implementation of the "New-TemporaryFile" cmdlet + /// The implementation of the "New-TemporaryFile" cmdlet. /// - [Cmdlet(VerbsCommon.New, "TemporaryFile", SupportsShouldProcess = true, HelpUri = "https://go.microsoft.com/fwlink/?LinkId=526726")] + [Cmdlet(VerbsCommon.New, "TemporaryFile", SupportsShouldProcess = true, ConfirmImpact = ConfirmImpact.Low, + HelpUri = "https://go.microsoft.com/fwlink/?LinkId=2097032")] [OutputType(typeof(System.IO.FileInfo))] public class NewTemporaryFileCommand : Cmdlet { /// - /// returns a TemporaryFile + /// Returns a TemporaryFile. /// protected override void EndProcessing() { string filePath = null; - string tempPath = System.Environment.GetEnvironmentVariable("TEMP"); + string tempPath = Path.GetTempPath(); if (ShouldProcess(tempPath)) { try { filePath = Path.GetTempFileName(); } - catch (Exception e) + catch (IOException ioException) { - WriteError( + ThrowTerminatingError( new ErrorRecord( - e, + ioException, "NewTemporaryFileWriteError", ErrorCategory.WriteError, tempPath)); return; } + if (!string.IsNullOrEmpty(filePath)) { - FileInfo file = new FileInfo(filePath); + FileInfo file = new(filePath); WriteObject(file); } } diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/NewTimeSpanCommand.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/NewTimeSpanCommand.cs index 36a15cdeb97..a5d784da9bb 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/NewTimeSpanCommand.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/NewTimeSpanCommand.cs @@ -1,18 +1,18 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. using System; using System.Management.Automation; + using Dbg = System.Management.Automation; namespace Microsoft.PowerShell.Commands { /// - /// implementation for the new-timespan command + /// Implementation for the new-timespan command. /// [Cmdlet(VerbsCommon.New, "TimeSpan", DefaultParameterSetName = "Date", - HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113360", RemotingCapability = RemotingCapability.None)] + HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2096709", RemotingCapability = RemotingCapability.None)] [OutputType(typeof(TimeSpan))] public sealed class NewTimeSpanCommand : PSCmdlet { @@ -20,7 +20,7 @@ public sealed class NewTimeSpanCommand : PSCmdlet /// /// This parameter indicates the date the time span begins; - /// it is used if two times are being compared + /// it is used if two times are being compared. /// [Parameter(Position = 0, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true, ParameterSetName = "Date")] [Alias("LastWriteTime")] @@ -30,16 +30,17 @@ public DateTime Start { return _start; } + set { _start = value; _startSpecified = true; } } + private DateTime _start; private bool _startSpecified; - /// /// This parameter indicates the end of a time span. It is used if two /// times are being compared. If one of the times is not specified, @@ -52,49 +53,53 @@ public DateTime End { return _end; } + set { _end = value; _endSpecified = true; } } + private DateTime _end; private bool _endSpecified = false; - /// - /// Allows the user to override the day + /// Allows the user to override the day. /// [Parameter(ParameterSetName = "Time")] - public int Days { get; set; } = 0; - + public int Days { get; set; } /// - /// Allows the user to override the hour + /// Allows the user to override the hour. /// [Parameter(ParameterSetName = "Time")] - public int Hours { get; set; } = 0; - + public int Hours { get; set; } /// - /// Allows the user to override the minute + /// Allows the user to override the minute. /// [Parameter(ParameterSetName = "Time")] - public int Minutes { get; set; } = 0; + public int Minutes { get; set; } + /// + /// Allows the user to override the second. + /// + [Parameter(ParameterSetName = "Time")] + public int Seconds { get; set; } /// - /// Allows the user to override the second + /// Allows the user to override the millisecond. /// [Parameter(ParameterSetName = "Time")] - public int Seconds { get; set; } = 0; + public int Milliseconds { get; set; } #endregion #region methods /// - /// Calculate and write out the appropriate timespan + /// Calculate and write out the appropriate timespan. /// protected override void ProcessRecord() { @@ -110,6 +115,7 @@ protected override void ProcessRecord() { startTime = Start; } + if (_endSpecified) { endTime = End; @@ -119,7 +125,7 @@ protected override void ProcessRecord() break; case "Time": - result = new TimeSpan(Days, Hours, Minutes, Seconds); + result = new TimeSpan(Days, Hours, Minutes, Seconds, Milliseconds); break; default: @@ -128,10 +134,7 @@ protected override void ProcessRecord() } WriteObject(result); - } // EndProcessing - + } #endregion - } // NewTimeSpanCommand -} // namespace Microsoft.PowerShell.Commands - - + } +} diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ObjectCommandComparer.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ObjectCommandComparer.cs index d3dc8bc5cc9..13b0234a02b 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ObjectCommandComparer.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ObjectCommandComparer.cs @@ -1,13 +1,12 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. #region Using directives using System; using System.Collections; -using System.Management.Automation; using System.Globalization; +using System.Management.Automation; #endregion @@ -17,10 +16,10 @@ namespace Microsoft.PowerShell.Commands /// /// Keeps the property value of inputObject. Because the value of a non-existing property is null, - /// isExistingProperty is needed to distinguish whether a property exists and its value is null or - /// the property does not exist at all. + /// isExistingProperty is needed to distinguish whether a property exists and its value is null or + /// the property does not exist at all. /// - internal class ObjectCommandPropertyValue + internal sealed class ObjectCommandPropertyValue { private ObjectCommandPropertyValue() { } @@ -31,7 +30,7 @@ internal ObjectCommandPropertyValue(object propVal) } /// - /// ObjectCommandPropertyValue constructor. + /// Initializes a new instance of the class. /// /// Property Value. /// Indicates if the Property value comparison has to be case sensitive or not. @@ -43,7 +42,6 @@ internal ObjectCommandPropertyValue(object propVal, bool isCaseSensitive, Cultur this.cultureInfo = cultureInfo; } - internal object PropertyValue { get; } internal bool IsExistingProperty { get; } @@ -67,9 +65,9 @@ internal CultureInfo Culture } } - internal static readonly ObjectCommandPropertyValue NonExistingProperty = new ObjectCommandPropertyValue(); - internal static readonly ObjectCommandPropertyValue ExistingNullProperty = new ObjectCommandPropertyValue(null); - private bool _caseSensitive; + internal static readonly ObjectCommandPropertyValue NonExistingProperty = new(); + internal static readonly ObjectCommandPropertyValue ExistingNullProperty = new(null); + private readonly bool _caseSensitive; internal CultureInfo cultureInfo = null; /// @@ -77,35 +75,34 @@ internal CultureInfo Culture /// /// Input Object. /// True if both the objects are same or else returns false. - public override bool Equals(Object inputObject) + public override bool Equals(object inputObject) { - ObjectCommandPropertyValue objectCommandPropertyValueObject = inputObject as ObjectCommandPropertyValue; - if (objectCommandPropertyValueObject == null) + if (inputObject is not ObjectCommandPropertyValue objectCommandPropertyValueObject) + { return false; + } object baseObject = PSObject.Base(PropertyValue); object inComingbaseObjectPropertyValue = PSObject.Base(objectCommandPropertyValueObject.PropertyValue); - IComparable baseObjectComparable = baseObject as IComparable; - - if (baseObjectComparable != null) + if (baseObject is IComparable) { - return (LanguagePrimitives.Compare(baseObject, inComingbaseObjectPropertyValue, CaseSensitive, Culture) == 0); + var success = LanguagePrimitives.TryCompare(baseObject, inComingbaseObjectPropertyValue, CaseSensitive, Culture, out int result); + return success && result == 0; } - else + + if (baseObject == null && inComingbaseObjectPropertyValue == null) { - if (baseObject == null && inComingbaseObjectPropertyValue == null) - { - return true; - } - if (baseObject != null && inComingbaseObjectPropertyValue != null) - { - return baseObject.ToString().Equals(inComingbaseObjectPropertyValue.ToString(), StringComparison.OrdinalIgnoreCase); - } + return true; + } - // One of the property values being compared is null. - return false; + if (baseObject != null && inComingbaseObjectPropertyValue != null) + { + return baseObject.ToString().Equals(inComingbaseObjectPropertyValue.ToString(), StringComparison.OrdinalIgnoreCase); } + + // One of the property values being compared is null. + return false; } /// @@ -115,12 +112,17 @@ public override bool Equals(Object inputObject) public override int GetHashCode() { if (PropertyValue == null) + { return 0; + } object baseObject = PSObject.Base(PropertyValue); - IComparable baseObjectComparable = baseObject as IComparable; + if (baseObject == null) + { + return 0; + } - if (baseObjectComparable != null) + if (baseObject is IComparable) { return baseObject.GetHashCode(); } @@ -132,11 +134,12 @@ public override int GetHashCode() } /// - /// + /// ObjectCommandComparer class. /// - internal class ObjectCommandComparer : IComparer + internal sealed class ObjectCommandComparer : IComparer { /// + /// Initializes a new instance of the class. /// Constructor that doesn't set any private field. /// Necessary because compareTo can compare two objects by calling /// ((ICompare)obj1).CompareTo(obj2) without using a key. @@ -154,7 +157,6 @@ private static bool IsValueNull(object value) return (val == null); } - internal int Compare(ObjectCommandPropertyValue first, ObjectCommandPropertyValue second) { if (first.IsExistingProperty && second.IsExistingProperty) @@ -173,70 +175,61 @@ internal int Compare(ObjectCommandPropertyValue first, ObjectCommandPropertyValu { return 1; } - //both are nonexisting + // both are nonexisting return 0; } /// - /// Main method that will compare first and second by - /// their keys considering case and order + /// Main method that will compare first and second by their keys considering case and order. /// /// - /// first object to extract value + /// First object to extract value. /// /// - /// second object to extract value + /// Second object to extract value. /// /// - /// 0 if they are the same, less than 0 if first is smaller, more than 0 if first is greater - /// + /// 0 if they are the same, less than 0 if first is smaller, more than 0 if first is greater. + /// public int Compare(object first, object second) { // This method will never throw exceptions, two null // objects are considered the same - if (IsValueNull(first) && IsValueNull(second)) return 0; - + if (IsValueNull(first) && IsValueNull(second)) + { + return 0; + } - PSObject firstMsh = first as PSObject; - if (firstMsh != null) + if (first is PSObject firstMsh) { first = firstMsh.BaseObject; } - PSObject secondMsh = second as PSObject; - if (secondMsh != null) + if (second is PSObject secondMsh) { second = secondMsh.BaseObject; } - try - { - return LanguagePrimitives.Compare(first, second, !_caseSensitive, _cultureInfo) * (_ascendingOrder ? 1 : -1); - } - catch (InvalidCastException) - { - } - catch (ArgumentException) + if (!LanguagePrimitives.TryCompare(first, second, !_caseSensitive, _cultureInfo, out int result)) { // Note that this will occur if the objects do not support // IComparable. We fall back to comparing as strings. - } - // being here means the first object doesn't support ICompare - // or an Exception was raised win Compare - string firstString = PSObject.AsPSObject(first).ToString(); - string secondString = PSObject.AsPSObject(second).ToString(); + // being here means the first object doesn't support ICompare + string firstString = PSObject.AsPSObject(first).ToString(); + string secondString = PSObject.AsPSObject(second).ToString(); - return _cultureInfo.CompareInfo.Compare(firstString, secondString, _caseSensitive ? CompareOptions.None : CompareOptions.IgnoreCase) * (_ascendingOrder ? 1 : -1); + result = _cultureInfo.CompareInfo.Compare(firstString, secondString, _caseSensitive ? CompareOptions.None : CompareOptions.IgnoreCase); + } + + return _ascendingOrder ? result : -result; } - private CultureInfo _cultureInfo = null; + private readonly CultureInfo _cultureInfo = null; - private bool _ascendingOrder = true; + private readonly bool _ascendingOrder = true; - private bool _caseSensitive = false; + private readonly bool _caseSensitive = false; } - #endregion } - diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/OrderObjectBase.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/OrderObjectBase.cs index 9d34c4b75ec..596bbcaafc6 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/OrderObjectBase.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/OrderObjectBase.cs @@ -1,20 +1,20 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. using System; -using System.Collections.Generic; using System.Collections; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; using System.Management.Automation; using System.Management.Automation.Internal; -using System.Globalization; + using Microsoft.PowerShell.Commands.Internal.Format; -using System.Diagnostics.CodeAnalysis; namespace Microsoft.PowerShell.Commands { /// - /// definitions for hash table keys + /// Definitions for hash table keys. /// internal static class SortObjectParameterDefinitionKeys { @@ -24,7 +24,7 @@ internal static class SortObjectParameterDefinitionKeys /// /// - internal class SortObjectExpressionParameterDefinition : CommandParameterDefinition + internal sealed class SortObjectExpressionParameterDefinition : CommandParameterDefinition { protected override void SetEntries() { @@ -36,7 +36,7 @@ protected override void SetEntries() /// /// - internal class GroupObjectExpressionParameterDefinition : CommandParameterDefinition + internal sealed class GroupObjectExpressionParameterDefinition : CommandParameterDefinition { protected override void SetEntries() { @@ -45,20 +45,23 @@ protected override void SetEntries() } /// - /// Base Cmdlet for cmdlets which deal with raw objects + /// Base Cmdlet for cmdlets which deal with raw objects. /// public class ObjectCmdletBase : PSCmdlet { #region Parameters /// - /// /// /// [Parameter] [System.Diagnostics.CodeAnalysis.SuppressMessage("GoldMan", "#pw17903:UseOfLCID", Justification = "The CultureNumber is only used if the property has been set with a hex string starting with 0x")] public string Culture { - get { return _cultureInfo != null ? _cultureInfo.ToString() : null; } + get + { + return _cultureInfo?.ToString(); + } + set { if (string.IsNullOrEmpty(value)) @@ -72,7 +75,7 @@ public string Culture if (trimmedValue.StartsWith("0x", StringComparison.OrdinalIgnoreCase)) { if ((trimmedValue.Length > 2) && - int.TryParse(trimmedValue.Substring(2), NumberStyles.AllowHexSpecifier, + int.TryParse(trimmedValue.AsSpan(2), NumberStyles.AllowHexSpecifier, CultureInfo.CurrentCulture, out cultureNumber)) { _cultureInfo = new CultureInfo(cultureNumber); @@ -85,21 +88,24 @@ public string Culture _cultureInfo = new CultureInfo(cultureNumber); return; } + _cultureInfo = new CultureInfo(value); } } + internal CultureInfo _cultureInfo = null; /// - /// /// /// [Parameter] public SwitchParameter CaseSensitive { get { return _caseSensitive; } + set { _caseSensitive = value; } } + private bool _caseSensitive; #endregion Parameters } @@ -112,10 +118,9 @@ public abstract class ObjectBase : ObjectCmdletBase #region Parameters /// - /// /// [Parameter(ValueFromPipeline = true)] - public PSObject InputObject { set; get; } = AutomationNull.Value; + public PSObject InputObject { get; set; } = AutomationNull.Value; /// /// Gets or Sets the Properties that would be used for Grouping, Sorting and Comparison. @@ -140,14 +145,16 @@ public class OrderObjectBase : ObjectBase internal SwitchParameter DescendingOrder { get { return !_ascending; } + set { _ascending = !value; } } + private bool _ascending = true; internal List InputObjects { get; } = new List(); /// - /// CultureInfo converted from the Culture Cmdlet parameter + /// CultureInfo converted from the Culture Cmdlet parameter. /// internal CultureInfo ConvertedCulture { @@ -160,9 +167,7 @@ internal CultureInfo ConvertedCulture #endregion Internal Properties /// - /// - /// Simply accumulates the incoming objects - /// + /// Simply accumulates the incoming objects. /// protected override void ProcessRecord() { @@ -178,11 +183,11 @@ internal sealed class OrderByProperty #region Internal properties /// - /// a logical matrix where each row is an input object and its property values specified by Properties + /// A logical matrix where each row is an input object and its property values specified by Properties. /// - internal List OrderMatrix { get; } = null; + internal List OrderMatrix { get; } - internal OrderByPropertyComparer Comparer { get; } = null; + internal OrderByPropertyComparer Comparer { get; } internal List MshParameterList { @@ -200,7 +205,7 @@ internal List MshParameterList // a string array and allows wildcard. // Yes, the Cmdlet is needed. It's used to get the TerminatingErrorContext, WriteError and WriteDebug. - #region process MshExpression and MshParameter + #region process PSPropertyExpression and MshParameter private static void ProcessExpressionParameter( List inputObjects, @@ -209,7 +214,7 @@ private static void ProcessExpressionParameter( out List mshParameterList) { mshParameterList = null; - TerminatingErrorContext invocationContext = new TerminatingErrorContext(cmdlet); + TerminatingErrorContext invocationContext = new(cmdlet); // compare-object and group-object use the same definition here ParameterProcessor processor = cmdlet is SortObjectCommand ? new ParameterProcessor(new SortObjectExpressionParameterDefinition()) : @@ -219,6 +224,7 @@ private static void ProcessExpressionParameter( { expr = GetDefaultKeyPropertySet(inputObjects[0]); } + if (expr != null) { List unexpandedParameterList = processor.ProcessParameters(expr, invocationContext); @@ -232,7 +238,7 @@ internal void ProcessExpressionParameter( PSCmdlet cmdlet, object[] expr) { - TerminatingErrorContext invocationContext = new TerminatingErrorContext(cmdlet); + TerminatingErrorContext invocationContext = new(cmdlet); // compare-object and group-object use the same definition here ParameterProcessor processor = cmdlet is SortObjectCommand ? new ParameterProcessor(new SortObjectExpressionParameterDefinition()) : @@ -246,17 +252,14 @@ internal void ProcessExpressionParameter( foreach (MshParameter unexpandedParameter in _unexpandedParameterList) { - MshExpression mshExpression = (MshExpression)unexpandedParameter.GetEntry(FormatParameterDefinitionKeys.ExpressionEntryKey); + PSPropertyExpression mshExpression = (PSPropertyExpression)unexpandedParameter.GetEntry(FormatParameterDefinitionKeys.ExpressionEntryKey); if (!mshExpression.HasWildCardCharacters) // this special cases 1) script blocks and 2) wildcard-less strings { _mshParameterList.Add(unexpandedParameter); } else { - if (_unExpandedParametersWithWildCardPattern == null) - { - _unExpandedParametersWithWildCardPattern = new List(); - } + _unExpandedParametersWithWildCardPattern ??= new List(); _unExpandedParametersWithWildCardPattern.Add(unexpandedParameter); } @@ -269,20 +272,20 @@ internal void ProcessExpressionParameter( // match property names on the incoming objects. private static List ExpandExpressions(List inputObjects, List unexpandedParameterList) { - List expandedParameterList = new List(); + List expandedParameterList = new(); if (unexpandedParameterList != null) { foreach (MshParameter unexpandedParameter in unexpandedParameterList) { - MshExpression ex = (MshExpression)unexpandedParameter.GetEntry(FormatParameterDefinitionKeys.ExpressionEntryKey); + PSPropertyExpression ex = (PSPropertyExpression)unexpandedParameter.GetEntry(FormatParameterDefinitionKeys.ExpressionEntryKey); if (!ex.HasWildCardCharacters) // this special cases 1) script blocks and 2) wildcard-less strings { expandedParameterList.Add(unexpandedParameter); } else { - SortedDictionary expandedPropertyNames = new SortedDictionary(StringComparer.OrdinalIgnoreCase); + SortedDictionary expandedPropertyNames = new(StringComparer.OrdinalIgnoreCase); if (inputObjects != null) { foreach (object inputObject in inputObjects) @@ -292,16 +295,16 @@ private static List ExpandExpressions(List inputObjects, continue; } - foreach (MshExpression resolvedName in ex.ResolveNames(PSObject.AsPSObject(inputObject))) + foreach (PSPropertyExpression resolvedName in ex.ResolveNames(PSObject.AsPSObject(inputObject))) { expandedPropertyNames[resolvedName.ToString()] = resolvedName; } } } - foreach (MshExpression expandedExpression in expandedPropertyNames.Values) + foreach (PSPropertyExpression expandedExpression in expandedPropertyNames.Values) { - MshParameter expandedParameter = new MshParameter(); + MshParameter expandedParameter = new(); expandedParameter.hash = (Hashtable)unexpandedParameter.hash.Clone(); expandedParameter.hash[FormatParameterDefinitionKeys.ExpressionEntryKey] = expandedExpression; @@ -322,22 +325,22 @@ private static void ExpandExpressions(PSObject inputObject, List U { foreach (MshParameter unexpandedParameter in UnexpandedParametersWithWildCardPattern) { - MshExpression ex = (MshExpression)unexpandedParameter.GetEntry(FormatParameterDefinitionKeys.ExpressionEntryKey); + PSPropertyExpression ex = (PSPropertyExpression)unexpandedParameter.GetEntry(FormatParameterDefinitionKeys.ExpressionEntryKey); - SortedDictionary expandedPropertyNames = new SortedDictionary(StringComparer.OrdinalIgnoreCase); + SortedDictionary expandedPropertyNames = new(StringComparer.OrdinalIgnoreCase); if (inputObject == null) { continue; } - foreach (MshExpression resolvedName in ex.ResolveNames(PSObject.AsPSObject(inputObject))) + foreach (PSPropertyExpression resolvedName in ex.ResolveNames(PSObject.AsPSObject(inputObject))) { expandedPropertyNames[resolvedName.ToString()] = resolvedName; } - foreach (MshExpression expandedExpression in expandedPropertyNames.Values) + foreach (PSPropertyExpression expandedExpression in expandedPropertyNames.Values) { - MshParameter expandedParameter = new MshParameter(); + MshParameter expandedParameter = new(); expandedParameter.hash = (Hashtable)unexpandedParameter.hash.Clone(); expandedParameter.hash[FormatParameterDefinitionKeys.ExpressionEntryKey] = expandedExpression; @@ -354,18 +357,18 @@ internal static string[] GetDefaultKeyPropertySet(PSObject mshObj) { return null; } - PSPropertySet defaultKeys = standardNames.Members["DefaultKeyPropertySet"] as PSPropertySet; - if (defaultKeys == null) + if (standardNames.Members["DefaultKeyPropertySet"] is not PSPropertySet defaultKeys) { return null; } + string[] props = new string[defaultKeys.ReferencedPropertyNames.Count]; defaultKeys.ReferencedPropertyNames.CopyTo(props, 0); return props; } - #endregion process MshExpression and MshParameter + #endregion process PSPropertyExpression and MshParameter internal static List CreateOrderMatrix( PSCmdlet cmdlet, @@ -373,24 +376,26 @@ internal static List CreateOrderMatrix( List mshParameterList ) { - List orderMatrixToCreate = new List(); + List orderMatrixToCreate = new(); for (int index = 0; index < inputObjects.Count; index++) { PSObject so = inputObjects[index]; if (so == null || so == AutomationNull.Value) continue; - List evaluationErrors = new List(); - List propertyNotFoundMsgs = new List(); + List evaluationErrors = new(); + List propertyNotFoundMsgs = new(); OrderByPropertyEntry result = OrderByPropertyEntryEvaluationHelper.ProcessObject(so, mshParameterList, evaluationErrors, propertyNotFoundMsgs, originalIndex: index); foreach (ErrorRecord err in evaluationErrors) { cmdlet.WriteError(err); } + foreach (string debugMsg in propertyNotFoundMsgs) { cmdlet.WriteDebug(debugMsg); } + orderMatrixToCreate.Add(result); } @@ -413,10 +418,11 @@ private static OrderByPropertyComparer CreateComparer( { return null; } - Nullable[] ascendingOverrides = null; + + bool?[] ascendingOverrides = null; if (mshParameterList != null && mshParameterList.Count != 0) { - ascendingOverrides = new Nullable[mshParameterList.Count]; + ascendingOverrides = new bool?[mshParameterList.Count]; for (int k = 0; k < ascendingOverrides.Length; k++) { object ascendingVal = mshParameterList[k].GetEntry( @@ -447,6 +453,7 @@ private static OrderByPropertyComparer CreateComparer( } } } + OrderByPropertyComparer comparer = OrderByPropertyComparer.CreateComparer(orderMatrix, ascending, ascendingOverrides, cultureInfo, caseSensitive); @@ -471,7 +478,7 @@ bool caseSensitive } /// - /// OrderByProperty constructor. + /// Initializes a new instance of the class. /// internal OrderByProperty() { @@ -482,7 +489,7 @@ internal OrderByProperty() /// /// Utility function used to create OrderByPropertyEntry for the supplied input object. /// - /// PSCmdlet + /// PSCmdlet. /// Input Object. /// Indicates if the Property value comparisons need to be case sensitive or not. /// Culture Info that needs to be used for comparison. @@ -500,14 +507,15 @@ internal OrderByPropertyEntry CreateOrderByPropertyEntry( ExpandExpressions(inputObject, _unExpandedParametersWithWildCardPattern, _mshParameterList); } - List evaluationErrors = new List(); - List propertyNotFoundMsgs = new List(); + List evaluationErrors = new(); + List propertyNotFoundMsgs = new(); OrderByPropertyEntry result = OrderByPropertyEntryEvaluationHelper.ProcessObject(inputObject, _mshParameterList, evaluationErrors, propertyNotFoundMsgs, isCaseSensitive, cultureInfo); foreach (ErrorRecord err in evaluationErrors) { cmdlet.WriteError(err); } + foreach (string debugMsg in propertyNotFoundMsgs) { cmdlet.WriteDebug(debugMsg); @@ -519,7 +527,7 @@ internal OrderByPropertyEntry CreateOrderByPropertyEntry( #endregion Utils // list of processed parameters obtained from the Expression array - private List _mshParameterList = null; + private readonly List _mshParameterList = null; // list of unprocessed parameters obtained from the Expression array. private List _unexpandedParameterList = null; @@ -535,7 +543,7 @@ internal static OrderByPropertyEntry ProcessObject(PSObject inputObject, List expressionResults = ex.GetValues(inputObject, false, true); + List expressionResults = ex.GetValues(inputObject, false, true); if (expressionResults.Count == 0) { @@ -583,9 +591,10 @@ private static void EvaluateSortingExpression( propertyNotFoundMsg = StringUtil.Format(SortObjectStrings.PropertyNotFound, ex.ToString()); return; } + propertyNotFoundMsg = null; // we obtained some results, enter them into the list - foreach (MshExpressionResult r in expressionResults) + foreach (PSPropertyExpressionResult r in expressionResults) { if (r.Exception == null) { @@ -593,7 +602,7 @@ private static void EvaluateSortingExpression( } else { - ErrorRecord errorRecord = new ErrorRecord( + ErrorRecord errorRecord = new( r.Exception, "ExpressionEvaluation", ErrorCategory.InvalidResult, @@ -601,18 +610,19 @@ private static void EvaluateSortingExpression( errors.Add(errorRecord); orderValues.Add(ObjectCommandPropertyValue.ExistingNullProperty); } + comparable = true; } } } /// - /// This is the row of the OrderMatrix + /// This is the row of the OrderMatrix. /// internal sealed class OrderByPropertyEntry { internal PSObject inputObject = null; - internal List orderValues = new List(); + internal List orderValues = new(); // The originalIndex field was added to enable stable heap-sorts (Top N/Bottom N) internal int originalIndex = -1; @@ -620,7 +630,7 @@ internal sealed class OrderByPropertyEntry internal bool comparable = false; } - internal class OrderByPropertyComparer : IComparer + internal sealed class OrderByPropertyComparer : IComparer { internal OrderByPropertyComparer(bool[] ascending, CultureInfo cultureInfo, bool caseSensitive) { @@ -651,7 +661,7 @@ public int Compare(OrderByPropertyEntry firstEntry, OrderByPropertyEntry secondE return order; } - internal static OrderByPropertyComparer CreateComparer(List orderMatrix, bool ascendingFlag, Nullable[] ascendingOverrides, CultureInfo cultureInfo, bool caseSensitive) + internal static OrderByPropertyComparer CreateComparer(List orderMatrix, bool ascendingFlag, bool?[] ascendingOverrides, CultureInfo cultureInfo, bool caseSensitive) { if (orderMatrix.Count == 0) return null; @@ -664,6 +674,7 @@ internal static OrderByPropertyComparer CreateComparer(List maxEntries) maxEntries = entry.orderValues.Count; } + if (maxEntries == 0) return null; @@ -685,10 +696,10 @@ internal static OrderByPropertyComparer CreateComparer(List + internal sealed class IndexedOrderByPropertyComparer : IComparer { internal IndexedOrderByPropertyComparer(OrderByPropertyComparer orderByPropertyComparer) { @@ -702,6 +713,7 @@ public int Compare(OrderByPropertyEntry lhs, OrderByPropertyEntry rhs) { return lhs.comparable.CompareTo(rhs.comparable) * -1; } + int result = _orderByPropertyComparer.Compare(lhs, rhs); // When items are identical according to the internal comparison, compare by index // to preserve the original order @@ -713,7 +725,6 @@ public int Compare(OrderByPropertyEntry lhs, OrderByPropertyEntry rhs) return result; } - OrderByPropertyComparer _orderByPropertyComparer = null; + private readonly OrderByPropertyComparer _orderByPropertyComparer = null; } } - diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/PSBreakpointAccessorCommandBase.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/PSBreakpointAccessorCommandBase.cs new file mode 100644 index 00000000000..d3dfd30f9e5 --- /dev/null +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/PSBreakpointAccessorCommandBase.cs @@ -0,0 +1,19 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +namespace Microsoft.PowerShell.Commands +{ + /// + /// Base class for Get/Set-PSBreakpoint. + /// + public abstract class PSBreakpointAccessorCommandBase : PSBreakpointCommandBase + { + #region strings + + internal const string CommandParameterSetName = "Command"; + internal const string LineParameterSetName = "Line"; + internal const string VariableParameterSetName = "Variable"; + + #endregion strings + } +} diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/PSBreakpointCommandBase.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/PSBreakpointCommandBase.cs new file mode 100644 index 00000000000..61d7236977d --- /dev/null +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/PSBreakpointCommandBase.cs @@ -0,0 +1,60 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.Management.Automation; +using System.Management.Automation.Runspaces; + +namespace Microsoft.PowerShell.Commands +{ + /// + /// Base class for PSBreakpoint cmdlets. + /// + public abstract class PSBreakpointCommandBase : PSCmdlet + { + #region parameters + + /// + /// Gets or sets the runspace where the breakpoints will be used. + /// + [Parameter] + [ValidateNotNull] + [Runspace] + public virtual Runspace Runspace { get; set; } + + #endregion parameters + + #region overrides + + /// + /// Identifies the default runspace. + /// + protected override void BeginProcessing() + { + Runspace ??= Context.CurrentRunspace; + } + + #endregion overrides + + #region protected methods + + /// + /// Write the given breakpoint out to the pipeline, decorated with the runspace instance id if appropriate. + /// + /// The breakpoint to write to the pipeline. + protected virtual void ProcessBreakpoint(Breakpoint breakpoint) + { + if (Runspace != Context.CurrentRunspace) + { + var pso = new PSObject(breakpoint); + pso.Properties.Add(new PSNoteProperty(RemotingConstants.RunspaceIdNoteProperty, Runspace.InstanceId)); + WriteObject(pso); + } + else + { + WriteObject(breakpoint); + } + } + + #endregion protected methods + } +} diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/PSBreakpointUpdaterCommandBase.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/PSBreakpointUpdaterCommandBase.cs new file mode 100644 index 00000000000..f186fbe1baa --- /dev/null +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/PSBreakpointUpdaterCommandBase.cs @@ -0,0 +1,168 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Management.Automation; +using System.Management.Automation.Internal; +using System.Management.Automation.Runspaces; + +namespace Microsoft.PowerShell.Commands +{ + /// + /// Base class for Enable/Disable/Remove-PSBreakpoint. + /// + public abstract class PSBreakpointUpdaterCommandBase : PSBreakpointCommandBase + { + #region strings + + internal const string BreakpointParameterSetName = "Breakpoint"; + internal const string IdParameterSetName = "Id"; + + #endregion strings + + #region parameters + + /// + /// Gets or sets the breakpoint to enable. + /// + [Parameter(ParameterSetName = BreakpointParameterSetName, ValueFromPipeline = true, Position = 0, Mandatory = true)] + [ValidateNotNull] + public Breakpoint[] Breakpoint { get; set; } + + /// + /// Gets or sets the Id of the breakpoint to enable. + /// + [Parameter(ParameterSetName = IdParameterSetName, ValueFromPipelineByPropertyName = true, Position = 0, Mandatory = true)] + [ValidateNotNull] + public int[] Id { get; set; } + + /// + /// Gets or sets the runspace where the breakpoints will be used. + /// + [Parameter(ParameterSetName = IdParameterSetName, ValueFromPipelineByPropertyName = true)] + [Alias("RunspaceId")] + [Runspace] + public override Runspace Runspace { get; set; } + + #endregion parameters + + #region overrides + + /// + /// Gathers the list of breakpoints to process and calls ProcessBreakpoints. + /// + protected override void ProcessRecord() + { + if (ParameterSetName.Equals(BreakpointParameterSetName, StringComparison.OrdinalIgnoreCase)) + { + foreach (Breakpoint breakpoint in Breakpoint) + { + if (ShouldProcessInternal(breakpoint.ToString()) && + TryGetRunspace(breakpoint)) + { + ProcessBreakpoint(breakpoint); + } + } + } + else + { + Debug.Assert( + ParameterSetName.Equals(IdParameterSetName, StringComparison.OrdinalIgnoreCase), + $"There should be no other parameter sets besides '{BreakpointParameterSetName}' and '{IdParameterSetName}'."); + + foreach (int id in Id) + { + Breakpoint breakpoint; + if (TryGetBreakpoint(id, out breakpoint) && + ShouldProcessInternal(breakpoint.ToString())) + { + ProcessBreakpoint(breakpoint); + } + } + } + } + + #endregion overrides + + #region private data + + private readonly Dictionary runspaces = new(); + + #endregion private data + + #region private methods + + private bool TryGetRunspace(Breakpoint breakpoint) + { + // Breakpoints retrieved from another runspace will have a RunspaceId note property of type Guid on them. + var pso = new PSObject(breakpoint); + var runspaceInstanceIdProperty = pso.Properties[RemotingConstants.RunspaceIdNoteProperty]; + if (runspaceInstanceIdProperty == null) + { + Runspace = Context.CurrentRunspace; + return true; + } + + Debug.Assert(runspaceInstanceIdProperty.TypeNameOfValue.Equals("System.Guid", StringComparison.OrdinalIgnoreCase), "Instance ids must be GUIDs."); + + var runspaceInstanceId = (Guid)runspaceInstanceIdProperty.Value; + if (runspaces.ContainsKey(runspaceInstanceId)) + { + Runspace = runspaces[runspaceInstanceId]; + return true; + } + + var matchingRunspaces = GetRunspaceUtils.GetRunspacesByInstanceId(new[] { runspaceInstanceId }); + if (matchingRunspaces.Count != 1) + { + WriteError( + new ErrorRecord( + new ArgumentException(StringUtil.Format(Debugger.RunspaceInstanceIdNotFound, runspaceInstanceId)), + "PSBreakpoint:RunspaceInstanceIdNotFound", + ErrorCategory.InvalidArgument, + null)); + return false; + } + + Runspace = runspaces[runspaceInstanceId] = matchingRunspaces[0]; + return true; + } + + private bool TryGetBreakpoint(int id, out Breakpoint breakpoint) + { + breakpoint = Runspace.Debugger.GetBreakpoint(id); + + if (breakpoint == null) + { + WriteError( + new ErrorRecord( + new ArgumentException(StringUtil.Format(Debugger.BreakpointIdNotFound, id)), + "PSBreakpoint:BreakpointIdNotFound", + ErrorCategory.InvalidArgument, + null)); + return false; + } + + return true; + } + + private bool ShouldProcessInternal(string target) + { + // ShouldProcess should be called only if the WhatIf or Confirm parameters are passed in explicitly. + // It should *not* be called if we are in a nested debug prompt and the current running command was + // run with -WhatIf or -Confirm, because this prevents the user from adding/removing breakpoints inside + // a debugger stop. + if (MyInvocation.BoundParameters.ContainsKey("WhatIf") || + MyInvocation.BoundParameters.ContainsKey("Confirm")) + { + return ShouldProcess(target); + } + + return true; + } + + #endregion private methods + } +} diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ReadConsoleCmdlet.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ReadConsoleCmdlet.cs index 94da63d9d09..3fabe75de24 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ReadConsoleCmdlet.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ReadConsoleCmdlet.cs @@ -1,8 +1,6 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. -using System; using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; @@ -15,21 +13,15 @@ namespace Microsoft.PowerShell.Commands { /// - /// /// Retrieves input from the host virtual console and writes it to the pipeline output. - /// /// - - [Cmdlet(VerbsCommunications.Read, "Host", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113371")] - [OutputType(typeof(String), typeof(SecureString))] + [Cmdlet(VerbsCommunications.Read, "Host", DefaultParameterSetName = "AsString", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2096610")] + [OutputType(typeof(string), typeof(SecureString))] public sealed class ReadHostCommand : PSCmdlet { /// - /// - /// Constructs a new instance. - /// + /// Initializes a new instance of the class. /// - public ReadHostCommand() { @@ -39,11 +31,8 @@ public sealed class ReadHostCommand : PSCmdlet #region Parameters /// - /// /// The objects to display on the host before collecting input. - /// /// - [Parameter(Position = 0, ValueFromRemainingArguments = true)] [AllowNull] public @@ -62,12 +51,9 @@ public sealed class ReadHostCommand : PSCmdlet } /// - /// - /// Set to no echo the input as is is typed. - /// + /// Gets or sets to no echo the input as is typed. If set then the cmdlet returns a secure string. /// - - [Parameter] + [Parameter(ParameterSetName = "AsSecureString")] public SwitchParameter AsSecureString @@ -82,16 +68,24 @@ public sealed class ReadHostCommand : PSCmdlet _safe = value; } } - #endregion Parameters + /// + /// Gets or sets whether the console will echo the input as is typed. If set then the cmdlet returns a regular string. + /// + [Parameter(ParameterSetName = "AsString")] + public + SwitchParameter + MaskInput + { + get; + set; + } + #endregion Parameters #region Cmdlet Overrides /// - /// - /// Write the prompt, then collect a line of input from the host, then - /// output it to the output stream. - /// + /// Write the prompt, then collect a line of input from the host, then output it to the output stream. /// protected override void BeginProcessing() { @@ -103,7 +97,7 @@ protected override void BeginProcessing() IEnumerator e = LanguagePrimitives.GetEnumerator(_prompt); if (e != null) { - StringBuilder sb = new StringBuilder(); + StringBuilder sb = new(); while (e.MoveNext()) { @@ -113,7 +107,7 @@ protected override void BeginProcessing() string element = (string)LanguagePrimitives.ConvertTo(e.Current, typeof(string), CultureInfo.InvariantCulture); - if (!String.IsNullOrEmpty(element)) + if (!string.IsNullOrEmpty(element)) { // Prepend a space if the stringbuilder isn't empty... // We could consider using $OFS here but that's probably more @@ -123,6 +117,7 @@ protected override void BeginProcessing() sb.Append(element); } } + promptString = sb.ToString(); } else @@ -130,34 +125,41 @@ protected override void BeginProcessing() promptString = (string)LanguagePrimitives.ConvertTo(_prompt, typeof(string), CultureInfo.InvariantCulture); } - FieldDescription fd = new FieldDescription(promptString); - if (AsSecureString) + FieldDescription fd = new(promptString); + if (AsSecureString || MaskInput) { - fd.SetParameterType(typeof(System.Security.SecureString)); + fd.SetParameterType(typeof(SecureString)); } else { - fd.SetParameterType(typeof(String)); + fd.SetParameterType(typeof(string)); } - Collection fdc = new Collection(); + Collection fdc = new(); fdc.Add(fd); - Dictionary result = Host.UI.Prompt("", "", fdc); + Dictionary result = Host.UI.Prompt(string.Empty, string.Empty, fdc); // Result can be null depending on the host implementation. One typical // example of a null return is for a canceled dialog. if (result != null) { foreach (PSObject o in result.Values) { - WriteObject(o); + if (MaskInput && o?.BaseObject is SecureString secureString) + { + WriteObject(Utils.GetStringFromSecureString(secureString)); + } + else + { + WriteObject(o); + } } } } else { object result; - if (AsSecureString) + if (AsSecureString || MaskInput) { result = Host.UI.ReadLineAsSecureString(); } @@ -165,16 +167,21 @@ protected override void BeginProcessing() { result = Host.UI.ReadLine(); } - WriteObject(result); + + if (MaskInput) + { + WriteObject(Utils.GetStringFromSecureString((SecureString)result)); + } + else + { + WriteObject(result); + } } } #endregion Cmdlet Overrides - - private object _prompt = null; - private Boolean _safe = false; + private bool _safe = false; } -} // namespace Microsoft.PowerShell.Commands - +} diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/RegisterObjectEventCommand.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/RegisterObjectEventCommand.cs index 1b4386200e1..d4e7dc784c1 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/RegisterObjectEventCommand.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/RegisterObjectEventCommand.cs @@ -1,8 +1,6 @@ -// -// Copyright (C) Microsoft. All rights reserved. -// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. -using System; using System.Management.Automation; namespace Microsoft.PowerShell.Commands @@ -10,14 +8,14 @@ namespace Microsoft.PowerShell.Commands /// /// Registers for an event on an object. /// - [Cmdlet(VerbsLifecycle.Register, "ObjectEvent", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=135244")] + [Cmdlet(VerbsLifecycle.Register, "ObjectEvent", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2096714")] [OutputType(typeof(PSEventJob))] public class RegisterObjectEventCommand : ObjectEventRegistrationBase { #region parameters /// - /// The object on which to subscribe + /// The object on which to subscribe. /// [Parameter(Mandatory = true, Position = 0)] public PSObject InputObject @@ -26,15 +24,17 @@ public PSObject InputObject { return _inputObject; } + set { _inputObject = value; } } + private PSObject _inputObject = null; /// - /// The event name to subscribe + /// The event name to subscribe. /// [Parameter(Mandatory = true, Position = 1)] public string EventName @@ -43,29 +43,31 @@ public string EventName { return _eventName; } + set { _eventName = value; } } + private string _eventName = null; #endregion parameters /// - /// Returns the object that generates events to be monitored + /// Returns the object that generates events to be monitored. /// - protected override Object GetSourceObject() + protected override object GetSourceObject() { return _inputObject; } /// - /// Returns the event name to be monitored on the input object + /// Returns the event name to be monitored on the input object. /// - protected override String GetSourceObjectEventName() + protected override string GetSourceObjectEventName() { return _eventName; } } -} \ No newline at end of file +} diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/RegisterPSEventCommand.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/RegisterPSEventCommand.cs index bcc24c49a54..6e4ced90760 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/RegisterPSEventCommand.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/RegisterPSEventCommand.cs @@ -1,6 +1,5 @@ -// -// Copyright (C) Microsoft. All rights reserved. -// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. using System; using System.Management.Automation; @@ -10,12 +9,12 @@ namespace Microsoft.PowerShell.Commands /// /// Registers for an event coming from the engine. /// - [Cmdlet(VerbsLifecycle.Register, "EngineEvent", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=135243")] + [Cmdlet(VerbsLifecycle.Register, "EngineEvent", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2097128")] [OutputType(typeof(PSEventJob))] public class RegisterEngineEventCommand : ObjectEventRegistrationBase { /// - /// Parameter for an identifier for this event subscription + /// Parameter for an identifier for this event subscription. /// [Parameter(Mandatory = true, Position = 100)] public new string SourceIdentifier @@ -24,6 +23,7 @@ public class RegisterEngineEventCommand : ObjectEventRegistrationBase { return base.SourceIdentifier; } + set { base.SourceIdentifier = value; @@ -31,9 +31,9 @@ public class RegisterEngineEventCommand : ObjectEventRegistrationBase } /// - /// Returns the object that generates events to be monitored + /// Returns the object that generates events to be monitored. /// - protected override Object GetSourceObject() + protected override object GetSourceObject() { // If it's not a forwarded event, the user must specify // an action @@ -42,7 +42,7 @@ protected override Object GetSourceObject() (!(bool)Forward) ) { - ErrorRecord errorRecord = new ErrorRecord( + ErrorRecord errorRecord = new( new ArgumentException(EventingStrings.ActionMandatoryForLocal), "ACTION_MANDATORY_FOR_LOCAL", ErrorCategory.InvalidArgument, @@ -55,11 +55,11 @@ protected override Object GetSourceObject() } /// - /// Returns the event name to be monitored on the input object + /// Returns the event name to be monitored on the input object. /// - protected override String GetSourceObjectEventName() + protected override string GetSourceObjectEventName() { return null; } } -} \ No newline at end of file +} diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Remove-PSBreakpoint.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Remove-PSBreakpoint.cs index bd55d3fddd0..de324b33fb8 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Remove-PSBreakpoint.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Remove-PSBreakpoint.cs @@ -1,24 +1,27 @@ -// -// Copyright (C) Microsoft. All rights reserved. -// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. using System.Management.Automation; namespace Microsoft.PowerShell.Commands { /// - /// This class implements Remove-PSBreakpoint + /// This class implements Remove-PSBreakpoint. /// - [Cmdlet(VerbsCommon.Remove, "PSBreakpoint", SupportsShouldProcess = true, DefaultParameterSetName = "Breakpoint", - HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113375")] - public class RemovePSBreakpointCommand : PSBreakpointCommandBase + [Cmdlet(VerbsCommon.Remove, "PSBreakpoint", SupportsShouldProcess = true, DefaultParameterSetName = BreakpointParameterSetName, + HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2097134")] + public class RemovePSBreakpointCommand : PSBreakpointUpdaterCommandBase { + #region overrides + /// - /// Removes the given breakpoint + /// Removes the given breakpoint. /// protected override void ProcessBreakpoint(Breakpoint breakpoint) { - this.Context.Debugger.RemoveBreakpoint(breakpoint); + Runspace.Debugger.RemoveBreakpoint(breakpoint); } + + #endregion overrides } -} \ No newline at end of file +} diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/RemoveAliasCommand.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/RemoveAliasCommand.cs new file mode 100644 index 00000000000..15c48efd847 --- /dev/null +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/RemoveAliasCommand.cs @@ -0,0 +1,73 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.Management.Automation; +using System.Management.Automation.Internal; + +namespace Microsoft.PowerShell.Commands +{ + /// + /// The implementation of the "Remove-Alias" cmdlet. + /// + [Cmdlet(VerbsCommon.Remove, "Alias", DefaultParameterSetName = "Default", HelpUri = "https://go.microsoft.com/fwlink/?linkid=2097127")] + [Alias("ral")] + public class RemoveAliasCommand : PSCmdlet + { + #region Parameters + + /// + /// The alias name to remove. + /// + [Parameter(Position = 0, Mandatory = true, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true)] + public string[] Name { get; set; } + + /// + /// The scope parameter for the command determines which scope the alias is removed from. + /// + [Parameter] + [ArgumentCompleter(typeof(ScopeArgumentCompleter))] + public string Scope { get; set; } + + /// + /// If set to true and an existing alias of the same name exists + /// and is ReadOnly, it will still be deleted. + /// + [Parameter] + public SwitchParameter Force { get; set; } + + #endregion Parameters + + #region Command code + + /// + /// The main processing loop of the command. + /// + protected override void ProcessRecord() + { + foreach (string aliasName in Name) + { + AliasInfo existingAlias = null; + if (string.IsNullOrEmpty(Scope)) + { + existingAlias = SessionState.Internal.GetAlias(aliasName); + } + else + { + existingAlias = SessionState.Internal.GetAliasAtScope(aliasName, Scope); + } + + if (existingAlias != null) + { + SessionState.Internal.RemoveAlias(aliasName, Force); + } + else + { + ItemNotFoundException notAliasFound = new(StringUtil.Format(AliasCommandStrings.NoAliasFound, "name", aliasName)); + ErrorRecord error = new(notAliasFound, "ItemNotFoundException", ErrorCategory.ObjectNotFound, aliasName); + WriteError(error); + } + } + } + #endregion Command code + } +} diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/RemoveEventCommand.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/RemoveEventCommand.cs index 381ebde3fa4..4b8ade4a94a 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/RemoveEventCommand.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/RemoveEventCommand.cs @@ -1,6 +1,5 @@ -// -// Copyright (C) Microsoft. All rights reserved. -// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. using System; using System.Management.Automation; @@ -10,13 +9,13 @@ namespace Microsoft.PowerShell.Commands /// /// Removes an event from the event queue. /// - [Cmdlet(VerbsCommon.Remove, "Event", SupportsShouldProcess = true, DefaultParameterSetName = "BySource", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=135247")] + [Cmdlet(VerbsCommon.Remove, "Event", SupportsShouldProcess = true, DefaultParameterSetName = "BySource", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2096715")] public class RemoveEventCommand : PSCmdlet { #region parameters /// - /// A source identifier for this event subscription + /// A source identifier for this event subscription. /// [Parameter(Mandatory = true, Position = 0, ParameterSetName = "BySource")] public string SourceIdentifier @@ -25,6 +24,7 @@ public string SourceIdentifier { return _sourceIdentifier; } + set { _sourceIdentifier = value; @@ -35,10 +35,11 @@ public string SourceIdentifier } } } + private string _sourceIdentifier = null; /// - /// An identifier for this event subscription + /// An identifier for this event subscription. /// [Parameter(Mandatory = true, Position = 0, ValueFromPipelineByPropertyName = true, ParameterSetName = "ByIdentifier")] public int EventIdentifier @@ -47,11 +48,13 @@ public int EventIdentifier { return _eventIdentifier; } + set { _eventIdentifier = value; } } + private int _eventIdentifier = -1; #endregion parameters @@ -59,7 +62,7 @@ public int EventIdentifier private WildcardPattern _matchPattern; /// - /// Remove the event from the queue + /// Remove the event from the queue. /// protected override void ProcessRecord() { @@ -91,7 +94,7 @@ protected override void ProcessRecord() foundMatch = true; if (ShouldProcess( - String.Format( + string.Format( System.Globalization.CultureInfo.CurrentCulture, EventingStrings.EventResource, currentEvent.SourceIdentifier), @@ -108,9 +111,9 @@ protected override void ProcessRecord() (!WildcardPattern.ContainsWildcardCharacters(_sourceIdentifier)) && (!foundMatch)) { - ErrorRecord errorRecord = new ErrorRecord( + ErrorRecord errorRecord = new( new ArgumentException( - String.Format( + string.Format( System.Globalization.CultureInfo.CurrentCulture, EventingStrings.SourceIdentifierNotFound, _sourceIdentifier)), "INVALID_SOURCE_IDENTIFIER", @@ -121,9 +124,9 @@ protected override void ProcessRecord() } else if ((_eventIdentifier >= 0) && (!foundMatch)) { - ErrorRecord errorRecord = new ErrorRecord( + ErrorRecord errorRecord = new( new ArgumentException( - String.Format( + string.Format( System.Globalization.CultureInfo.CurrentCulture, EventingStrings.EventIdentifierNotFound, _eventIdentifier)), "INVALID_EVENT_IDENTIFIER", @@ -134,4 +137,4 @@ protected override void ProcessRecord() } } } -} \ No newline at end of file +} diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/RunspaceAttribute.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/RunspaceAttribute.cs new file mode 100644 index 00000000000..696813449cb --- /dev/null +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/RunspaceAttribute.cs @@ -0,0 +1,97 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#pragma warning disable 1634, 1691 +#pragma warning disable 56506 + +using Microsoft.PowerShell.Commands; + +namespace System.Management.Automation.Runspaces +{ + /// + /// Defines the attribute used to designate a cmdlet parameter as one that + /// should accept runspaces. + /// + [AttributeUsage(AttributeTargets.Field | AttributeTargets.Property, AllowMultiple = false)] + public sealed class RunspaceAttribute : ArgumentTransformationAttribute + { + /// + /// Transforms the input data to a Runspace. + /// + /// + /// The engine APIs for the context under which the transformation is being + /// made. + /// + /// + /// If a string, the transformation uses the input as the runspace name. + /// If an int, the transformation uses the input as the runspace ID. + /// If a guid, the transformation uses the input as the runspace GUID. + /// If already a Runspace, the transform does nothing. + /// + /// A runspace object representing the inputData. + public override object Transform(EngineIntrinsics engineIntrinsics, object inputData) + { + if (engineIntrinsics?.Host?.UI == null) + { + throw PSTraceSource.NewArgumentNullException("engineIntrinsics"); + } + + if (inputData == null) + { + return null; + } + + // Try to coerce the input as a runspace + Runspace runspace = LanguagePrimitives.FromObjectAs(inputData); + if (runspace != null) + { + return runspace; + } + + // Try to coerce the runspace if the user provided a string, int, or guid + switch (inputData) + { + case string name: + var runspacesByName = GetRunspaceUtils.GetRunspacesByName(new[] { name }); + if (runspacesByName.Count == 1) + { + return runspacesByName[0]; + } + + break; + + case int id: + var runspacesById = GetRunspaceUtils.GetRunspacesById(new[] { id }); + if (runspacesById.Count == 1) + { + return runspacesById[0]; + } + + break; + + case Guid guid: + var runspacesByGuid = GetRunspaceUtils.GetRunspacesByInstanceId(new[] { guid }); + if (runspacesByGuid.Count == 1) + { + return runspacesByGuid[0]; + } + + break; + + default: + // Non-convertible type + break; + } + + // If we couldn't get a single runspace, return the inputData + return inputData; + } + + /// + /// Gets a flag indicating whether or not null optional parameters are transformed. + /// + public override bool TransformNullOptionalParameters { get { return false; } } + } +} + +#pragma warning restore 56506 diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Select-Object.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Select-Object.cs new file mode 100644 index 00000000000..e0b0bff07c5 --- /dev/null +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Select-Object.cs @@ -0,0 +1,837 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Management.Automation; +using System.Management.Automation.Internal; + +using Microsoft.PowerShell.Commands.Internal.Format; + +namespace Microsoft.PowerShell.Commands +{ + internal sealed class SelectObjectExpressionParameterDefinition : CommandParameterDefinition + { + protected override void SetEntries() + { + this.hashEntries.Add(new ExpressionEntryDefinition()); + this.hashEntries.Add(new NameEntryDefinition()); + } + } + + /// + /// + [Cmdlet(VerbsCommon.Select, "Object", DefaultParameterSetName = "DefaultParameter", + HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2096716", RemotingCapability = RemotingCapability.None)] + public sealed class SelectObjectCommand : PSCmdlet + { + #region Command Line Switches + + /// + /// + /// + [Parameter(ValueFromPipeline = true)] + public PSObject InputObject { get; set; } = AutomationNull.Value; + + /// + /// + /// + [Parameter(Position = 0, ParameterSetName = "DefaultParameter")] + [Parameter(Position = 0, ParameterSetName = "SkipLastParameter")] + public object[] Property { get; set; } + + /// + /// + /// + [Parameter(ParameterSetName = "DefaultParameter")] + [Parameter(ParameterSetName = "SkipLastParameter")] + public string[] ExcludeProperty { get; set; } + + /// + /// + /// + [Parameter(ParameterSetName = "DefaultParameter")] + [Parameter(ParameterSetName = "SkipLastParameter")] + public string ExpandProperty { get; set; } + + /// + /// + /// + [Parameter] + public SwitchParameter Unique + { + get { return _unique; } + + set { _unique = value; } + } + + private bool _unique; + + /// + /// Gets or sets case insensitive switch for string comparison. + /// Used in combination with Unique switch parameter. + /// + [Parameter] + public SwitchParameter CaseInsensitive { get; set; } + + /// + /// + /// + [Parameter(ParameterSetName = "DefaultParameter")] + // NTRAID#Windows Out Of Band Releases-927878-2006/03/02 + // Allow zero + [ValidateRange(0, int.MaxValue)] + public int Last + { + get { return _last; } + + set { _last = value; _firstOrLastSpecified = true; } + } + + private int _last = 0; + + /// + /// + /// + [Parameter(ParameterSetName = "DefaultParameter")] + // NTRAID#Windows Out Of Band Releases-927878-2006/03/02 + // Allow zero + [ValidateRange(0, int.MaxValue)] + public int First + { + get { return _first; } + + set { _first = value; _firstOrLastSpecified = true; } + } + + private int _first = 0; + private bool _firstOrLastSpecified; + + /// + /// Skips the specified number of items from top when used with First, from end when used with Last or SkipLast. + /// + /// + [Parameter(ParameterSetName = "DefaultParameter")] + [Parameter(ParameterSetName = "SkipLastParameter")] + [ValidateRange(0, int.MaxValue)] + public int Skip { get; set; } + + /// + /// Skip the specified number of items from end. + /// + [Parameter(ParameterSetName = "SkipLastParameter")] + [ValidateRange(0, int.MaxValue)] + public int SkipLast { get; set; } + + /// + /// With this switch present, the cmdlet won't "short-circuit" + /// (i.e. won't stop upstream cmdlets after it knows that no further objects will be emitted downstream). + /// + [Parameter(ParameterSetName = "DefaultParameter")] + [Parameter(ParameterSetName = "IndexParameter")] + public SwitchParameter Wait { get; set; } + + /// + /// Used to display the object at the specified index. + /// + /// + [Parameter(ParameterSetName = "IndexParameter")] + [ValidateRange(0, int.MaxValue)] + [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] + public int[] Index + { + get + { + return _index; + } + + set + { + _index = value; + _indexSpecified = true; + _isIncludeIndex = true; + Array.Sort(_index); + } + } + + /// + /// Used to display all objects at the specified indices. + /// + /// + [Parameter(ParameterSetName = "SkipIndexParameter")] + [ValidateRange(0, int.MaxValue)] + [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] + public int[] SkipIndex + { + get + { + return _index; + } + + set + { + _index = value; + _indexSpecified = true; + _isIncludeIndex = false; + Array.Sort(_index); + } + } + + private int[] _index; + private bool _indexSpecified; + private bool _isIncludeIndex; + + #endregion + + private SelectObjectQueue _selectObjectQueue; + + private sealed class SelectObjectQueue : Queue + { + internal SelectObjectQueue(int first, int last, int skip, int skipLast, bool firstOrLastSpecified) + { + _first = first; + _last = last; + _skip = skip; + _skipLast = skipLast; + _firstOrLastSpecified = firstOrLastSpecified; + } + + public bool AllRequestedObjectsProcessed + { + get + { + return _firstOrLastSpecified && _last == 0 && _first != 0 && _streamedObjectCount >= _first; + } + } + + public new void Enqueue(PSObject obj) + { + if (_last > 0 && this.Count >= (_last + _skip) && _first == 0) + { + base.Dequeue(); + } + else if (_last > 0 && this.Count >= _last && _first != 0) + { + base.Dequeue(); + } + + base.Enqueue(obj); + } + + public PSObject StreamingDequeue() + { + // if skip parameter is not mentioned or there are no more objects to skip + if (_skip == 0) + { + if (_skipLast > 0) + { + // We are going to skip some items from end, but it's okay to process + // the early input objects once we have more items in queue than the + // specified 'skipLast' value. + if (this.Count > _skipLast) + { + return Dequeue(); + } + } + else + { + if (_streamedObjectCount < _first || !_firstOrLastSpecified) + { + Diagnostics.Assert(this.Count > 0, "Streaming an empty queue"); + _streamedObjectCount++; + return Dequeue(); + } + + if (_last == 0) + { + Dequeue(); + } + } + } + else + { + // if last parameter is not mentioned,remove the objects and decrement the skip + if (_last == 0) + { + Dequeue(); + _skip--; + } + else if (_first != 0) + { + _skip--; + Dequeue(); + } + } + + return null; + } + + private int _streamedObjectCount; + private readonly int _first; + private readonly int _last; + private int _skip; + private readonly int _skipLast; + private readonly bool _firstOrLastSpecified; + } + + /// + /// List of processed parameters obtained from the Expression array. + /// + private List _propertyMshParameterList; + + /// + /// Singleton list of process parameters obtained from ExpandProperty. + /// + private List _expandMshParameterList; + + private PSPropertyExpressionFilter _exclusionFilter; + + private sealed class UniquePSObjectHelper + { + internal UniquePSObjectHelper(PSObject o, int notePropertyCount) + { + WrittenObject = o; + NotePropertyCount = notePropertyCount; + } + + internal readonly PSObject WrittenObject; + + internal int NotePropertyCount { get; } + } + + private List _uniques = null; + + private void ProcessExpressionParameter() + { + TerminatingErrorContext invocationContext = new(this); + ParameterProcessor processor = + new(new SelectObjectExpressionParameterDefinition()); + if ((Property != null) && (Property.Length != 0)) + { + // Build property list taking into account the wildcards and @{name=;expression=} + + _propertyMshParameterList = processor.ProcessParameters(Property, invocationContext); + } + else + { + // Property don't exist + _propertyMshParameterList = new List(); + } + + if (!string.IsNullOrEmpty(ExpandProperty)) + { + _expandMshParameterList = processor.ProcessParameters(new string[] { ExpandProperty }, invocationContext); + } + + if (ExcludeProperty != null) + { + _exclusionFilter = new PSPropertyExpressionFilter(ExcludeProperty); + // ExcludeProperty implies -Property * for better UX + if ((Property == null) || (Property.Length == 0)) + { + Property = new object[] { "*" }; + _propertyMshParameterList = processor.ProcessParameters(Property, invocationContext); + } + } + } + + private void ProcessObject(PSObject inputObject) + { + if ((Property == null || Property.Length == 0) && string.IsNullOrEmpty(ExpandProperty)) + { + FilteredWriteObject(inputObject, new List()); + return; + } + + // If property parameter is mentioned + List matchedProperties = new(); + foreach (MshParameter p in _propertyMshParameterList) + { + ProcessParameter(p, inputObject, matchedProperties); + } + + if (string.IsNullOrEmpty(ExpandProperty)) + { + PSObject result = new(); + if (matchedProperties.Count != 0) + { + HashSet propertyNames = new(StringComparer.OrdinalIgnoreCase); + + foreach (PSNoteProperty noteProperty in matchedProperties) + { + try + { + if (!propertyNames.Contains(noteProperty.Name)) + { + propertyNames.Add(noteProperty.Name); + result.Properties.Add(noteProperty); + } + else + { + WriteAlreadyExistingPropertyError(noteProperty.Name, inputObject, + "AlreadyExistingUserSpecifiedPropertyNoExpand"); + } + } + catch (ExtendedTypeSystemException) + { + WriteAlreadyExistingPropertyError(noteProperty.Name, inputObject, + "AlreadyExistingUserSpecifiedPropertyNoExpand"); + } + } + } + + FilteredWriteObject(result, matchedProperties); + } + else + { + ProcessExpandParameter(_expandMshParameterList[0], inputObject, matchedProperties); + } + } + + private void ProcessParameter(MshParameter p, PSObject inputObject, List result) + { + string name = p.GetEntry(NameEntryDefinition.NameEntryKey) as string; + + PSPropertyExpression ex = p.GetEntry(FormatParameterDefinitionKeys.ExpressionEntryKey) as PSPropertyExpression; + List expressionResults = new(); + foreach (PSPropertyExpression resolvedName in ex.ResolveNames(inputObject)) + { + if (_exclusionFilter == null || !_exclusionFilter.IsMatch(resolvedName)) + { + List tempExprResults = resolvedName.GetValues(inputObject); + if (tempExprResults == null) + { + continue; + } + + foreach (PSPropertyExpressionResult mshExpRes in tempExprResults) + { + expressionResults.Add(mshExpRes); + } + } + } + + // allow 'Select-Object -Property noexist-name' to return a PSObject with property noexist-name, + // unless noexist-name itself contains wildcards + if (expressionResults.Count == 0 && !ex.HasWildCardCharacters) + { + expressionResults.Add(new PSPropertyExpressionResult(null, ex, null)); + } + + // if we have an expansion, renaming is not acceptable + else if (!string.IsNullOrEmpty(name) && expressionResults.Count > 1) + { + string errorMsg = SelectObjectStrings.RenamingMultipleResults; + ErrorRecord errorRecord = new( + new InvalidOperationException(errorMsg), + "RenamingMultipleResults", + ErrorCategory.InvalidOperation, + inputObject); + WriteError(errorRecord); + return; + } + + foreach (PSPropertyExpressionResult r in expressionResults) + { + // filter the exclusions, if any + if (_exclusionFilter != null && _exclusionFilter.IsMatch(r.ResolvedExpression)) + continue; + + PSNoteProperty mshProp; + if (string.IsNullOrEmpty(name)) + { + string resolvedExpressionName = r.ResolvedExpression.ToString(); + if (string.IsNullOrEmpty(resolvedExpressionName)) + { + PSArgumentException mshArgE = PSTraceSource.NewArgumentException( + "Property", + SelectObjectStrings.EmptyScriptBlockAndNoName); + ThrowTerminatingError( + new ErrorRecord( + mshArgE, + "EmptyScriptBlockAndNoName", + ErrorCategory.InvalidArgument, null)); + } + + mshProp = new PSNoteProperty(resolvedExpressionName, r.Result); + } + else + { + mshProp = new PSNoteProperty(name, r.Result); + } + + result.Add(mshProp); + } + } + + private void ProcessExpandParameter(MshParameter p, PSObject inputObject, + List matchedProperties) + { + PSPropertyExpression ex = p.GetEntry(FormatParameterDefinitionKeys.ExpressionEntryKey) as PSPropertyExpression; + List expressionResults = ex.GetValues(inputObject); + + if (expressionResults.Count == 0) + { + ErrorRecord errorRecord = new( + PSTraceSource.NewArgumentException("ExpandProperty", SelectObjectStrings.PropertyNotFound, ExpandProperty), + "ExpandPropertyNotFound", + ErrorCategory.InvalidArgument, + inputObject); + throw new SelectObjectException(errorRecord); + } + + if (expressionResults.Count > 1) + { + ErrorRecord errorRecord = new( + PSTraceSource.NewArgumentException("ExpandProperty", SelectObjectStrings.MutlipleExpandProperties, ExpandProperty), + "MutlipleExpandProperties", + ErrorCategory.InvalidArgument, + inputObject); + throw new SelectObjectException(errorRecord); + } + + PSPropertyExpressionResult r = expressionResults[0]; + if (r.Exception == null) + { + // ignore the property value if it's null + if (r.Result == null) + { + return; + } + + System.Collections.IEnumerable results = LanguagePrimitives.GetEnumerable(r.Result); + if (results == null) + { + // add NoteProperties if there is any + // If r.Result is a base object, we don't want to associate the NoteProperty + // directly with it. We want the NoteProperty to be associated only with this + // particular PSObject, so that when the user uses the base object else where, + // its members remain the same as before the Select-Object command run. + PSObject expandedObject = PSObject.AsPSObject(r.Result, true); + AddNoteProperties(expandedObject, inputObject, matchedProperties); + + FilteredWriteObject(expandedObject, matchedProperties); + return; + } + + foreach (object expandedValue in results) + { + // ignore the element if it's null + if (expandedValue == null) + { + continue; + } + + // add NoteProperties if there is any + // If expandedValue is a base object, we don't want to associate the NoteProperty + // directly with it. We want the NoteProperty to be associated only with this + // particular PSObject, so that when the user uses the base object else where, + // its members remain the same as before the Select-Object command run. + PSObject expandedObject = PSObject.AsPSObject(expandedValue, true); + AddNoteProperties(expandedObject, inputObject, matchedProperties); + + FilteredWriteObject(expandedObject, matchedProperties); + } + } + else + { + ErrorRecord errorRecord = new( + r.Exception, + "PropertyEvaluationExpand", + ErrorCategory.InvalidResult, + inputObject); + throw new SelectObjectException(errorRecord); + } + } + + private void AddNoteProperties(PSObject expandedObject, PSObject inputObject, IEnumerable matchedProperties) + { + foreach (PSNoteProperty noteProperty in matchedProperties) + { + try + { + if (expandedObject.Properties[noteProperty.Name] != null) + { + WriteAlreadyExistingPropertyError(noteProperty.Name, inputObject, "AlreadyExistingUserSpecifiedPropertyExpand"); + } + else + { + expandedObject.Properties.Add(noteProperty); + } + } + catch (ExtendedTypeSystemException) + { + WriteAlreadyExistingPropertyError(noteProperty.Name, inputObject, "AlreadyExistingUserSpecifiedPropertyExpand"); + } + } + } + + private void WriteAlreadyExistingPropertyError(string name, object inputObject, string errorId) + { + ErrorRecord errorRecord = new( + PSTraceSource.NewArgumentException("Property", SelectObjectStrings.AlreadyExistingProperty, name), + errorId, + ErrorCategory.InvalidOperation, + inputObject); + WriteError(errorRecord); + } + + private void FilteredWriteObject(PSObject obj, List addedNoteProperties) + { + Diagnostics.Assert(obj != null, "This command should never write null"); + + if (!_unique) + { + if (obj != AutomationNull.Value) + { + SetPSCustomObject(obj, newPSObject: addedNoteProperties.Count > 0); + WriteObject(obj); + } + + return; + } + // if only unique is mentioned + else if ((_unique)) + { + bool isObjUnique = true; + foreach (UniquePSObjectHelper uniqueObj in _uniques) + { + ObjectCommandComparer comparer = new( + ascending: true, + CultureInfo.CurrentCulture, + caseSensitive: !CaseInsensitive.IsPresent); + + if ((comparer.Compare(obj.BaseObject, uniqueObj.WrittenObject.BaseObject) == 0) && + (uniqueObj.NotePropertyCount == addedNoteProperties.Count)) + { + bool found = true; + foreach (PSNoteProperty note in addedNoteProperties) + { + PSMemberInfo prop = uniqueObj.WrittenObject.Properties[note.Name]; + if (prop == null || comparer.Compare(prop.Value, note.Value) != 0) + { + found = false; + break; + } + } + + if (found) + { + isObjUnique = false; + break; + } + } + else + { + continue; + } + } + + if (isObjUnique) + { + SetPSCustomObject(obj, newPSObject: addedNoteProperties.Count > 0); + _uniques.Add(new UniquePSObjectHelper(obj, addedNoteProperties.Count)); + } + } + } + + private void SetPSCustomObject(PSObject psObj, bool newPSObject) + { + if (psObj.ImmediateBaseObject is PSCustomObject) + { + var typeName = "Selected." + InputObject.BaseObject.GetType().ToString(); + if (newPSObject || !psObj.TypeNames.Contains(typeName)) + { + psObj.TypeNames.Insert(0, typeName); + } + } + } + + private void ProcessObjectAndHandleErrors(PSObject pso) + { + Diagnostics.Assert(pso != null, "Caller should verify pso != null"); + + try + { + ProcessObject(pso); + } + catch (SelectObjectException e) + { + WriteError(e.ErrorRecord); + } + } + + /// + /// + protected override void BeginProcessing() + { + ProcessExpressionParameter(); + + if (_unique) + { + _uniques = new List(); + } + + _selectObjectQueue = new SelectObjectQueue(_first, _last, Skip, SkipLast, _firstOrLastSpecified); + } + + /// + /// Handles processing of InputObject. + /// + protected override void ProcessRecord() + { + if (InputObject != AutomationNull.Value && InputObject != null) + { + if (_indexSpecified) + { + ProcessIndexed(); + } + else + { + _selectObjectQueue.Enqueue(InputObject); + PSObject streamingInputObject = _selectObjectQueue.StreamingDequeue(); + if (streamingInputObject != null) + { + ProcessObjectAndHandleErrors(streamingInputObject); + } + + if (_selectObjectQueue.AllRequestedObjectsProcessed && !this.Wait) + { + this.EndProcessing(); + throw new StopUpstreamCommandsException(this); + } + } + } + } + + /// + /// The index of the active index filter. + /// + private int _currentFilterIndex; + + /// + /// The index of the object being processed. + /// + private int _currentObjectIndex; + + /// + /// Handles processing of InputObject if -Index or -SkipIndex is specified. + /// + private void ProcessIndexed() + { + if (_isIncludeIndex) + { + if (_currentFilterIndex < _index.Length) + { + int nextIndexToOutput = _index[_currentFilterIndex]; + if (_currentObjectIndex == nextIndexToOutput) + { + ProcessObjectAndHandleErrors(InputObject); + while ((_currentFilterIndex < _index.Length) && (_index[_currentFilterIndex] == nextIndexToOutput)) + { + _currentFilterIndex++; + } + } + } + + if (!Wait && _currentFilterIndex >= _index.Length) + { + EndProcessing(); + throw new StopUpstreamCommandsException(this); + } + + _currentObjectIndex++; + } + else + { + if (_currentFilterIndex < _index.Length) + { + int nextIndexToSkip = _index[_currentFilterIndex]; + if (_currentObjectIndex != nextIndexToSkip) + { + ProcessObjectAndHandleErrors(InputObject); + } + else + { + while ((_currentFilterIndex < _index.Length) && (_index[_currentFilterIndex] == nextIndexToSkip)) + { + _currentFilterIndex++; + } + } + } + else + { + ProcessObjectAndHandleErrors(InputObject); + } + + _currentObjectIndex++; + } + } + + /// + /// Completes the processing of Input. + /// + protected override void EndProcessing() + { + // We can skip this part for 'IndexParameter' and 'SkipLastParameter' sets because: + // 1. 'IndexParameter' set doesn't use selectObjectQueue. + // 2. 'SkipLastParameter' set should have processed all valid input in the ProcessRecord. + if (ParameterSetName == "DefaultParameter") + { + if (_first != 0) + { + while ((_selectObjectQueue.Count > 0)) + { + ProcessObjectAndHandleErrors(_selectObjectQueue.Dequeue()); + } + } + else + { + while ((_selectObjectQueue.Count > 0)) + { + int lenQueue = _selectObjectQueue.Count; + if (lenQueue > Skip) + { + ProcessObjectAndHandleErrors(_selectObjectQueue.Dequeue()); + } + else + { + break; + } + } + } + } + + if (_uniques != null) + { + foreach (UniquePSObjectHelper obj in _uniques) + { + if (obj.WrittenObject == null || obj.WrittenObject == AutomationNull.Value) + { + continue; + } + + WriteObject(obj.WrittenObject); + } + } + } + } + + /// + /// Used only internally for select-object. + /// + [SuppressMessage("Microsoft.Usage", "CA2237:MarkISerializableTypesWithSerializable", Justification = "This exception is internal and never thrown by any public API")] + [SuppressMessage("Microsoft.Design", "CA1032:ImplementStandardExceptionConstructors", Justification = "This exception is internal and never thrown by any public API")] + [SuppressMessage("Microsoft.Design", "CA1064:ExceptionsShouldBePublic", Justification = "This exception is internal and never thrown by any public API")] + internal sealed class SelectObjectException : SystemException + { + internal ErrorRecord ErrorRecord { get; } + + internal SelectObjectException(ErrorRecord errorRecord) + { + ErrorRecord = errorRecord; + } + } +} diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Send-MailMessage.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Send-MailMessage.cs index 13ed7e00f1a..2598d953496 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Send-MailMessage.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Send-MailMessage.cs @@ -1,289 +1,188 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. using System; -using System.Text; -using System.Globalization; -using System.Net.Mail; using System.Diagnostics.CodeAnalysis; +using System.Globalization; using System.Management.Automation; - +using System.Net.Mail; +using System.Text; namespace Microsoft.PowerShell.Commands { #region SendMailMessage /// - /// implementation for the Send-MailMessage command + /// Implementation for the Send-MailMessage command. /// - [Cmdlet(VerbsCommunications.Send, "MailMessage", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=135256")] + [Obsolete("This cmdlet does not guarantee secure connections to SMTP servers. While there is no immediate replacement available in PowerShell, we recommend you do not use Send-MailMessage at this time. See https://aka.ms/SendMailMessage for more information.")] + [Cmdlet(VerbsCommunications.Send, "MailMessage", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2097115")] public sealed class SendMailMessage : PSCmdlet { #region Command Line Parameters /// - /// Specifies the files names to be attached to the email. + /// Gets or sets the files names to be attached to the email. /// If the filename specified can not be found, then the relevant error /// message should be thrown. /// - [Parameter(ValueFromPipeline = true)] + [Parameter(ValueFromPipeline = true, ValueFromPipelineByPropertyName = true)] [ValidateNotNullOrEmpty] [Alias("PsPath")] [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] - public String[] Attachments - { - get { return _attachments; } - set - { - _attachments = value; - } - } - private String[] _attachments; + public string[] Attachments { get; set; } /// - /// Specifies the address collection that contains the + /// Gets or sets the address collection that contains the /// blind carbon copy (BCC) recipients for the e-mail message. /// - [Parameter] + [Parameter(ValueFromPipelineByPropertyName = true)] [ValidateNotNullOrEmpty] [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] - public String[] Bcc - { - get { return _bcc; } - set - { - _bcc = value; - } - } - private String[] _bcc; + public string[] Bcc { get; set; } /// - /// Specifies the body (content) of the message + /// Gets or sets the body (content) of the message. /// - [Parameter(Position = 2)] + [Parameter(Position = 2, ValueFromPipelineByPropertyName = true)] [ValidateNotNullOrEmpty] - public String Body - { - get { return _body; } - set - { - _body = value; - } - } - private String _body; + public string Body { get; set; } /// - /// Specifies a value indicating whether the mail message body is in Html. + /// Gets or sets the value indicating whether the mail message body is in Html. /// - [Parameter] + [Parameter(ValueFromPipelineByPropertyName = true)] [Alias("BAH")] - public SwitchParameter BodyAsHtml - { - get { return _bodyashtml; } - set - { - _bodyashtml = value; - } - } - private SwitchParameter _bodyashtml; + public SwitchParameter BodyAsHtml { get; set; } /// - /// Specifies the encoding used for the content of the body and also the subject. + /// Gets or sets the encoding used for the content of the body and also the subject. + /// This is set to ASCII to ensure there are no problems with any email server. /// - [Parameter()] + [Parameter(ValueFromPipelineByPropertyName = true)] [Alias("BE")] [ValidateNotNullOrEmpty] - [ArgumentToEncodingNameTransformationAttribute()] + [ArgumentEncodingCompletions] + [ArgumentToEncodingTransformation] public Encoding Encoding { - get { return _encoding; } + get + { + return _encoding; + } + set { + EncodingConversion.WarnIfObsolete(this, value); _encoding = value; } } - private Encoding _encoding = new ASCIIEncoding(); + + private Encoding _encoding = Encoding.ASCII; /// - /// Specifies the address collection that contains the + /// Gets or sets the address collection that contains the /// carbon copy (CC) recipients for the e-mail message. /// - [Parameter] + [Parameter(ValueFromPipelineByPropertyName = true)] [ValidateNotNullOrEmpty] [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "Cc")] [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] - public String[] Cc - { - get { return _cc; } - set - { - _cc = value; - } - } - private String[] _cc; + public string[] Cc { get; set; } /// - /// Specifies the delivery notifications options for the e-mail message. The various - /// option available for this parameter are None, OnSuccess, OnFailure, Delay and Never + /// Gets or sets the delivery notifications options for the e-mail message. The various + /// options available for this parameter are None, OnSuccess, OnFailure, Delay and Never. /// - [Parameter()] + [Parameter(ValueFromPipelineByPropertyName = true)] [Alias("DNO")] [ValidateNotNullOrEmpty] - public DeliveryNotificationOptions DeliveryNotificationOption - { - get { return _deliverynotification; } - set - { - _deliverynotification = value; - } - } - private DeliveryNotificationOptions _deliverynotification; + public DeliveryNotificationOptions DeliveryNotificationOption { get; set; } /// - /// Specifies the from address for this e-mail message. The default value for - /// this parameter is the email address of the currently logged on user + /// Gets or sets the from address for this e-mail message. The default value for + /// this parameter is the email address of the currently logged on user. /// - [Parameter(Mandatory = true)] + [Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true)] [ValidateNotNullOrEmpty] - public String From - { - get { return _from; } - set - { - _from = value; - } - } - private String _from; + public string From { get; set; } /// - /// Specifies the name of the Host used to send the email. This host name will be assigned - /// to the Powershell variable PSEmailServer,if this host can not reached an appropriate error + /// Gets or sets the name of the Host used to send the email. This host name will be assigned + /// to the Powershell variable PSEmailServer, if this host can not reached an appropriate error. /// message will be displayed. /// - [Parameter(Position = 3)] + [Parameter(Position = 3, ValueFromPipelineByPropertyName = true)] [Alias("ComputerName")] [ValidateNotNullOrEmpty] - public String SmtpServer - { - get { return _smtpserver; } - set - { - _smtpserver = value; - } - } - private String _smtpserver; + public string SmtpServer { get; set; } /// - /// Specifies the priority of the email message. The valid values for this are Normal, High and Low + /// Gets or sets the priority of the email message. The valid values for this are Normal, High and Low. /// - [Parameter] + [Parameter(ValueFromPipelineByPropertyName = true)] [ValidateNotNullOrEmpty] - public MailPriority Priority - { - get { return _priority; } - set - { - _priority = value; - } - } - private MailPriority _priority; + public MailPriority Priority { get; set; } /// - /// Specifies the subject of the email message. + /// Gets or sets the Reply-To field for this e-mail message. /// - [Parameter(Mandatory = true, Position = 1)] - [Alias("sub")] - [ValidateNotNullOrEmpty] - public String Subject - { - get { return _subject; } - set - { - _subject = value; - } - } - private String _subject; + [Parameter(ValueFromPipelineByPropertyName = true)] + public string[] ReplyTo { get; set; } + /// + /// Gets or sets the subject of the email message. + /// + [Parameter(Mandatory = false, Position = 1, ValueFromPipelineByPropertyName = true)] + [Alias("sub")] + public string Subject { get; set; } /// - /// Specifies the To address for this e-mail message. + /// Gets or sets the To address for this e-mail message. /// - [Parameter(Mandatory = true, Position = 0)] + [Parameter(Mandatory = true, Position = 0, ValueFromPipelineByPropertyName = true)] [ValidateNotNullOrEmpty] [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] - public String[] To - { - get { return _to; } - set - { - _to = value; - } - } - private String[] _to; + public string[] To { get; set; } /// - /// Specifies the credential for this e-mail message. + /// Gets or sets the credential for this e-mail message. /// - [Parameter()] + [Parameter(ValueFromPipelineByPropertyName = true)] [Credential] [ValidateNotNullOrEmpty] - public PSCredential Credential - { - get { return _credential; } - set - { - _credential = value; - } - } - private PSCredential _credential; + public PSCredential Credential { get; set; } /// - /// Specifies if Secured layer is required or not + /// Gets or sets if Secured layer is required or not. /// - [Parameter()] - public SwitchParameter UseSsl - { - get { return _usessl; } - set - { - _usessl = value; - } - } - private SwitchParameter _usessl; + [Parameter(ValueFromPipelineByPropertyName = true)] + public SwitchParameter UseSsl { get; set; } /// - /// Specifies the Port to be used on the server. + /// Gets or sets the Port to be used on the server. /// /// /// Value must be greater than zero. /// - [Parameter()] - [ValidateRange(0, Int32.MaxValue)] - public int Port - { - get { return _port; } - set { _port = value; } - } - private int _port = 0; + [Parameter(ValueFromPipelineByPropertyName = true)] + [ValidateRange(0, int.MaxValue)] + public int Port { get; set; } #endregion - - #region private variables and methods - + #region Private variables and methods // Instantiate a new instance of MailMessage - private MailMessage _mMailMessage = new MailMessage(); + private readonly MailMessage _mMailMessage = new(); private SmtpClient _mSmtpClient = null; /// - /// Add the input addresses which are either string or hashtable to the MailMessage - /// It returns true if the from parameter has more than one value + /// Add the input addresses which are either string or hashtable to the MailMessage. + /// It returns true if the from parameter has more than one value. /// /// /// - /// private void AddAddressesToMailMessage(object address, string param) { string[] objEmailAddresses = address as string[]; @@ -308,11 +207,16 @@ private void AddAddressesToMailMessage(object address, string param) _mMailMessage.Bcc.Add(new MailAddress(strEmailAddress)); break; } + case "replyTo": + { + _mMailMessage.ReplyToList.Add(new MailAddress(strEmailAddress)); + break; + } } } catch (FormatException e) { - ErrorRecord er = new ErrorRecord(e, "FormatException", ErrorCategory.InvalidType, null); + ErrorRecord er = new(e, "FormatException", ErrorCategory.InvalidType, null); WriteError(er); continue; } @@ -324,110 +228,111 @@ private void AddAddressesToMailMessage(object address, string param) #region Overrides /// - /// ProcessRecord override + /// BeginProcessing override. /// - protected override - void - BeginProcessing() + protected override void BeginProcessing() { try { // Set the sender address of the mail message - _mMailMessage.From = new MailAddress(_from); + _mMailMessage.From = new MailAddress(From); } catch (FormatException e) { - ErrorRecord er = new ErrorRecord(e, "FormatException", ErrorCategory.InvalidType, _from); + ErrorRecord er = new(e, "FormatException", ErrorCategory.InvalidType, From); ThrowTerminatingError(er); - // return; } // Set the recipient address of the mail message - AddAddressesToMailMessage(_to, "to"); + AddAddressesToMailMessage(To, "to"); // Set the BCC address of the mail message - if (_bcc != null) + if (Bcc != null) { - AddAddressesToMailMessage(_bcc, "bcc"); + AddAddressesToMailMessage(Bcc, "bcc"); } // Set the CC address of the mail message - if (_cc != null) + if (Cc != null) { - AddAddressesToMailMessage(_cc, "cc"); + AddAddressesToMailMessage(Cc, "cc"); } + // Set the Reply-To address of the mail message + if (ReplyTo != null) + { + AddAddressesToMailMessage(ReplyTo, "replyTo"); + } - - //set the delivery notification - _mMailMessage.DeliveryNotificationOptions = _deliverynotification; + // Set the delivery notification + _mMailMessage.DeliveryNotificationOptions = DeliveryNotificationOption; // Set the subject of the mail message - _mMailMessage.Subject = _subject; + _mMailMessage.Subject = Subject; // Set the body of the mail message - _mMailMessage.Body = _body; + _mMailMessage.Body = Body; - //set the subject and body encoding - _mMailMessage.SubjectEncoding = _encoding; - _mMailMessage.BodyEncoding = _encoding; + // Set the subject and body encoding + _mMailMessage.SubjectEncoding = Encoding; + _mMailMessage.BodyEncoding = Encoding; // Set the format of the mail message body as HTML - _mMailMessage.IsBodyHtml = _bodyashtml; + _mMailMessage.IsBodyHtml = BodyAsHtml; // Set the priority of the mail message to normal - _mMailMessage.Priority = _priority; - + _mMailMessage.Priority = Priority; - //get the PowerShell environment variable - //globalEmailServer might be null if it is deleted by: PS> del variable:PSEmailServer + // Get the PowerShell environment variable + // globalEmailServer might be null if it is deleted by: PS> del variable:PSEmailServer PSVariable globalEmailServer = SessionState.Internal.GetVariable(SpecialVariables.PSEmailServer); - if (_smtpserver == null && globalEmailServer != null) + if (SmtpServer == null && globalEmailServer != null) { - _smtpserver = Convert.ToString(globalEmailServer.Value, CultureInfo.InvariantCulture); + SmtpServer = Convert.ToString(globalEmailServer.Value, CultureInfo.InvariantCulture); } - if (string.IsNullOrEmpty(_smtpserver)) + + if (string.IsNullOrEmpty(SmtpServer)) { - ErrorRecord er = new ErrorRecord(new InvalidOperationException(SendMailMessageStrings.HostNameValue), null, ErrorCategory.InvalidArgument, null); + ErrorRecord er = new(new InvalidOperationException(SendMailMessageStrings.HostNameValue), null, ErrorCategory.InvalidArgument, null); this.ThrowTerminatingError(er); } - if (0 == _port) + if (Port == 0) { - _mSmtpClient = new SmtpClient(_smtpserver); + _mSmtpClient = new SmtpClient(SmtpServer); } else { - _mSmtpClient = new SmtpClient(_smtpserver, _port); + _mSmtpClient = new SmtpClient(SmtpServer, Port); } - if (_usessl) + if (UseSsl) { _mSmtpClient.EnableSsl = true; } - if (_credential != null) + if (Credential != null) { _mSmtpClient.UseDefaultCredentials = false; - _mSmtpClient.Credentials = _credential.GetNetworkCredential(); + _mSmtpClient.Credentials = Credential.GetNetworkCredential(); } - else if (!_usessl) + else if (!UseSsl) { _mSmtpClient.UseDefaultCredentials = true; } } /// - /// ProcessRecord override + /// ProcessRecord override. /// protected override void ProcessRecord() { - //add the attachments - if (_attachments != null) + // Add the attachments + if (Attachments != null) { string filepath = string.Empty; - foreach (string attachFile in _attachments) + foreach (string attachFile in Attachments) { try { @@ -435,17 +340,18 @@ protected override void ProcessRecord() } catch (ItemNotFoundException e) { - //NOTE: This will throw + // NOTE: This will throw PathUtils.ReportFileOpenFailure(this, filepath, e); } - Attachment mailAttachment = new Attachment(filepath); + + Attachment mailAttachment = new(filepath); _mMailMessage.Attachments.Add(mailAttachment); } } } /// - /// EndProcessing + /// EndProcessing override. /// protected override void EndProcessing() { @@ -456,71 +362,42 @@ protected override void EndProcessing() } catch (SmtpFailedRecipientsException ex) { - ErrorRecord er = new ErrorRecord(ex, "SmtpFailedRecipientsException", ErrorCategory.InvalidOperation, _mSmtpClient); + ErrorRecord er = new(ex, "SmtpFailedRecipientsException", ErrorCategory.InvalidOperation, _mSmtpClient); WriteError(er); } catch (SmtpException ex) { if (ex.InnerException != null) { - ErrorRecord er = new ErrorRecord(new SmtpException(ex.InnerException.Message), "SmtpException", ErrorCategory.InvalidOperation, _mSmtpClient); + ErrorRecord er = new(new SmtpException(ex.InnerException.Message), "SmtpException", ErrorCategory.InvalidOperation, _mSmtpClient); WriteError(er); } else { - ErrorRecord er = new ErrorRecord(ex, "SmtpException", ErrorCategory.InvalidOperation, _mSmtpClient); + ErrorRecord er = new(ex, "SmtpException", ErrorCategory.InvalidOperation, _mSmtpClient); WriteError(er); } } catch (InvalidOperationException ex) { - ErrorRecord er = new ErrorRecord(ex, "InvalidOperationException", ErrorCategory.InvalidOperation, _mSmtpClient); + ErrorRecord er = new(ex, "InvalidOperationException", ErrorCategory.InvalidOperation, _mSmtpClient); WriteError(er); } catch (System.Security.Authentication.AuthenticationException ex) { - ErrorRecord er = new ErrorRecord(ex, "AuthenticationException", ErrorCategory.InvalidOperation, _mSmtpClient); + ErrorRecord er = new(ex, "AuthenticationException", ErrorCategory.InvalidOperation, _mSmtpClient); WriteError(er); } + finally + { + _mSmtpClient.Dispose(); - //if we don't dispose the attachments, the sender can't modify or use the files sent. - _mMailMessage.Attachments.Dispose(); + // If we don't dispose the attachments, the sender can't modify or use the files sent. + _mMailMessage.Attachments.Dispose(); + } } #endregion } - - /// - /// To make it easier to specify -Encoding parameter, we add an ArgumentTransformationAttribute here. - /// When the input data is of type string and is valid to be converted to System.Text.Encoding, we do - /// the conversion and return the converted value. Otherwise, we just return the input data. - /// - internal sealed class ArgumentToEncodingNameTransformationAttribute : ArgumentTransformationAttribute - { - public override object Transform(EngineIntrinsics engineIntrinsics, object inputData) - { - string encodingName; - if (LanguagePrimitives.TryConvertTo(inputData, out encodingName)) - { - if (string.Equals(encodingName, EncodingConversion.Unknown, StringComparison.OrdinalIgnoreCase) || - string.Equals(encodingName, EncodingConversion.String, StringComparison.OrdinalIgnoreCase) || - string.Equals(encodingName, EncodingConversion.Unicode, StringComparison.OrdinalIgnoreCase) || - string.Equals(encodingName, EncodingConversion.BigEndianUnicode, StringComparison.OrdinalIgnoreCase) || - string.Equals(encodingName, EncodingConversion.Utf8, StringComparison.OrdinalIgnoreCase) || - string.Equals(encodingName, EncodingConversion.Utf7, StringComparison.OrdinalIgnoreCase) || - string.Equals(encodingName, EncodingConversion.Utf32, StringComparison.OrdinalIgnoreCase) || - string.Equals(encodingName, EncodingConversion.Ascii, StringComparison.OrdinalIgnoreCase) || - string.Equals(encodingName, EncodingConversion.Default, StringComparison.OrdinalIgnoreCase) || - string.Equals(encodingName, EncodingConversion.OEM, StringComparison.OrdinalIgnoreCase)) - { - // the encodingName is guaranteed to be valid, so it is safe to pass null to method - // Convert(Cmdlet cmdlet, string encoding) as the value of 'cmdlet'. - return EncodingConversion.Convert(null, encodingName); - } - } - return inputData; - } - } - #endregion -} \ No newline at end of file +} diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Set-PSBreakpoint.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Set-PSBreakpoint.cs index 50095080cec..ccf17652c79 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Set-PSBreakpoint.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Set-PSBreakpoint.cs @@ -1,6 +1,5 @@ -// -// Copyright (C) Microsoft. All rights reserved. -// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. using System; using System.Collections.ObjectModel; @@ -8,94 +7,85 @@ using System.IO; using System.Management.Automation; using System.Management.Automation.Internal; +using System.Management.Automation.Runspaces; namespace Microsoft.PowerShell.Commands { /// /// This class implements Set-PSBreakpoint command. /// - [Cmdlet(VerbsCommon.Set, "PSBreakpoint", DefaultParameterSetName = "Line", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113449")] - [OutputType(typeof(VariableBreakpoint), typeof(CommandBreakpoint), typeof(LineBreakpoint))] - public class SetPSBreakpointCommand : PSCmdlet + [Cmdlet(VerbsCommon.Set, "PSBreakpoint", DefaultParameterSetName = LineParameterSetName, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2096623")] + [OutputType(typeof(CommandBreakpoint), ParameterSetName = new string[] { CommandParameterSetName })] + [OutputType(typeof(LineBreakpoint), ParameterSetName = new string[] { LineParameterSetName })] + [OutputType(typeof(VariableBreakpoint), ParameterSetName = new string[] { VariableParameterSetName })] + public class SetPSBreakpointCommand : PSBreakpointAccessorCommandBase { #region parameters /// - /// the action to take when hitting this breakpoint + /// Gets or sets the action to take when hitting this breakpoint. /// - [Parameter(ParameterSetName = "Command")] - [Parameter(ParameterSetName = "Line")] - [Parameter(ParameterSetName = "Variable")] - public ScriptBlock Action { get; set; } = null; + [Parameter(ParameterSetName = CommandParameterSetName)] + [Parameter(ParameterSetName = LineParameterSetName)] + [Parameter(ParameterSetName = VariableParameterSetName)] + public ScriptBlock Action { get; set; } /// - /// The column to set the breakpoint on + /// Gets or sets the column to set the breakpoint on. /// - [Parameter(Position = 2, ParameterSetName = "Line")] + [Parameter(Position = 2, ParameterSetName = LineParameterSetName)] [ValidateRange(1, int.MaxValue)] - public int Column - { - get - { - return _column ?? 0; - } - set - { - _column = value; - } - } - private int? _column = null; - + public int Column { get; set; } /// - /// the command(s) to set the breakpoint on + /// Gets or sets the command(s) to set the breakpoint on. /// [Alias("C")] - [Parameter(ParameterSetName = "Command", Mandatory = true)] - [ValidateNotNull] - public string[] Command { get; set; } = null; + [Parameter(ParameterSetName = CommandParameterSetName, Mandatory = true)] + public string[] Command { get; set; } /// - /// the line to set the breakpoint on + /// Gets or sets the line to set the breakpoint on. /// - [Parameter(Position = 1, ParameterSetName = "Line", Mandatory = true)] - [ValidateNotNull] - public int[] Line { get; set; } = null; + [Parameter(Position = 1, ParameterSetName = LineParameterSetName, Mandatory = true)] + public int[] Line { get; set; } /// - /// the script to set the breakpoint on + /// Gets or sets the script to set the breakpoint on. /// - [Parameter(ParameterSetName = "Command", Position = 0)] - [Parameter(ParameterSetName = "Line", Mandatory = true, Position = 0)] - [Parameter(ParameterSetName = "Variable", Position = 0)] + [Parameter(ParameterSetName = CommandParameterSetName, Position = 0)] + [Parameter(ParameterSetName = LineParameterSetName, Mandatory = true, Position = 0)] + [Parameter(ParameterSetName = VariableParameterSetName, Position = 0)] [ValidateNotNull] - public string[] Script { get; set; } = null; + public string[] Script { get; set; } /// - /// the variables to set the breakpoint(s) on + /// Gets or sets the variables to set the breakpoint(s) on. /// [Alias("V")] - [Parameter(ParameterSetName = "Variable", Mandatory = true)] - [ValidateNotNull] - public string[] Variable { get; set; } = null; + [Parameter(ParameterSetName = VariableParameterSetName, Mandatory = true)] + public string[] Variable { get; set; } /// - /// + /// Gets or sets the access type for variable breakpoints to break on. /// - [Parameter(ParameterSetName = "Variable")] + [Parameter(ParameterSetName = VariableParameterSetName)] public VariableAccessMode Mode { get; set; } = VariableAccessMode.Write; #endregion parameters + #region overrides + /// - /// verifies that debugging is supported + /// Verifies that debugging is supported. /// protected override void BeginProcessing() { - // + // Call the base method to ensure Runspace is initialized properly. + base.BeginProcessing(); + // Check whether we are executing on a remote session and if so // whether the RemoteScript debug option is selected. - // if (this.Context.InternalHost.ExternalHost is System.Management.Automation.Remoting.ServerRemoteHost && ((this.Context.CurrentRunspace == null) || (this.Context.CurrentRunspace.Debugger == null) || ((this.Context.CurrentRunspace.Debugger.DebugMode & DebugModes.RemoteScript) != DebugModes.RemoteScript) && @@ -128,14 +118,12 @@ protected override void BeginProcessing() } /// - /// set a new breakpoint + /// Set a new breakpoint. /// protected override void ProcessRecord() { - // // If there is a script, resolve its path - // - Collection scripts = new Collection(); + Collection scripts = new(); if (Script != null) { @@ -152,7 +140,7 @@ protected override void ProcessRecord() WriteError( new ErrorRecord( new ArgumentException(StringUtil.Format(Debugger.FileDoesNotExist, providerPath)), - "SetPSBreakpoint:FileDoesNotExist", + "NewPSBreakpoint:FileDoesNotExist", ErrorCategory.InvalidArgument, null)); @@ -166,7 +154,7 @@ protected override void ProcessRecord() WriteError( new ErrorRecord( new ArgumentException(StringUtil.Format(Debugger.WrongExtension, providerPath)), - "SetPSBreakpoint:WrongExtension", + "NewPSBreakpoint:WrongExtension", ErrorCategory.InvalidArgument, null)); continue; @@ -177,10 +165,8 @@ protected override void ProcessRecord() } } - // // If it is a command breakpoint... - // - if (ParameterSetName.Equals("Command", StringComparison.OrdinalIgnoreCase)) + if (ParameterSetName.Equals(CommandParameterSetName, StringComparison.OrdinalIgnoreCase)) { for (int i = 0; i < Command.Length; i++) { @@ -188,21 +174,19 @@ protected override void ProcessRecord() { foreach (string path in scripts) { - WriteObject( - Context.Debugger.NewCommandBreakpoint(path.ToString(), Command[i], Action)); + ProcessBreakpoint( + Runspace.Debugger.SetCommandBreakpoint(Command[i], Action, path)); } } else { - WriteObject( - Context.Debugger.NewCommandBreakpoint(Command[i], Action)); + ProcessBreakpoint( + Runspace.Debugger.SetCommandBreakpoint(Command[i], Action, path: null)); } } } - // // If it is a variable breakpoint... - // - else if (ParameterSetName.Equals("Variable", StringComparison.OrdinalIgnoreCase)) + else if (ParameterSetName.Equals(VariableParameterSetName, StringComparison.OrdinalIgnoreCase)) { for (int i = 0; i < Variable.Length; i++) { @@ -210,23 +194,21 @@ protected override void ProcessRecord() { foreach (string path in scripts) { - WriteObject( - Context.Debugger.NewVariableBreakpoint(path.ToString(), Variable[i], Mode, Action)); + ProcessBreakpoint( + Runspace.Debugger.SetVariableBreakpoint(Variable[i], Mode, Action, path)); } } else { - WriteObject( - Context.Debugger.NewVariableBreakpoint(Variable[i], Mode, Action)); + ProcessBreakpoint( + Runspace.Debugger.SetVariableBreakpoint(Variable[i], Mode, Action, path: null)); } } } - // // Else it is the default parameter set (Line breakpoint)... - // else { - Debug.Assert(ParameterSetName.Equals("Line", StringComparison.OrdinalIgnoreCase)); + Debug.Assert(ParameterSetName.Equals(LineParameterSetName, StringComparison.OrdinalIgnoreCase)); for (int i = 0; i < Line.Length; i++) { @@ -244,19 +226,13 @@ protected override void ProcessRecord() foreach (string path in scripts) { - if (_column != null) - { - WriteObject( - Context.Debugger.NewStatementBreakpoint(path, Line[i], Column, Action)); - } - else - { - WriteObject( - Context.Debugger.NewLineBreakpoint(path, Line[i], Action)); - } + ProcessBreakpoint( + Runspace.Debugger.SetLineBreakpoint(path, Line[i], Column, Action)); } } } } + + #endregion overrides } -} \ No newline at end of file +} diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/SetAliasCommand.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/SetAliasCommand.cs index 0d3c5ae95f4..6c0007fa2a2 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/SetAliasCommand.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/SetAliasCommand.cs @@ -1,18 +1,15 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. -using System; using System.Management.Automation; using System.Management.Automation.Internal; namespace Microsoft.PowerShell.Commands { /// - /// The implementation of the "set-alias" cmdlet + /// The implementation of the "set-alias" cmdlet. /// - /// - [Cmdlet(VerbsCommon.Set, "Alias", SupportsShouldProcess = true, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113390")] + [Cmdlet(VerbsCommon.Set, "Alias", SupportsShouldProcess = true, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2096625")] [OutputType(typeof(AliasInfo))] public class SetAliasCommand : WriteAliasCommandBase { @@ -21,13 +18,12 @@ public class SetAliasCommand : WriteAliasCommandBase /// /// The main processing loop of the command. /// - /// protected override void ProcessRecord() { // Create the alias info AliasInfo aliasToSet = - new AliasInfo( + new( Name, Value, Context, @@ -48,7 +44,7 @@ protected override void ProcessRecord() try { - if (String.IsNullOrEmpty(Scope)) + if (string.IsNullOrEmpty(Scope)) { result = SessionState.Internal.SetAliasItem(aliasToSet, Force, MyInvocation.CommandOrigin); } @@ -73,8 +69,7 @@ protected override void ProcessRecord() WriteObject(result); } } - } // ProcessRecord + } #endregion Command code - } // class SetAliasCommand -}//Microsoft.PowerShell.Commands - + } +} diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/SetDateCommand.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/SetDateCommand.cs index f5a744c1a7e..5dfe40fa9d4 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/SetDateCommand.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/SetDateCommand.cs @@ -1,6 +1,6 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + #pragma warning disable 1634, 1691 using System; @@ -8,36 +8,35 @@ using System.Management.Automation; using System.Management.Automation.Internal; using System.Runtime.InteropServices; + using Dbg = System.Management.Automation; namespace Microsoft.PowerShell.Commands { /// - /// implementation for the set-date command + /// Implementation for the set-date command. /// - [Cmdlet(VerbsCommon.Set, "Date", DefaultParameterSetName = "Date", SupportsShouldProcess = true, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113393")] + [Cmdlet(VerbsCommon.Set, "Date", DefaultParameterSetName = "Date", SupportsShouldProcess = true, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2097133")] [OutputType(typeof(DateTime))] public sealed class SetDateCommand : PSCmdlet { #region parameters /// - /// Allows user to override the date/time object that will be processed + /// Allows user to override the date/time object that will be processed. /// [Parameter(Position = 0, Mandatory = true, ParameterSetName = "Date", ValueFromPipeline = true, ValueFromPipelineByPropertyName = true)] public DateTime Date { get; set; } - /// - /// Allows a use to specify a timespan with which to apply to the current time + /// Allows a use to specify a timespan with which to apply to the current time. /// [Parameter(Position = 0, Mandatory = true, ParameterSetName = "Adjust", ValueFromPipelineByPropertyName = true)] [AllowNull] public TimeSpan Adjust { get; set; } - /// - /// This option determines the default output format used to display the object set-date emits + /// This option determines the default output format used to display the object set-date emits. /// [Parameter] public DisplayHintType DisplayHint { get; set; } = DisplayHintType.DateTime; @@ -47,9 +46,8 @@ public sealed class SetDateCommand : PSCmdlet #region methods /// - /// set the date + /// Set the date. /// - [ArchitectureSensitive] protected override void ProcessRecord() { DateTime dateToUse; @@ -72,43 +70,60 @@ protected override void ProcessRecord() if (ShouldProcess(dateToUse.ToString())) { #if UNIX - if (!Platform.NonWindowsSetDate(dateToUse)) + // We are not validating the native call here. + // We just want to be sure that we're using the value the user provided us. + if (Dbg.Internal.InternalTestHooks.SetDate) + { + WriteObject(dateToUse); + } + else if (!Platform.NonWindowsSetDate(dateToUse)) { throw new Win32Exception(Marshal.GetLastWin32Error()); } #else // build up the SystemTime struct to pass to SetSystemTime - NativeMethods.SystemTime systemTime = new NativeMethods.SystemTime(); - systemTime.Year = (UInt16)dateToUse.Year; - systemTime.Month = (UInt16)dateToUse.Month; - systemTime.Day = (UInt16)dateToUse.Day; - systemTime.Hour = (UInt16)dateToUse.Hour; - systemTime.Minute = (UInt16)dateToUse.Minute; - systemTime.Second = (UInt16)dateToUse.Second; - systemTime.Milliseconds = (UInt16)dateToUse.Millisecond; + NativeMethods.SystemTime systemTime = new(); + systemTime.Year = (ushort)dateToUse.Year; + systemTime.Month = (ushort)dateToUse.Month; + systemTime.Day = (ushort)dateToUse.Day; + systemTime.Hour = (ushort)dateToUse.Hour; + systemTime.Minute = (ushort)dateToUse.Minute; + systemTime.Second = (ushort)dateToUse.Second; + systemTime.Milliseconds = (ushort)dateToUse.Millisecond; #pragma warning disable 56523 - if (!NativeMethods.SetLocalTime(ref systemTime)) + if (Dbg.Internal.InternalTestHooks.SetDate) { - throw new Win32Exception(Marshal.GetLastWin32Error()); + WriteObject(systemTime); } - - // MSDN says to call this twice to account for changes - // between DST - if (!NativeMethods.SetLocalTime(ref systemTime)) + else { - throw new Win32Exception(Marshal.GetLastWin32Error()); + if (!NativeMethods.SetLocalTime(ref systemTime)) + { + throw new Win32Exception(Marshal.GetLastWin32Error()); + } + + // MSDN says to call this twice to account for changes + // between DST + if (!NativeMethods.SetLocalTime(ref systemTime)) + { + throw new Win32Exception(Marshal.GetLastWin32Error()); + } } #pragma warning restore 56523 #endif } - //output DateTime object wrapped in an PSObject with DisplayHint attached - PSObject outputObj = new PSObject(dateToUse); - PSNoteProperty note = new PSNoteProperty("DisplayHint", DisplayHint); + // output DateTime object wrapped in an PSObject with DisplayHint attached + PSObject outputObj = new(dateToUse); + PSNoteProperty note = new("DisplayHint", DisplayHint); outputObj.Properties.Add(note); - WriteObject(outputObj); - } // EndProcessing + // If we've turned on the SetDate test hook, don't emit the output object here because we emitted it earlier. + if (!Dbg.Internal.InternalTestHooks.SetDate) + { + WriteObject(outputObj); + } + } #endregion @@ -117,24 +132,22 @@ protected override void ProcessRecord() internal static class NativeMethods { [StructLayout(LayoutKind.Sequential)] - public class SystemTime + public struct SystemTime { - public UInt16 Year; - public UInt16 Month; - public UInt16 DayOfWeek; - public UInt16 Day; - public UInt16 Hour; - public UInt16 Minute; - public UInt16 Second; - public UInt16 Milliseconds; + public ushort Year; + public ushort Month; + public ushort DayOfWeek; + public ushort Day; + public ushort Hour; + public ushort Minute; + public ushort Second; + public ushort Milliseconds; } [DllImport(PinvokeDllNames.SetLocalTimeDllName, SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] public static extern bool SetLocalTime(ref SystemTime systime); - } // NativeMethods - + } #endregion - } // SetDateCommand -} // namespace Microsoft.PowerShell.Commands - - + } +} diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ShowCommand/ShowCommand.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ShowCommand/ShowCommand.cs index a3f4c3d6a59..60b05807d48 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ShowCommand/ShowCommand.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ShowCommand/ShowCommand.cs @@ -1,30 +1,29 @@ -//----------------------------------------------------------------------- -// Copyright © Microsoft Corporation. All rights reserved. -// -//----------------------------------------------------------------------- +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Globalization; +using System.Management.Automation; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading; + +using Microsoft.PowerShell.Commands.ShowCommandExtension; namespace Microsoft.PowerShell.Commands { - using System; - using System.Collections.Generic; - using System.Collections.ObjectModel; - using System.Globalization; - using System.Management.Automation; - using System.Reflection; - using System.Runtime.InteropServices; - using System.Text; - using System.Threading; - using Microsoft.PowerShell.Commands.ShowCommandExtension; - /// /// Show-Command displays a GUI for a cmdlet, or for all cmdlets if no specific cmdlet is specified. /// - [Cmdlet(VerbsCommon.Show, "Command", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=217448")] + [Cmdlet(VerbsCommon.Show, "Command", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2109589")] public class ShowCommandCommand : PSCmdlet, IDisposable { #region Private Fields /// - /// Set to true when ProcessRecord is reached, since it will always open a window + /// Set to true when ProcessRecord is reached, since it will always open a window. /// private bool _hasOpenedWindow; @@ -44,14 +43,14 @@ public class ShowCommandCommand : PSCmdlet, IDisposable private List _commands; /// - /// List of modules that have been loaded indexed by module name + /// List of modules that have been loaded indexed by module name. /// private Dictionary _importedModules; /// /// Record the EndProcessing error. /// - private PSDataCollection _errors = new PSDataCollection(); + private PSDataCollection _errors = new(); /// /// Field used for the NoCommonParameter parameter. @@ -59,19 +58,11 @@ public class ShowCommandCommand : PSCmdlet, IDisposable private SwitchParameter _noCommonParameter; /// - /// Object used for ShowCommand with a command name that holds the view model created for the command + /// Object used for ShowCommand with a command name that holds the view model created for the command. /// private object _commandViewModelObj; #endregion - /// - /// Finalizes an instance of the ShowCommandCommand class - /// - ~ShowCommandCommand() - { - this.Dispose(false); - } - #region Input Cmdlet Parameter /// /// Gets or sets the command name. @@ -84,48 +75,50 @@ public class ShowCommandCommand : PSCmdlet, IDisposable /// Gets or sets the Width. /// [Parameter] - [ValidateRange(300, Int32.MaxValue)] + [ValidateRange(300, int.MaxValue)] public double Height { get; set; } /// /// Gets or sets the Width. /// [Parameter] - [ValidateRange(300, Int32.MaxValue)] + [ValidateRange(300, int.MaxValue)] public double Width { get; set; } /// - /// Gets or sets a value indicating Common Parameters should not be displayed + /// Gets or sets a value indicating Common Parameters should not be displayed. /// [Parameter] public SwitchParameter NoCommonParameter { get { return _noCommonParameter; } + set { _noCommonParameter = value; } } /// - /// Gets or sets a value indicating errors should not cause a message window to be displayed + /// Gets or sets a value indicating errors should not cause a message window to be displayed. /// [Parameter] public SwitchParameter ErrorPopup { get; set; } /// - /// Gets or sets a value indicating the command should be sent to the pipeline as a string instead of run + /// Gets or sets a value indicating the command should be sent to the pipeline as a string instead of run. /// [Parameter] public SwitchParameter PassThru { get { return _passThrough; } + set { _passThrough = value; } - } // PassThru + } #endregion #region Public and Protected Methods /// /// Executes a PowerShell script, writing the output objects to the pipeline. /// - /// Script to execute + /// Script to execute. public void RunScript(string script) { if (_showCommandProxy == null || string.IsNullOrEmpty(script)) @@ -155,7 +148,8 @@ public void RunScript(string script) return; } - if (!ConsoleInputWithNativeMethods.AddToConsoleInputBuffer(script, true)) + // Don't send newline at end as PSReadLine shows it rather than executing + if (!ConsoleInputWithNativeMethods.AddToConsoleInputBuffer(script, newLine: false)) { this.WriteDebug(FormatAndOut_out_gridview.CannotWriteToConsoleInputBuffer); this.RunScriptSilentlyAndWithErrorHookup(script); @@ -163,7 +157,7 @@ public void RunScript(string script) } /// - /// Dispose method in IDisposable + /// Dispose method in IDisposable. /// public void Dispose() { @@ -180,8 +174,8 @@ protected override void BeginProcessing() if (_showCommandProxy.ScreenHeight < this.Height) { - ErrorRecord error = new ErrorRecord( - new NotSupportedException(String.Format(CultureInfo.CurrentUICulture, FormatAndOut_out_gridview.PropertyValidate, "Height", _showCommandProxy.ScreenHeight)), + ErrorRecord error = new( + new NotSupportedException(string.Format(CultureInfo.CurrentUICulture, FormatAndOut_out_gridview.PropertyValidate, "Height", _showCommandProxy.ScreenHeight)), "PARAMETER_DATA_ERROR", ErrorCategory.InvalidData, null); @@ -190,8 +184,8 @@ protected override void BeginProcessing() if (_showCommandProxy.ScreenWidth < this.Width) { - ErrorRecord error = new ErrorRecord( - new NotSupportedException(String.Format(CultureInfo.CurrentUICulture, FormatAndOut_out_gridview.PropertyValidate, "Width", _showCommandProxy.ScreenWidth)), + ErrorRecord error = new( + new NotSupportedException(string.Format(CultureInfo.CurrentUICulture, FormatAndOut_out_gridview.PropertyValidate, "Width", _showCommandProxy.ScreenWidth)), "PARAMETER_DATA_ERROR", ErrorCategory.InvalidData, null); @@ -215,7 +209,7 @@ protected override void ProcessRecord() } /// - /// Optionally displays errors in a message + /// Optionally displays errors in a message. /// protected override void EndProcessing() { @@ -224,8 +218,8 @@ protected override void EndProcessing() return; } - // We wait untill the window is loaded and then activate it - // to work arround the console window gaining activation somewhere + // We wait until the window is loaded and then activate it + // to work around the console window gaining activation somewhere // in the end of ProcessRecord, which causes the keyboard focus // (and use oif tab key to focus controls) to go away from the window _showCommandProxy.WindowLoaded.WaitOne(); @@ -239,7 +233,7 @@ protected override void EndProcessing() return; } - StringBuilder errorString = new StringBuilder(); + StringBuilder errorString = new(); for (int i = 0; i < _errors.Count; i++) { @@ -267,19 +261,19 @@ protected override void StopProcessing() #region Private Methods /// - /// Runs the script in a new PowerShell instance and hooks up error stream to potentially display error popup. + /// Runs the script in a new PowerShell instance and hooks up error stream to potentially display error popup. /// This method has the inconvenience of not showing to the console user the script being executed. /// - /// script to be run + /// Script to be run. private void RunScriptSilentlyAndWithErrorHookup(string script) { // errors are not created here, because there is a field for it used in the final pop up - PSDataCollection output = new PSDataCollection(); + PSDataCollection output = new(); - output.DataAdded += new EventHandler(this.Output_DataAdded); - _errors.DataAdded += new EventHandler(this.Error_DataAdded); + output.DataAdded += this.Output_DataAdded; + _errors.DataAdded += this.Error_DataAdded; - PowerShell ps = PowerShell.Create(RunspaceMode.CurrentRunspace); + System.Management.Automation.PowerShell ps = System.Management.Automation.PowerShell.Create(RunspaceMode.CurrentRunspace); ps.Streams.Error = _errors; ps.Commands.AddScript(script); @@ -288,12 +282,12 @@ private void RunScriptSilentlyAndWithErrorHookup(string script) } /// - /// Issues an error when this.commandName was not found + /// Issues an error when this.commandName was not found. /// private void IssueErrorForNoCommand() { - InvalidOperationException errorException = new InvalidOperationException( - String.Format( + InvalidOperationException errorException = new( + string.Format( CultureInfo.CurrentUICulture, FormatAndOut_out_gridview.CommandNotFound, Name)); @@ -301,12 +295,12 @@ private void IssueErrorForNoCommand() } /// - /// Issues an error when there is more than one command matching this.commandName + /// Issues an error when there is more than one command matching this.commandName. /// private void IssueErrorForMoreThanOneCommand() { - InvalidOperationException errorException = new InvalidOperationException( - String.Format( + InvalidOperationException errorException = new( + string.Format( CultureInfo.CurrentUICulture, FormatAndOut_out_gridview.MoreThanOneCommand, Name, @@ -315,10 +309,10 @@ private void IssueErrorForMoreThanOneCommand() } /// - /// Called from CommandProcessRecord to run the command that will get the CommandInfo and list of modules + /// Called from CommandProcessRecord to run the command that will get the CommandInfo and list of modules. /// - /// command to be retrieved - /// list of loaded modules + /// Command to be retrieved. + /// List of loaded modules. private void GetCommandInfoAndModules(out CommandInfo command, out Dictionary modules) { command = null; @@ -366,7 +360,7 @@ private void GetCommandInfoAndModules(out CommandInfo command, out Dictionary /// ProcessRecord when a command name is specified. /// - /// true if there was no exception processing this record + /// True if there was no exception processing this record. private bool CanProcessRecordForOneCommand() { CommandInfo commandInfo; @@ -375,7 +369,7 @@ private bool CanProcessRecordForOneCommand() try { - _commandViewModelObj = _showCommandProxy.GetCommandViewModel(new ShowCommandCommandInfo(commandInfo), _noCommonParameter.ToBool(), _importedModules, this.Name.IndexOf('\\') != -1); + _commandViewModelObj = _showCommandProxy.GetCommandViewModel(new ShowCommandCommandInfo(commandInfo), _noCommonParameter.ToBool(), _importedModules, this.Name.Contains('\\')); _showCommandProxy.ShowCommandWindow(_commandViewModelObj, _passThrough); } catch (TargetInvocationException ti) @@ -390,7 +384,7 @@ private bool CanProcessRecordForOneCommand() /// /// ProcessRecord when a command name is not specified. /// - /// true if there was no exception processing this record + /// True if there was no exception processing this record. private bool CanProcessRecordForAllCommands() { Collection rawCommands = this.InvokeCommand.InvokeScript(_showCommandProxy.GetShowAllModulesCommand()); @@ -412,11 +406,11 @@ private bool CanProcessRecordForAllCommands() } /// - /// Waits untill the window has been closed answering HelpNeeded events + /// Waits until the window has been closed answering HelpNeeded events. /// private void WaitForWindowClosedOrHelpNeeded() { - do + while (true) { int which = WaitHandle.WaitAny(new WaitHandle[] { _showCommandProxy.WindowClosed, _showCommandProxy.HelpNeeded, _showCommandProxy.ImportModuleNeeded }); @@ -450,33 +444,32 @@ private void WaitForWindowClosedOrHelpNeeded() _showCommandProxy.ImportModuleDone(_importedModules, _commands); continue; } - while (true); } /// - /// Writes the output of a script being run into the pipeline + /// Writes the output of a script being run into the pipeline. /// - /// output collection - /// output event + /// Output collection. + /// Output event. private void Output_DataAdded(object sender, DataAddedEventArgs e) { this.WriteObject(((PSDataCollection)sender)[e.Index]); } /// - /// Writes the errors of a script being run into the pipeline + /// Writes the errors of a script being run into the pipeline. /// - /// error collection - /// error event + /// Error collection. + /// Error event. private void Error_DataAdded(object sender, DataAddedEventArgs e) { this.WriteError(((PSDataCollection)sender)[e.Index]); } /// - /// Implements IDisposable logic + /// Implements IDisposable logic. /// - /// true if being called from Dispose + /// True if being called from Dispose. private void Dispose(bool isDisposing) { if (isDisposing) @@ -491,21 +484,21 @@ private void Dispose(bool isDisposing) #endregion /// - /// Wraps interop code for console input buffer + /// Wraps interop code for console input buffer. /// internal static class ConsoleInputWithNativeMethods { /// - /// Constant used in calls to GetStdHandle + /// Constant used in calls to GetStdHandle. /// internal const int STD_INPUT_HANDLE = -10; /// - /// Adds a string to the console input buffer + /// Adds a string to the console input buffer. /// - /// string to add to console input buffer - /// true to add Enter after the string - /// true if it was successful in adding all characters to console input buffer + /// String to add to console input buffer. + /// True to add Enter after the string. + /// True if it was successful in adding all characters to console input buffer. internal static bool AddToConsoleInputBuffer(string str, bool newLine) { IntPtr handle = ConsoleInputWithNativeMethods.GetStdHandle(ConsoleInputWithNativeMethods.STD_INPUT_HANDLE); @@ -553,21 +546,21 @@ internal static bool AddToConsoleInputBuffer(string str, bool newLine) } /// - /// Gets the console handle + /// Gets the console handle. /// - /// which console handle to get - /// the console handle + /// Which console handle to get. + /// The console handle. [DllImport("kernel32.dll", SetLastError = true)] internal static extern IntPtr GetStdHandle(int nStdHandle); /// - /// Writes to the console input buffer + /// Writes to the console input buffer. /// - /// console handle - /// inputs to be written - /// number of inputs to be written - /// returned number of inputs actually written - /// 0 if the function fails + /// Console handle. + /// Inputs to be written. + /// Number of inputs to be written. + /// Returned number of inputs actually written. + /// 0 if the function fails. [DllImport("kernel32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] internal static extern bool WriteConsoleInput( @@ -577,31 +570,31 @@ internal static extern bool WriteConsoleInput( out uint lpNumberOfEventsWritten); /// - /// A record to be added to the console buffer + /// A record to be added to the console buffer. /// internal struct INPUT_RECORD { /// - /// The proper event type for a KeyEvent KEY_EVENT_RECORD + /// The proper event type for a KeyEvent KEY_EVENT_RECORD. /// internal const int KEY_EVENT = 0x0001; /// - /// input buffer event type + /// Input buffer event type. /// internal ushort EventType; /// - /// The actual event. The original structure is a union of many others, but this is the largest of them - /// And we don't need other kinds of events + /// The actual event. The original structure is a union of many others, but this is the largest of them. + /// And we don't need other kinds of events. /// internal KEY_EVENT_RECORD KeyEvent; /// /// Sets the necessary fields of for a KeyDown event for the /// - /// input record to be set - /// character to set the record with + /// Input record to be set. + /// Character to set the record with. internal static void SetInputRecord(ref INPUT_RECORD inputRecord, char character) { inputRecord.EventType = INPUT_RECORD.KEY_EVENT; @@ -611,38 +604,38 @@ internal static void SetInputRecord(ref INPUT_RECORD inputRecord, char character } /// - /// Type of INPUT_RECORD which is a key + /// Type of INPUT_RECORD which is a key. /// [StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)] internal struct KEY_EVENT_RECORD { /// - /// true for key down and false for key up, but only needed if wVirtualKeyCode is used + /// True for key down and false for key up, but only needed if wVirtualKeyCode is used. /// internal bool bKeyDown; /// - /// repeat count + /// Repeat count. /// internal ushort wRepeatCount; /// - /// virtual key code + /// Virtual key code. /// internal ushort wVirtualKeyCode; /// - /// virtual key scan code + /// Virtual key scan code. /// internal ushort wVirtualScanCode; /// - /// character in input. If this is specified, wVirtualKeyCode, and others don't need to be + /// Character in input. If this is specified, wVirtualKeyCode, and others don't need to be. /// internal char UnicodeChar; /// - /// State of keys like Shift and control + /// State of keys like Shift and control. /// internal uint dwControlKeyState; } diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ShowCommand/ShowCommandCommandInfo.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ShowCommand/ShowCommandCommandInfo.cs index 27b18fe61c2..e2ba41fb3fc 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ShowCommand/ShowCommandCommandInfo.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ShowCommand/ShowCommandCommandInfo.cs @@ -1,33 +1,28 @@ -//----------------------------------------------------------------------- -// Copyright © Microsoft Corporation. All rights reserved. -// -//----------------------------------------------------------------------- +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Management.Automation; namespace Microsoft.PowerShell.Commands.ShowCommandExtension { - using System; - using System.Collections.Generic; - using System.Linq; - using System.Management.Automation; - /// - /// Implements a facade around CommandInfo and its deserialized counterpart + /// Implements a facade around CommandInfo and its deserialized counterpart. /// public class ShowCommandCommandInfo { /// - /// Creates an instance of the ShowCommandCommandInfo class based on a CommandInfo object + /// Initializes a new instance of the class + /// with the specified . /// - /// /// /// The object to wrap. /// public ShowCommandCommandInfo(CommandInfo other) { - if (null == other) - { - throw new ArgumentNullException("other"); - } + ArgumentNullException.ThrowIfNull(other); this.Name = other.Name; this.ModuleName = other.ModuleName; @@ -41,7 +36,7 @@ public ShowCommandCommandInfo(CommandInfo other) { this.ParameterSets = other.ParameterSets - .Select(x => new ShowCommandParameterSetInfo(x)) + .Select(static x => new ShowCommandParameterSetInfo(x)) .ToList() .AsReadOnly(); } @@ -65,18 +60,15 @@ public ShowCommandCommandInfo(CommandInfo other) } /// - /// Creates an instance of the ShowCommandCommandInfo class based on a PSObject object + /// Initializes a new instance of the class + /// with the specified . /// - /// /// /// The object to wrap. /// public ShowCommandCommandInfo(PSObject other) { - if (null == other) - { - throw new ArgumentNullException("other"); - } + ArgumentNullException.ThrowIfNull(other); this.Name = other.Members["Name"].Value as string; this.ModuleName = other.Members["ModuleName"].Value as string; @@ -94,9 +86,9 @@ public ShowCommandCommandInfo(PSObject other) this.CommandType = (CommandTypes)((other.Members["CommandType"].Value as PSObject).BaseObject); var parameterSets = (other.Members["ParameterSets"].Value as PSObject).BaseObject as System.Collections.ArrayList; - this.ParameterSets = GetObjectEnumerable(parameterSets).Cast().Select(x => new ShowCommandParameterSetInfo(x)).ToList().AsReadOnly(); + this.ParameterSets = GetObjectEnumerable(parameterSets).Cast().Select(static x => new ShowCommandParameterSetInfo(x)).ToList().AsReadOnly(); - if (other.Members["Module"] != null && other.Members["Module"].Value as PSObject != null) + if (other.Members["Module"]?.Value is PSObject) { this.Module = new ShowCommandModuleInfo(other.Members["Module"].Value as PSObject); } @@ -104,9 +96,8 @@ public ShowCommandCommandInfo(PSObject other) } /// - /// Builds a strongly typed IEnumerable{object} out of an IEnumerable + /// Builds a strongly typed IEnumerable{object} out of an IEnumerable. /// - /// /// /// The object to enumerate. /// @@ -121,31 +112,31 @@ internal static IEnumerable GetObjectEnumerable(System.Collections.IEnum /// /// A string representing the definition of the command. /// - public string Name { get; private set; } + public string Name { get; } /// /// A string representing module the command belongs to. /// - public string ModuleName { get; private set; } + public string ModuleName { get; } /// /// A reference to the module the command came from. /// - public ShowCommandModuleInfo Module { get; private set; } + public ShowCommandModuleInfo Module { get; } /// /// An enumeration of the command types this command belongs to. /// - public CommandTypes CommandType { get; private set; } + public CommandTypes CommandType { get; } /// /// A string representing the definition of the command. /// - public string Definition { get; private set; } + public string Definition { get; } /// /// A string representing the definition of the command. /// - public ICollection ParameterSets { get; private set; } + public ICollection ParameterSets { get; } } -} \ No newline at end of file +} diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ShowCommand/ShowCommandModuleInfo.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ShowCommand/ShowCommandModuleInfo.cs index d8e47bee380..f31bc93525d 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ShowCommand/ShowCommandModuleInfo.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ShowCommand/ShowCommandModuleInfo.cs @@ -1,55 +1,47 @@ -//----------------------------------------------------------------------- -// Copyright © Microsoft Corporation. All rights reserved. -// -//----------------------------------------------------------------------- +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Management.Automation; namespace Microsoft.PowerShell.Commands.ShowCommandExtension { - using System; - using System.Management.Automation; - /// - /// Implements a facade around PSModuleInfo and its deserialized counterpart + /// Implements a facade around PSModuleInfo and its deserialized counterpart. /// public class ShowCommandModuleInfo { /// - /// Creates an instance of the ShowCommandModuleInfo class based on a CommandInfo object + /// Initializes a new instance of the class + /// with the specified . /// - /// /// /// The object to wrap. /// public ShowCommandModuleInfo(PSModuleInfo other) { - if (null == other) - { - throw new ArgumentNullException("other"); - } + ArgumentNullException.ThrowIfNull(other); this.Name = other.Name; } /// - /// Creates an instance of the ShowCommandModuleInfo class based on a PSObject object + /// Initializes a new instance of the class + /// with the specified . /// - /// /// /// The object to wrap. /// public ShowCommandModuleInfo(PSObject other) { - if (null == other) - { - throw new ArgumentNullException("other"); - } + ArgumentNullException.ThrowIfNull(other); this.Name = other.Members["Name"].Value as string; } /// - /// Gets the name of this module + /// Gets the name of this module. /// - public string Name { get; private set; } + public string Name { get; } } -} \ No newline at end of file +} diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ShowCommand/ShowCommandParameterInfo.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ShowCommand/ShowCommandParameterInfo.cs index 2e72c44039b..9bf79c5bd76 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ShowCommand/ShowCommandParameterInfo.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ShowCommand/ShowCommandParameterInfo.cs @@ -1,34 +1,28 @@ -//----------------------------------------------------------------------- -// Copyright © Microsoft Corporation. All rights reserved. -// -//----------------------------------------------------------------------- +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Management.Automation; namespace Microsoft.PowerShell.Commands.ShowCommandExtension { - using System; - using System.Collections.Generic; - using System.Linq; - using System.Management.Automation; - - /// - /// Implements a facade around ShowCommandParameterInfo and its deserialized counterpart + /// Implements a facade around ShowCommandParameterInfo and its deserialized counterpart. /// public class ShowCommandParameterInfo { /// - /// Creates an instance of the ShowCommandParameterInfo class based on a CommandParameterInfo object + /// Initializes a new instance of the class + /// with the specified . /// - /// /// /// The object to wrap. /// public ShowCommandParameterInfo(CommandParameterInfo other) { - if (null == other) - { - throw new ArgumentNullException("other"); - } + ArgumentNullException.ThrowIfNull(other); this.Name = other.Name; this.IsMandatory = other.IsMandatory; @@ -36,7 +30,7 @@ public ShowCommandParameterInfo(CommandParameterInfo other) this.ParameterType = new ShowCommandParameterType(other.ParameterType); this.Position = other.Position; - var validateSetAttribute = other.Attributes.Where(x => typeof(ValidateSetAttribute).IsAssignableFrom(x.GetType())).Cast().LastOrDefault(); + var validateSetAttribute = other.Attributes.Where(static x => typeof(ValidateSetAttribute).IsAssignableFrom(x.GetType())).Cast().LastOrDefault(); if (validateSetAttribute != null) { this.HasParameterSet = true; @@ -45,18 +39,15 @@ public ShowCommandParameterInfo(CommandParameterInfo other) } /// - /// Creates an instance of the ShowCommandParameterInfo class based on a PSObject object + /// Initializes a new instance of the class. + /// Creates an instance of the ShowCommandParameterInfo class based on a PSObject object. /// - /// /// /// The object to wrap. /// public ShowCommandParameterInfo(PSObject other) { - if (null == other) - { - throw new ArgumentNullException("other"); - } + ArgumentNullException.ThrowIfNull(other); this.Name = other.Members["Name"].Value as string; this.IsMandatory = (bool)(other.Members["IsMandatory"].Value); @@ -73,37 +64,37 @@ public ShowCommandParameterInfo(PSObject other) /// /// Gets the name of the parameter. /// - public string Name { get; private set; } + public string Name { get; } /// /// True if the parameter is dynamic, or false otherwise. /// - public bool IsMandatory { get; private set; } + public bool IsMandatory { get; } /// /// Gets whether the parameter can take values from the incoming pipeline object. /// - public bool ValueFromPipeline { get; private set; } + public bool ValueFromPipeline { get; } /// /// Gets the type of the parameter. /// - public ShowCommandParameterType ParameterType { get; private set; } + public ShowCommandParameterType ParameterType { get; } /// - /// The possible values of this parameter + /// The possible values of this parameter. /// - public IList ValidParamSetValues { get; private set; } + public IList ValidParamSetValues { get; } /// /// Gets whether the parameter has a parameter set. /// - public bool HasParameterSet { get; private set; } + public bool HasParameterSet { get; } /// /// Gets the position in which the parameter can be specified on the command line /// if not named. If the returned value is int.MinValue then the parameter must be named. /// - public int Position { get; private set; } + public int Position { get; } } -} \ No newline at end of file +} diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ShowCommand/ShowCommandParameterSetInfo.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ShowCommand/ShowCommandParameterSetInfo.cs index d252395de22..c5ec1c74c08 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ShowCommand/ShowCommandParameterSetInfo.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ShowCommand/ShowCommandParameterSetInfo.cs @@ -1,73 +1,64 @@ -//----------------------------------------------------------------------- -// Copyright © Microsoft Corporation. All rights reserved. -// -//----------------------------------------------------------------------- +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Management.Automation; namespace Microsoft.PowerShell.Commands.ShowCommandExtension { - using System; - using System.Collections.Generic; - using System.Linq; - using System.Management.Automation; - - /// - /// Implements a facade around CommandParameterSetInfo and its deserialized counterpart + /// Implements a facade around CommandParameterSetInfo and its deserialized counterpart. /// public class ShowCommandParameterSetInfo { /// - /// Creates an instance of the ShowCommandParameterSetInfo class based on a CommandParameterSetInfo object + /// Initializes a new instance of the class + /// with the specified . /// - /// /// /// The object to wrap. /// public ShowCommandParameterSetInfo(CommandParameterSetInfo other) { - if (null == other) - { - throw new ArgumentNullException("other"); - } + ArgumentNullException.ThrowIfNull(other); this.Name = other.Name; this.IsDefault = other.IsDefault; - this.Parameters = other.Parameters.Select(x => new ShowCommandParameterInfo(x)).ToArray(); + this.Parameters = other.Parameters.Select(static x => new ShowCommandParameterInfo(x)).ToArray(); } /// - /// Creates an instance of the ShowCommandParameterSetInfo class based on a PSObject object + /// Initializes a new instance of the class + /// with the specified . /// - /// /// /// The object to wrap. /// public ShowCommandParameterSetInfo(PSObject other) { - if (null == other) - { - throw new ArgumentNullException("other"); - } + ArgumentNullException.ThrowIfNull(other); this.Name = other.Members["Name"].Value as string; this.IsDefault = (bool)(other.Members["IsDefault"].Value); var parameters = (other.Members["Parameters"].Value as PSObject).BaseObject as System.Collections.ArrayList; - this.Parameters = ShowCommandCommandInfo.GetObjectEnumerable(parameters).Cast().Select(x => new ShowCommandParameterInfo(x)).ToArray(); + this.Parameters = ShowCommandCommandInfo.GetObjectEnumerable(parameters).Cast().Select(static x => new ShowCommandParameterInfo(x)).ToArray(); } /// - /// Gets the name of the parameter set + /// Gets the name of the parameter set. /// - public string Name { get; private set; } + public string Name { get; } /// /// Gets whether the parameter set is the default parameter set. /// - public bool IsDefault { get; private set; } + public bool IsDefault { get; } /// /// Gets the parameter information for the parameters in this parameter set. /// - public ICollection Parameters { get; private set; } + public ICollection Parameters { get; } } -} \ No newline at end of file +} diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ShowCommand/ShowCommandParameterType.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ShowCommand/ShowCommandParameterType.cs index 899038bee1b..01da285a1d7 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ShowCommand/ShowCommandParameterType.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ShowCommand/ShowCommandParameterType.cs @@ -1,34 +1,27 @@ -//----------------------------------------------------------------------- -// -// Copyright © Microsoft Corporation. All rights reserved. -// -//----------------------------------------------------------------------- +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections; +using System.Management.Automation; namespace Microsoft.PowerShell.Commands.ShowCommandExtension { - using System; - using System.Collections; - using System.Management.Automation; - - /// - /// Implements a facade around ShowCommandParameterInfo and its deserialized counterpart + /// Implements a facade around ShowCommandParameterInfo and its deserialized counterpart. /// public class ShowCommandParameterType { /// - /// Creates an instance of the ShowCommandParameterType class based on a Type object + /// Initializes a new instance of the class + /// with the specified . /// - /// /// /// The object to wrap. /// public ShowCommandParameterType(Type other) { - if (null == other) - { - throw new ArgumentNullException("other"); - } + ArgumentNullException.ThrowIfNull(other); this.FullName = other.FullName; if (other.IsEnum) @@ -47,18 +40,15 @@ public ShowCommandParameterType(Type other) } /// - /// Creates an instance of the ShowCommandParameterType class based on a Type object + /// Initializes a new instance of the class + /// with the specified . /// - /// /// /// The object to wrap. /// public ShowCommandParameterType(PSObject other) { - if (null == other) - { - throw new ArgumentNullException("other"); - } + ArgumentNullException.ThrowIfNull(other); this.IsEnum = (bool)(other.Members["IsEnum"].Value); this.FullName = other.Members["FullName"].Value as string; @@ -78,82 +68,82 @@ public ShowCommandParameterType(PSObject other) } /// - /// The full name of the outermost type + /// The full name of the outermost type. /// - public string FullName { get; private set; } + public string FullName { get; } /// - /// Whether or not this type is an enum + /// Whether or not this type is an enum. /// - public bool IsEnum { get; private set; } + public bool IsEnum { get; } /// - /// Whether or not this type is an dictionary + /// Whether or not this type is an dictionary. /// - public bool ImplementsDictionary { get; private set; } + public bool ImplementsDictionary { get; } /// - /// Whether or not this enum has a flag attribute + /// Whether or not this enum has a flag attribute. /// - public bool HasFlagAttribute { get; private set; } + public bool HasFlagAttribute { get; } /// - /// Whether or not this type is an array type + /// Whether or not this type is an array type. /// - public bool IsArray { get; private set; } + public bool IsArray { get; } /// - /// Gets the inner type, if this corresponds to an array type + /// Gets the inner type, if this corresponds to an array type. /// - public ShowCommandParameterType ElementType { get; private set; } + public ShowCommandParameterType ElementType { get; } /// - /// Whether or not this type is a string + /// Whether or not this type is a string. /// public bool IsString { get { - return String.Equals(this.FullName, "System.String", StringComparison.OrdinalIgnoreCase); + return string.Equals(this.FullName, "System.String", StringComparison.OrdinalIgnoreCase); } } /// - /// Whether or not this type is an script block + /// Whether or not this type is an script block. /// public bool IsScriptBlock { get { - return String.Equals(this.FullName, "System.Management.Automation.ScriptBlock", StringComparison.OrdinalIgnoreCase); + return string.Equals(this.FullName, "System.Management.Automation.ScriptBlock", StringComparison.OrdinalIgnoreCase); } } /// - /// Whether or not this type is a bool + /// Whether or not this type is a bool. /// public bool IsBoolean { get { - return String.Equals(this.FullName, "System.Management.Automation.ScriptBlock", StringComparison.OrdinalIgnoreCase); + return string.Equals(this.FullName, "System.Management.Automation.ScriptBlock", StringComparison.OrdinalIgnoreCase); } } /// - /// Whether or not this type is a switch parameter + /// Whether or not this type is a switch parameter. /// public bool IsSwitch { get { - return String.Equals(this.FullName, "System.Management.Automation.SwitchParameter", StringComparison.OrdinalIgnoreCase); + return string.Equals(this.FullName, "System.Management.Automation.SwitchParameter", StringComparison.OrdinalIgnoreCase); } } /// - /// If this is an enum value, return the list of potential values + /// If this is an enum value, return the list of potential values. /// - public ArrayList EnumValues { get; private set; } + public ArrayList EnumValues { get; } } -} \ No newline at end of file +} diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ShowCommand/ShowCommandProxy.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ShowCommand/ShowCommandProxy.cs index 62fe9cb8628..ab8909bb590 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ShowCommand/ShowCommandProxy.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ShowCommand/ShowCommandProxy.cs @@ -1,29 +1,28 @@ -//----------------------------------------------------------------------- -// Copyright © Microsoft Corporation. All rights reserved. -// -//----------------------------------------------------------------------- +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Management.Automation; +using System.Management.Automation.Internal; +using System.Threading; + +using Microsoft.PowerShell.Commands.ShowCommandExtension; namespace Microsoft.PowerShell.Commands { - using System; - using System.Collections.Generic; - using System.Management.Automation; - using System.Management.Automation.Internal; - using System.Threading; - using System.Collections.ObjectModel; - using Microsoft.PowerShell.Commands.ShowCommandExtension; - /// /// Help show-command create WPF object and invoke WPF windows with the - /// Microsoft.PowerShell.Commands.ShowCommandInternal.ShowCommandHelperhelp type defined in Microsoft.PowerShell.GraphicalHost.dll + /// Microsoft.PowerShell.Commands.ShowCommandInternal.ShowCommandHelperhelp type defined in Microsoft.PowerShell.GraphicalHost.dll. /// - internal class ShowCommandProxy + internal sealed class ShowCommandProxy { private const string ShowCommandHelperName = "Microsoft.PowerShell.Commands.ShowCommandInternal.ShowCommandHelper"; - private ShowCommandCommand _cmdlet; + private readonly ShowCommandCommand _cmdlet; - private GraphicalHostReflectionWrapper _graphicalHostReflectionWrapper; + private readonly GraphicalHostReflectionWrapper _graphicalHostReflectionWrapper; internal ShowCommandProxy(ShowCommandCommand cmdlet) { @@ -76,9 +75,9 @@ internal string GetShowAllModulesCommand() return (string)_graphicalHostReflectionWrapper.CallStaticMethod("GetShowAllModulesCommand", false, true); } - internal Dictionary GetImportedModulesDictionary(object[] moduleObjects) + internal Dictionary GetImportedModulesDictionary(object[] moduleObjects) { - return (Dictionary)_graphicalHostReflectionWrapper.CallStaticMethod("GetImportedModulesDictionary", new object[] { moduleObjects }); + return (Dictionary)_graphicalHostReflectionWrapper.CallStaticMethod("GetImportedModulesDictionary", new object[] { moduleObjects }); } internal List GetCommandList(object[] commandObjects) @@ -126,7 +125,6 @@ internal AutoResetEvent WindowLoaded } } - internal string CommandNeedingHelp { get @@ -148,7 +146,6 @@ internal void DisplayHelp(Collection helpResults) _graphicalHostReflectionWrapper.CallMethod("DisplayHelp", helpResults); } - internal string GetImportModuleCommand(string module) { return (string)_graphicalHostReflectionWrapper.CallStaticMethod("GetImportModuleCommand", module, false, true); diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ShowMarkdownCommand.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ShowMarkdownCommand.cs new file mode 100644 index 00000000000..3f40ec3439e --- /dev/null +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/ShowMarkdownCommand.cs @@ -0,0 +1,230 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections.ObjectModel; +using System.Diagnostics; +using System.Globalization; +using System.IO; +using System.Management.Automation; +using System.Management.Automation.Internal; + +using Microsoft.PowerShell.MarkdownRender; + +namespace Microsoft.PowerShell.Commands +{ + /// + /// Show the VT100EncodedString or Html property of on console or show. + /// VT100EncodedString will be displayed on console. + /// Html will be displayed in default browser. + /// + [Cmdlet( + VerbsCommon.Show, "Markdown", + DefaultParameterSetName = "Path", + HelpUri = "https://go.microsoft.com/fwlink/?linkid=2102329")] + [OutputType(typeof(string))] + public class ShowMarkdownCommand : PSCmdlet + { + /// + /// Gets or sets InputObject of type Microsoft.PowerShell.MarkdownRender.MarkdownInfo to display. + /// + [ValidateNotNullOrEmpty] + [Parameter(Mandatory = true, ValueFromPipeline = true, ParameterSetName = "InputObject")] + public PSObject InputObject { get; set; } + + /// + /// Gets or sets path to markdown file(s) to display. + /// + [ValidateNotNullOrEmpty] + [Parameter(Position = 0, Mandatory = true, + ValueFromPipelineByPropertyName = true, ParameterSetName = "Path")] + public string[] Path { get; set; } + + /// + /// Gets or sets the literal path parameter to markdown files(s) to display. + /// + [Parameter(ParameterSetName = "LiteralPath", + Mandatory = true, ValueFromPipeline = false, ValueFromPipelineByPropertyName = true)] + [Alias("PSPath", "LP")] + public string[] LiteralPath + { + get { return Path; } + + set { Path = value; } + } + + /// + /// Gets or sets the switch to view Html in default browser. + /// + [Parameter] + public SwitchParameter UseBrowser { get; set; } + + private System.Management.Automation.PowerShell _powerShell; + + /// + /// Override BeginProcessing. + /// + protected override void BeginProcessing() + { + _powerShell = System.Management.Automation.PowerShell.Create(RunspaceMode.CurrentRunspace); + } + + /// + /// Override ProcessRecord. + /// + protected override void ProcessRecord() + { + switch (ParameterSetName) + { + case "InputObject": + if (InputObject.BaseObject is MarkdownInfo markdownInfo) + { + ProcessMarkdownInfo(markdownInfo); + } + else + { + ConvertFromMarkdown("InputObject", InputObject.BaseObject); + } + + break; + + case "Path": + case "LiteralPath": + ConvertFromMarkdown(ParameterSetName, Path); + break; + + default: + throw new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, ConvertMarkdownStrings.InvalidParameterSet, ParameterSetName)); + } + } + + /// + /// Process markdown as path. + /// + /// Name of parameter to pass to `ConvertFrom-Markdown`. + /// Value of parameter. + private void ConvertFromMarkdown(string parameter, object input) + { + _powerShell.AddCommand("Microsoft.PowerShell.Utility\\ConvertFrom-Markdown").AddParameter(parameter, input); + if (!UseBrowser) + { + _powerShell.AddParameter("AsVT100EncodedString"); + } + + Collection output = _powerShell.Invoke(); + + if (_powerShell.HadErrors) + { + foreach (ErrorRecord errorRecord in _powerShell.Streams.Error) + { + WriteError(errorRecord); + } + } + + foreach (MarkdownInfo result in output) + { + ProcessMarkdownInfo(result); + } + } + + /// + /// Process markdown as input objects. + /// + /// Markdown object to process. + private void ProcessMarkdownInfo(MarkdownInfo markdownInfo) + { + if (UseBrowser) + { + var html = markdownInfo.Html; + + if (!string.IsNullOrEmpty(html)) + { + string tmpFilePath = System.IO.Path.Combine(System.IO.Path.GetTempPath(), Guid.NewGuid().ToString() + ".html"); + + try + { + using (var writer = new StreamWriter(new FileStream(tmpFilePath, FileMode.Create, FileAccess.Write, FileShare.Write))) + { + writer.Write(html); + } + } + catch (Exception e) + { + var errorRecord = new ErrorRecord( + e, + "ErrorWritingTempFile", + ErrorCategory.WriteError, + tmpFilePath); + + WriteError(errorRecord); + return; + } + + if (InternalTestHooks.ShowMarkdownOutputBypass) + { + WriteObject(html); + return; + } + + try + { + ProcessStartInfo startInfo = new(); + startInfo.FileName = tmpFilePath; + startInfo.UseShellExecute = true; + Process.Start(startInfo); + } + catch (Exception e) + { + var errorRecord = new ErrorRecord( + e, + "ErrorLaunchingDefaultApplication", + ErrorCategory.InvalidOperation, + targetObject: null); + + WriteError(errorRecord); + return; + } + } + else + { + string errorMessage = StringUtil.Format(ConvertMarkdownStrings.MarkdownInfoInvalid, "Html"); + var errorRecord = new ErrorRecord( + new InvalidDataException(errorMessage), + "HtmlIsNullOrEmpty", + ErrorCategory.InvalidData, + html); + + WriteError(errorRecord); + } + } + else + { + var vt100String = markdownInfo.VT100EncodedString; + + if (!string.IsNullOrEmpty(vt100String)) + { + WriteObject(vt100String); + } + else + { + string errorMessage = StringUtil.Format(ConvertMarkdownStrings.MarkdownInfoInvalid, "VT100EncodedString"); + var errorRecord = new ErrorRecord( + new InvalidDataException(errorMessage), + "VT100EncodedStringIsNullOrEmpty", + ErrorCategory.InvalidData, + vt100String); + + WriteError(errorRecord); + } + } + } + + /// + /// Override EndProcessing. + /// + protected override void EndProcessing() + { + _powerShell?.Dispose(); + } + } +} diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Sort-Object.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Sort-Object.cs new file mode 100644 index 00000000000..365a669c186 --- /dev/null +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Sort-Object.cs @@ -0,0 +1,274 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.Collections.Generic; +using System.Management.Automation; + +namespace Microsoft.PowerShell.Commands +{ + /// + /// + [Cmdlet("Sort", + "Object", + HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2097038", + DefaultParameterSetName = "Default", + RemotingCapability = RemotingCapability.None)] + public sealed class SortObjectCommand : OrderObjectBase + { + #region Command Line Switches + + /// + /// Gets or sets a value indicating whether a stable sort is required. + /// + /// + /// + /// Items that are duplicates according to the sort algorithm will appear + /// in the same relative order in a stable sort. + /// + [Parameter(ParameterSetName = "Default")] + public SwitchParameter Stable { get; set; } + + /// + /// Gets or sets a value indicating whether the sort order is descending. + /// + [Parameter] + public SwitchParameter Descending + { + get { return DescendingOrder; } + + set { DescendingOrder = value; } + } + + /// + /// Gets or sets a value indicating whether the sort filters out any duplicate objects. + /// + /// + [Parameter] + public SwitchParameter Unique { get; set; } + + #endregion + + /// + /// Gets or sets the number of items to return in a Top N sort. + /// + [Parameter(ParameterSetName = "Top", Mandatory = true)] + [ValidateRange(1, int.MaxValue)] + public int Top { get; set; } + + /// + /// Gets or sets the number of items to return in a Bottom N sort. + /// + [Parameter(ParameterSetName = "Bottom", Mandatory = true)] + [ValidateRange(1, int.MaxValue)] + public int Bottom { get; set; } + + /// + /// Moves unique entries to the front of the list. + /// + private int MoveUniqueEntriesToFront(List sortedData, OrderByPropertyComparer comparer) + { + // If we have sorted data then we know we have at least one unique item + int uniqueCount = sortedData.Count > 0 ? 1 : 0; + + // Move the first of each unique entry to the front of the list + for (int uniqueItemIndex = 0, nextUniqueItemIndex = 1; uniqueItemIndex < sortedData.Count && uniqueCount != Top; uniqueItemIndex++, nextUniqueItemIndex++) + { + // Identify the index of the next unique item + while (nextUniqueItemIndex < sortedData.Count && comparer.Compare(sortedData[uniqueItemIndex], sortedData[nextUniqueItemIndex]) == 0) + { + nextUniqueItemIndex++; + } + + // If there are no more unique items, break + if (nextUniqueItemIndex == sortedData.Count) + { + break; + } + + // Move the next unique item forward and increment the unique item counter + sortedData[uniqueItemIndex + 1] = sortedData[nextUniqueItemIndex]; + uniqueCount++; + } + + return uniqueCount; + } + + /// + /// Sort unsorted OrderByPropertyEntry data using a full sort. + /// + private int FullSort(List dataToSort, OrderByPropertyComparer comparer) + { + // Track how many items in the list are sorted + int sortedItemCount = dataToSort.Count; + + // Future: It may be worth comparing List.Sort with SortedSet when handling unique + // records in case SortedSet is faster (SortedSet was not an option in earlier + // versions of PowerShell). + dataToSort.Sort(comparer); + + if (Unique) + { + // Move unique entries to the front of the list (this is significantly faster + // than removing them) + sortedItemCount = MoveUniqueEntriesToFront(dataToSort, comparer); + } + + return sortedItemCount; + } + + /// + /// Sort unsorted OrderByPropertyEntry data using an indexed min-/max-heap sort. + /// + private int Heapify(List dataToSort, OrderByPropertyComparer orderByPropertyComparer) + { + // Instantiate the Heapify comparer, which takes index into account for sort stability + var comparer = new IndexedOrderByPropertyComparer(orderByPropertyComparer); + + // Identify how many items will be in the heap and the current number of items + int heapCount = 0; + int heapCapacity = Stable ? int.MaxValue + : Top > 0 ? Top : Bottom; + + // Identify the comparator (the value all comparisons will be made against based on whether we're + // doing a Top N or Bottom N sort) + // Note: All comparison results in the loop below are performed related to the value of the + // comparator. OrderByPropertyComparer.Compare will return -1 to indicate that the lhs is smaller + // if an ascending sort is being executed, or -1 to indicate that the lhs is larger if a descending + // sort is being executed. The comparator will be -1 if we're executing a Top N sort, or 1 if we're + // executing a Bottom N sort. These two pairs of states allow us to perform the proper comparison + // regardless of whether we're executing an ascending or descending Top N or Bottom N sort. This + // allows us to build a min-heap or max-heap for each of these sorts with the exact same logic. + // Min-heap: used for faster processing of a top N descending sort and a bottom N ascending sort + // Max-heap: used for faster processing of a top N ascending sort and a bottom N descending sort + int comparator = Top > 0 ? -1 : 1; + + // For unique sorts, use a sorted set to avoid adding unique items to the heap + SortedSet uniqueSet = Unique ? new SortedSet(orderByPropertyComparer) : null; + + // Tracking the index is necessary so that unsortable items can be output at the end, in the order + // in which they were received. + for (int dataIndex = 0, discardedDuplicates = 0; dataIndex + discardedDuplicates < dataToSort.Count; dataIndex++) + { + // Min-heap: if the heap is full and the root item is larger than the entry, discard the entry + // Max-heap: if the heap is full and the root item is smaller than the entry, discard the entry + if (heapCount == heapCapacity && comparer.Compare(dataToSort[0], dataToSort[dataIndex]) == comparator) + { + continue; + } + + // If we're doing a unique sort and the entry is not unique, discard the duplicate entry + if (Unique && !uniqueSet.Add(dataToSort[dataIndex + discardedDuplicates])) + { + discardedDuplicates++; + dataIndex--; + continue; + } + + // Shift next non-duplicate entry into place + if (discardedDuplicates > 0) + { + dataToSort[dataIndex] = dataToSort[dataIndex + discardedDuplicates]; + } + + // Add the current item to the heap and bubble it up into the correct position + int childIndex = dataIndex; + while (childIndex > 0) + { + int parentIndex = ((childIndex > (heapCapacity - 1) ? heapCapacity : childIndex) - 1) >> 1; + + // Min-heap: if the child item is larger than its parent, break + // Max-heap: if the child item is smaller than its parent, break + if (comparer.Compare(dataToSort[childIndex], dataToSort[parentIndex]) == comparator) + { + break; + } + + var temp = dataToSort[parentIndex]; + dataToSort[parentIndex] = dataToSort[childIndex]; + dataToSort[childIndex] = temp; + + childIndex = parentIndex; + } + + heapCount++; + + // If the heap size is too large, remove the root and rearrange the heap + if (heapCount > heapCapacity) + { + // Move the last item to the root and reset the heap count (this effectively removes the last item) + dataToSort[0] = dataToSort[dataIndex]; + heapCount = heapCapacity; + + // Bubble the root item down into the correct position + int parentIndex = 0; + int parentItemCount = heapCapacity >> 1; + while (parentIndex < parentItemCount) + { + // Min-heap: use the smaller of the two children in the comparison + // Max-heap: use the larger of the two children in the comparison + int leftChildIndex = (parentIndex << 1) + 1; + int rightChildIndex = leftChildIndex + 1; + childIndex = rightChildIndex == heapCapacity || comparer.Compare(dataToSort[leftChildIndex], dataToSort[rightChildIndex]) != comparator + ? leftChildIndex + : rightChildIndex; + + // Min-heap: if the smallest child is larger than or equal to its parent, break + // Max-heap: if the largest child is smaller than or equal to its parent, break + int childComparisonResult = comparer.Compare(dataToSort[childIndex], dataToSort[parentIndex]); + if (childComparisonResult == 0 || childComparisonResult == comparator) + { + break; + } + + var temp = dataToSort[childIndex]; + dataToSort[childIndex] = dataToSort[parentIndex]; + dataToSort[parentIndex] = temp; + + parentIndex = childIndex; + } + } + } + + dataToSort.Sort(0, heapCount, comparer); + + return heapCount; + } + + /// + /// + protected override void EndProcessing() + { + OrderByProperty orderByProperty = new( + this, InputObjects, Property, !Descending, ConvertedCulture, CaseSensitive); + + var dataToProcess = orderByProperty.OrderMatrix; + var comparer = orderByProperty.Comparer; + if (comparer == null || dataToProcess == null || dataToProcess.Count == 0) + { + return; + } + + // Track the number of items that will be output from the data once it is sorted + int sortedItemCount = dataToProcess.Count; + + // If -Stable, -Top & -Bottom were not used, invoke an in-place full sort + if (!Stable && Top == 0 && Bottom == 0) + { + sortedItemCount = FullSort(dataToProcess, comparer); + } + // Otherwise, use an indexed min-/max-heap to perform an in-place heap sort (heap + // sorts are inheritantly stable, meaning they will preserve the respective order + // of duplicate objects as they are sorted on the heap) + else + { + sortedItemCount = Heapify(dataToProcess, comparer); + } + + // Write out the portion of the processed data that was sorted + for (int index = 0; index < sortedItemCount; index++) + { + WriteObject(dataToProcess[index].inputObject); + } + } + } +} diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/StartSleepCommand.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/StartSleepCommand.cs index 8006470e231..839a0b7c051 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/StartSleepCommand.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/StartSleepCommand.cs @@ -1,10 +1,10 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. using System; using System.Management.Automation; using System.Threading; + using Dbg = System.Management.Automation; namespace Microsoft.PowerShell.Commands @@ -12,86 +12,90 @@ namespace Microsoft.PowerShell.Commands /// /// Suspend shell, script, or runspace activity for the specified period of time. /// - [Cmdlet(VerbsLifecycle.Start, "Sleep", DefaultParameterSetName = "Seconds", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113407")] + [Cmdlet(VerbsLifecycle.Start, "Sleep", DefaultParameterSetName = "Seconds", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2097041")] public sealed class StartSleepCommand : PSCmdlet, IDisposable { private bool _disposed = false; #region IDisposable /// - /// Dispose method of IDisposable interface. + /// Dispose method of IDisposable interface. /// public void Dispose() { - if (_disposed == false) + if (!_disposed) { if (_waitHandle != null) { _waitHandle.Dispose(); _waitHandle = null; } + _disposed = true; } } #endregion - - #region parameters /// - /// Allows sleep time to be specified in seconds + /// Allows sleep time to be specified in seconds. /// [Parameter(Position = 0, Mandatory = true, ParameterSetName = "Seconds", ValueFromPipeline = true, ValueFromPipelineByPropertyName = true)] - [ValidateRangeAttribute(0, int.MaxValue / 1000)] - public int Seconds { get; set; } - + [ValidateRange(0.0, (double)(int.MaxValue / 1000))] + public double Seconds { get; set; } /// - /// Allows sleep time to be specified in milliseconds + /// Allows sleep time to be specified in milliseconds. /// [Parameter(Mandatory = true, ParameterSetName = "Milliseconds", ValueFromPipelineByPropertyName = true)] - [ValidateRangeAttribute(0, int.MaxValue)] + [ValidateRange(0, int.MaxValue)] [Alias("ms")] public int Milliseconds { get; set; } + /// + /// Allows sleep time to be specified as a TimeSpan. + /// + [Parameter(Position = 0, Mandatory = true, ParameterSetName = "FromTimeSpan", ValueFromPipeline = true, + ValueFromPipelineByPropertyName = true)] + [ValidateRange(ValidateRangeKind.NonNegative)] + [Alias("ts")] + public TimeSpan Duration { get; set; } + #endregion #region methods - //Wait handle which is used by thread to sleep. + // Wait handle which is used by thread to sleep. private ManualResetEvent _waitHandle; - //object used for synchronizes pipeline thread and stop thread - //access to waitHandle - private object _syncObject = new object(); + // object used for synchronizes pipeline thread and stop thread + // access to waitHandle + private readonly object _syncObject = new(); - //this is set to true by stopProcessing + // this is set to true by stopProcessing private bool _stopping = false; /// - /// This method causes calling thread to sleep for - /// specified milliseconds + /// This method causes calling thread to sleep for specified milliseconds. /// private void Sleep(int milliSecondsToSleep) { lock (_syncObject) { - if (_stopping == false) + if (!_stopping) { _waitHandle = new ManualResetEvent(false); } } - if (_waitHandle != null) - { - _waitHandle.WaitOne(milliSecondsToSleep, true); - } + + _waitHandle?.WaitOne(milliSecondsToSleep, true); } /// - /// + /// ProcessRecord method. /// protected override void ProcessRecord() { @@ -100,39 +104,53 @@ protected override void ProcessRecord() switch (ParameterSetName) { case "Seconds": - sleepTime = Seconds * 1000; + sleepTime = (int)(Seconds * 1000); break; case "Milliseconds": sleepTime = Milliseconds; break; + case "FromTimeSpan": + if (Duration.TotalMilliseconds > int.MaxValue) + { + PSArgumentException argumentException = PSTraceSource.NewArgumentException( + nameof(Duration), + StartSleepStrings.MaximumDurationExceeded, + TimeSpan.FromMilliseconds(int.MaxValue), + Duration); + + ThrowTerminatingError( + new ErrorRecord( + argumentException, + "MaximumDurationExceeded", + ErrorCategory.InvalidArgument, + targetObject: null)); + } + + sleepTime = (int)Math.Floor(Duration.TotalMilliseconds); + break; + default: Dbg.Diagnostics.Assert(false, "Only one of the specified parameter sets should be called."); break; } Sleep(sleepTime); - } // EndProcessing + } /// - /// stopprocessing override + /// StopProcessing override. /// - protected override - void - StopProcessing() + protected override void StopProcessing() { lock (_syncObject) { _stopping = true; - if (_waitHandle != null) - { - _waitHandle.Set(); - } + _waitHandle?.Set(); } } #endregion - } // StartSleepCommand -} // namespace Microsoft.PowerShell.Commands - + } +} diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Tee-Object.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Tee-Object.cs new file mode 100644 index 00000000000..27ea15406f7 --- /dev/null +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Tee-Object.cs @@ -0,0 +1,170 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Management.Automation; +using System.Text; + +using Microsoft.PowerShell.Commands.Internal.Format; + +namespace Microsoft.PowerShell.Commands +{ + /// + /// Class for Tee-object implementation. + /// + [Cmdlet("Tee", "Object", DefaultParameterSetName = "File", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2097034")] + public sealed class TeeObjectCommand : PSCmdlet, IDisposable + { + /// + /// Object to process. + /// + [Parameter(ValueFromPipeline = true)] + public PSObject InputObject + { + get { return _inputObject; } + + set { _inputObject = value; } + } + + private PSObject _inputObject; + + /// + /// FilePath parameter. + /// + [Parameter(Mandatory = true, Position = 0, ParameterSetName = "File")] + [Alias("Path")] + public string FilePath + { + get { return _fileName; } + + set { _fileName = value; } + } + + private string _fileName; + + /// + /// Literal FilePath parameter. + /// + [Parameter(Mandatory = true, ParameterSetName = "LiteralFile")] + [Alias("PSPath", "LP")] + public string LiteralPath + { + get + { + return _fileName; + } + + set + { + _fileName = value; + } + } + + /// + /// Append switch. + /// + [Parameter(ParameterSetName = "File")] + public SwitchParameter Append + { + get { return _append; } + + set { _append = value; } + } + + private bool _append; + + /// + /// Gets or sets the Encoding. + /// + [Parameter(ParameterSetName = "File")] + [Parameter(ParameterSetName = "LiteralFile")] + [ArgumentToEncodingTransformation] + [ArgumentEncodingCompletions] + [ValidateNotNullOrEmpty] + public Encoding Encoding { get; set; } = Encoding.Default; + + /// + /// Variable parameter. + /// + [Parameter(Mandatory = true, ParameterSetName = "Variable")] + public string Variable + { + get { return _variable; } + + set { _variable = value; } + } + + private string _variable; + + /// + /// + protected override void BeginProcessing() + { + _commandWrapper = new CommandWrapper(); + if (string.Equals(ParameterSetName, "File", StringComparison.OrdinalIgnoreCase)) + { + _commandWrapper.Initialize(Context, "out-file", typeof(OutFileCommand)); + _commandWrapper.AddNamedParameter("filepath", _fileName); + _commandWrapper.AddNamedParameter("append", _append); + _commandWrapper.AddNamedParameter("encoding", Encoding); + } + else if (string.Equals(ParameterSetName, "LiteralFile", StringComparison.OrdinalIgnoreCase)) + { + _commandWrapper.Initialize(Context, "out-file", typeof(OutFileCommand)); + _commandWrapper.AddNamedParameter("LiteralPath", _fileName); + _commandWrapper.AddNamedParameter("append", _append); + _commandWrapper.AddNamedParameter("encoding", Encoding); + } + else + { + // variable parameter set + _commandWrapper.Initialize(Context, "set-variable", typeof(SetVariableCommand)); + _commandWrapper.AddNamedParameter("name", _variable); + // Can't use set-var's passthru because it writes the var object to the pipeline, we want just + // the values to be written + } + } + + /// + /// + protected override void ProcessRecord() + { + _commandWrapper.Process(_inputObject); + WriteObject(_inputObject); + } + + /// + /// + protected override void EndProcessing() + { + _commandWrapper.ShutDown(); + } + + private void Dispose(bool isDisposing) + { + if (!_alreadyDisposed) + { + _alreadyDisposed = true; + if (isDisposing && _commandWrapper != null) + { + _commandWrapper.Dispose(); + _commandWrapper = null; + } + } + } + + /// + /// Dispose method in IDisposable. + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + #region private + private CommandWrapper _commandWrapper; + private bool _alreadyDisposed; + #endregion private + } +} diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/TestJsonCommand.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/TestJsonCommand.cs new file mode 100644 index 00000000000..909cbff3c8f --- /dev/null +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/TestJsonCommand.cs @@ -0,0 +1,335 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Management.Automation; +using System.Net.Http; +using System.Security; +using System.Text.Json; +using System.Text.Json.Nodes; +using Json.Schema; + +namespace Microsoft.PowerShell.Commands +{ + /// + /// This class implements Test-Json command. + /// + [Cmdlet(VerbsDiagnostic.Test, "Json", DefaultParameterSetName = JsonStringParameterSet, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2096609")] + [OutputType(typeof(bool))] + public class TestJsonCommand : PSCmdlet + { + #region Parameter Set Names + + private const string JsonStringParameterSet = "JsonString"; + private const string JsonStringWithSchemaStringParameterSet = "JsonStringWithSchemaString"; + private const string JsonStringWithSchemaFileParameterSet = "JsonStringWithSchemaFile"; + private const string JsonPathParameterSet = "JsonPath"; + private const string JsonPathWithSchemaStringParameterSet = "JsonPathWithSchemaString"; + private const string JsonPathWithSchemaFileParameterSet = "JsonPathWithSchemaFile"; + private const string JsonLiteralPathParameterSet = "JsonLiteralPath"; + private const string JsonLiteralPathWithSchemaStringParameterSet = "JsonLiteralPathWithSchemaString"; + private const string JsonLiteralPathWithSchemaFileParameterSet = "JsonLiteralPathWithSchemaFile"; + + #endregion + + #region Json Document Option Constants + + private const string IgnoreCommentsOption = "IgnoreComments"; + private const string AllowTrailingCommasOption = "AllowTrailingCommas"; + + #endregion + + #region Parameters + + /// + /// Gets or sets JSON string to be validated. + /// + [Parameter(Position = 0, Mandatory = true, ValueFromPipeline = true, ParameterSetName = JsonStringParameterSet)] + [Parameter(Position = 0, Mandatory = true, ValueFromPipeline = true, ParameterSetName = JsonStringWithSchemaStringParameterSet)] + [Parameter(Position = 0, Mandatory = true, ValueFromPipeline = true, ParameterSetName = JsonStringWithSchemaFileParameterSet)] + public string Json { get; set; } + + /// + /// Gets or sets JSON file path to be validated. + /// + [Parameter(Position = 0, Mandatory = true, ValueFromPipelineByPropertyName = true, ParameterSetName = JsonPathParameterSet)] + [Parameter(Position = 0, Mandatory = true, ValueFromPipelineByPropertyName = true, ParameterSetName = JsonPathWithSchemaStringParameterSet)] + [Parameter(Position = 0, Mandatory = true, ValueFromPipelineByPropertyName = true, ParameterSetName = JsonPathWithSchemaFileParameterSet)] + public string Path { get; set; } + + /// + /// Gets or sets JSON literal file path to be validated. + /// + [Parameter(Position = 0, Mandatory = true, ValueFromPipelineByPropertyName = true, ParameterSetName = JsonLiteralPathParameterSet)] + [Parameter(Position = 0, Mandatory = true, ValueFromPipelineByPropertyName = true, ParameterSetName = JsonLiteralPathWithSchemaStringParameterSet)] + [Parameter(Position = 0, Mandatory = true, ValueFromPipelineByPropertyName = true, ParameterSetName = JsonLiteralPathWithSchemaFileParameterSet)] + [Alias("PSPath", "LP")] + public string LiteralPath + { + get + { + return _isLiteralPath ? Path : null; + } + + set + { + _isLiteralPath = true; + Path = value; + } + } + + /// + /// Gets or sets schema to validate the JSON against. + /// This is optional parameter. + /// If the parameter is absent the cmdlet only attempts to parse the JSON string. + /// If the parameter present the cmdlet attempts to parse the JSON string and + /// then validates the JSON against the schema. Before testing the JSON string, + /// the cmdlet parses the schema doing implicitly check the schema too. + /// + [Parameter(Position = 1, Mandatory = true, ParameterSetName = JsonStringWithSchemaStringParameterSet)] + [Parameter(Position = 1, Mandatory = true, ParameterSetName = JsonPathWithSchemaStringParameterSet)] + [Parameter(Position = 1, Mandatory = true, ParameterSetName = JsonLiteralPathWithSchemaStringParameterSet)] + [ValidateNotNullOrEmpty] + public string Schema { get; set; } + + /// + /// Gets or sets path to the file containing schema to validate the JSON string against. + /// This is optional parameter. + /// + [Parameter(Position = 1, Mandatory = true, ParameterSetName = JsonStringWithSchemaFileParameterSet)] + [Parameter(Position = 1, Mandatory = true, ParameterSetName = JsonPathWithSchemaFileParameterSet)] + [Parameter(Position = 1, Mandatory = true, ParameterSetName = JsonLiteralPathWithSchemaFileParameterSet)] + [ValidateNotNullOrEmpty] + public string SchemaFile { get; set; } + + /// + /// Gets or sets JSON document options. + /// + [Parameter] + [ValidateNotNullOrEmpty] + [ValidateSet(IgnoreCommentsOption, AllowTrailingCommasOption)] + public string[] Options { get; set; } = Array.Empty(); + + #endregion + + #region Private Members + + private bool _isLiteralPath = false; + private JsonSchema _jschema; + private JsonDocumentOptions _documentOptions; + + #endregion + + /// + /// Prepare a JSON schema. + /// + protected override void BeginProcessing() + { + // By default, a JSON Schema implementation isn't supposed to automatically fetch content. + // Instead JsonSchema.Net has been set up with a registry so that users can pre-register + // any schemas they may need to resolve. + // However, pre-registering schemas doesn't make sense in the context of a Powershell command, + // and automatically fetching referenced URIs is likely the preferred behavior. To do that, + // this property must be set with a method to retrieve and deserialize the content. + // For more information, see https://json-everything.net/json-schema#automatic-resolution + SchemaRegistry.Global.Fetch = static uri => + { + try + { + string text; + switch (uri.Scheme) + { + case "http": + case "https": + { + using var client = new HttpClient(); + text = client.GetStringAsync(uri).Result; + break; + } + case "file": + var filename = Uri.UnescapeDataString(uri.AbsolutePath); + text = File.ReadAllText(filename); + break; + default: + throw new FormatException(string.Format(TestJsonCmdletStrings.InvalidUriScheme, uri.Scheme)); + } + + return JsonSerializer.Deserialize(text); + } + catch (Exception e) + { + throw new JsonSchemaReferenceResolutionException(e); + } + }; + + string resolvedpath = string.Empty; + + try + { + if (Schema != null) + { + try + { + _jschema = JsonSchema.FromText(Schema); + } + catch (JsonException e) + { + Exception exception = new(TestJsonCmdletStrings.InvalidJsonSchema, e); + WriteError(new ErrorRecord(exception, "InvalidJsonSchema", ErrorCategory.InvalidData, Schema)); + } + } + else if (SchemaFile != null) + { + try + { + resolvedpath = Context.SessionState.Path.GetUnresolvedProviderPathFromPSPath(SchemaFile); + _jschema = JsonSchema.FromFile(resolvedpath); + } + catch (JsonException e) + { + Exception exception = new(TestJsonCmdletStrings.InvalidJsonSchema, e); + WriteError(new ErrorRecord(exception, "InvalidJsonSchema", ErrorCategory.InvalidData, SchemaFile)); + } + } + } + catch (Exception e) when ( + // Handle exceptions related to file access to provide more specific error message + // https://learn.microsoft.com/dotnet/standard/io/handling-io-errors + e is IOException || + e is UnauthorizedAccessException || + e is NotSupportedException || + e is SecurityException + ) + { + Exception exception = new( + string.Format( + CultureInfo.CurrentUICulture, + TestJsonCmdletStrings.JsonSchemaFileOpenFailure, + resolvedpath), + e); + ThrowTerminatingError(new ErrorRecord(exception, "JsonSchemaFileOpenFailure", ErrorCategory.OpenError, resolvedpath)); + } + catch (Exception e) + { + Exception exception = new(TestJsonCmdletStrings.InvalidJsonSchema, e); + ThrowTerminatingError(new ErrorRecord(exception, "InvalidJsonSchema", ErrorCategory.InvalidData, resolvedpath)); + } + + _documentOptions = new JsonDocumentOptions + { + CommentHandling = Options.Contains(IgnoreCommentsOption, StringComparer.OrdinalIgnoreCase) + ? JsonCommentHandling.Skip + : JsonCommentHandling.Disallow, + AllowTrailingCommas = Options.Contains(AllowTrailingCommasOption, StringComparer.OrdinalIgnoreCase) + }; + } + + /// + /// Validate a JSON. + /// + protected override void ProcessRecord() + { + bool result = true; + + string jsonToParse = string.Empty; + + if (Json != null) + { + jsonToParse = Json; + } + else if (Path != null) + { + string resolvedPath = PathUtils.ResolveFilePath(Path, this, _isLiteralPath); + + if (!File.Exists(resolvedPath)) + { + ItemNotFoundException exception = new( + Path, + "PathNotFound", + SessionStateStrings.PathNotFound); + + ThrowTerminatingError(exception.ErrorRecord); + } + + jsonToParse = File.ReadAllText(resolvedPath); + } + + try + { + + var parsedJson = JsonNode.Parse(jsonToParse, nodeOptions: null, _documentOptions); + + if (_jschema != null) + { + EvaluationResults evaluationResults = _jschema.Evaluate(parsedJson, new EvaluationOptions { OutputFormat = OutputFormat.Hierarchical }); + result = evaluationResults.IsValid; + if (!result) + { + ReportValidationErrors(evaluationResults); + } + } + } + catch (JsonSchemaReferenceResolutionException jsonExc) + { + result = false; + + Exception exception = new(TestJsonCmdletStrings.InvalidJsonSchema, jsonExc); + WriteError(new ErrorRecord(exception, "InvalidJsonSchema", ErrorCategory.InvalidData, _jschema)); + } + catch (Exception exc) + { + result = false; + + Exception exception = new(TestJsonCmdletStrings.InvalidJson, exc); + WriteError(new ErrorRecord(exception, "InvalidJson", ErrorCategory.InvalidData, Json)); + } + + WriteObject(result); + } + + /// + /// Recursively reports validation errors from hierarchical evaluation results. + /// Skips nodes (and their children) where IsValid is true to avoid false positives + /// from constructs like OneOf or AnyOf. + /// + /// The evaluation result to process. + private void ReportValidationErrors(EvaluationResults evaluationResult) + { + // Skip this node and all children if validation passed + if (evaluationResult.IsValid) + { + return; + } + + // Report errors at this level + HandleValidationErrors(evaluationResult); + + // Recursively process child results + if (evaluationResult.HasDetails) + { + foreach (var nestedResult in evaluationResult.Details) + { + ReportValidationErrors(nestedResult); + } + } + } + + private void HandleValidationErrors(EvaluationResults evaluationResult) + { + if (!evaluationResult.HasErrors) + { + return; + } + + foreach (var error in evaluationResult.Errors!) + { + Exception exception = new(string.Format(TestJsonCmdletStrings.InvalidJsonAgainstSchemaDetailed, error.Value, evaluationResult.InstanceLocation)); + ErrorRecord errorRecord = new(exception, "InvalidJsonAgainstSchemaDetailed", ErrorCategory.InvalidData, null); + WriteError(errorRecord); + } + } + } +} diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/TimeExpressionCommand.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/TimeExpressionCommand.cs index 80ea05f4851..24ed81995d1 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/TimeExpressionCommand.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/TimeExpressionCommand.cs @@ -1,13 +1,12 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + #region Using directives using System; using System.Management.Automation; using System.Management.Automation.Internal; - #endregion namespace Microsoft.PowerShell.Commands @@ -16,48 +15,44 @@ namespace Microsoft.PowerShell.Commands /// Implements a cmdlet that applies a script block /// to each element of the pipeline. /// - [Cmdlet(VerbsDiagnostic.Measure, "Command", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113348", RemotingCapability = RemotingCapability.None)] + [Cmdlet(VerbsDiagnostic.Measure, "Command", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2097029", RemotingCapability = RemotingCapability.None)] [OutputType(typeof(TimeSpan))] public sealed class MeasureCommandCommand : PSCmdlet { #region parameters /// - /// This parameter specifies the current pipeline object + /// This parameter specifies the current pipeline object. /// [Parameter(ValueFromPipeline = true)] - public PSObject InputObject { set; get; } = AutomationNull.Value; - + public PSObject InputObject { get; set; } = AutomationNull.Value; /// - /// The script block to apply + /// The script block to apply. /// [Parameter(Position = 0, Mandatory = true)] - public ScriptBlock Expression { set; get; } + public ScriptBlock Expression { get; set; } #endregion #region private members - private System.Diagnostics.Stopwatch _stopWatch = new System.Diagnostics.Stopwatch(); + private readonly System.Diagnostics.Stopwatch _stopWatch = new(); #endregion #region methods - /// - /// Output the timer + /// Output the timer. /// protected override void EndProcessing() { WriteObject(_stopWatch.Elapsed); - } // EndProcessing - + } /// - /// Execute the script block passing in the current pipeline object as - /// it's only parameter. + /// Execute the script block passing in the current pipeline object as it's only parameter. /// protected override void ProcessRecord() { @@ -68,15 +63,13 @@ protected override void ProcessRecord() useLocalScope: false, errorHandlingBehavior: ScriptBlock.ErrorHandlingBehavior.WriteToCurrentErrorPipe, dollarUnder: InputObject, // $_ - input: new object[0], // $input + input: Array.Empty(), // $input scriptThis: AutomationNull.Value, outputPipe: new Pipe { NullPipe = true }, invocationInfo: null); _stopWatch.Stop(); } - #endregion } -} // namespace Microsoft.PowerShell.Commands - +} diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/UnblockFile.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/UnblockFile.cs index 95d91473183..6633a66f96f 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/UnblockFile.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/UnblockFile.cs @@ -1,17 +1,20 @@ -#if !UNIX -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. #region Using directives using System; using System.Collections.Generic; using System.Collections.ObjectModel; -using System.ComponentModel; using System.Diagnostics.CodeAnalysis; +#if UNIX +using System.Globalization; +using System.Management.Automation; +using System.Runtime.InteropServices; +#else using System.Management.Automation; using System.Management.Automation.Internal; +#endif #endregion @@ -19,11 +22,16 @@ namespace Microsoft.PowerShell.Commands { /// Removes the Zone.Identifier stream from a file. [Cmdlet(VerbsSecurity.Unblock, "File", DefaultParameterSetName = "ByPath", SupportsShouldProcess = true, - HelpUri = "https://go.microsoft.com/fwlink/?LinkID=217450")] + HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2097033")] public sealed class UnblockFileCommand : PSCmdlet { +#if UNIX + private const string MacBlockAttribute = "com.apple.quarantine"; + private const int RemovexattrFollowSymLink = 0; +#endif + /// - /// The path of the file to unblock + /// The path of the file to unblock. /// [Parameter(Mandatory = true, Position = 0, ParameterSetName = "ByPath")] [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] @@ -33,6 +41,7 @@ public string[] Path { return _paths; } + set { _paths = value; @@ -40,10 +49,10 @@ public string[] Path } /// - /// The literal path of the file to unblock + /// The literal path of the file to unblock. /// [Parameter(Mandatory = true, ParameterSetName = "ByLiteralPath", ValueFromPipelineByPropertyName = true)] - [Alias("PSPath")] + [Alias("PSPath", "LP")] [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] public string[] LiteralPath { @@ -51,6 +60,7 @@ public string[] LiteralPath { return _paths; } + set { _paths = value; @@ -64,10 +74,10 @@ public string[] LiteralPath /// protected override void ProcessRecord() { - List pathsToProcess = new List(); + List pathsToProcess = new(); ProviderInfo provider = null; - if (String.Equals(this.ParameterSetName, "ByLiteralPath", StringComparison.OrdinalIgnoreCase)) + if (string.Equals(this.ParameterSetName, "ByLiteralPath", StringComparison.OrdinalIgnoreCase)) { foreach (string path in _paths) { @@ -100,7 +110,7 @@ protected override void ProcessRecord() { if (!WildcardPattern.ContainsWildcardCharacters(path)) { - ErrorRecord errorRecord = new ErrorRecord(e, + ErrorRecord errorRecord = new(e, "FileNotFound", ErrorCategory.ObjectNotFound, path); @@ -109,6 +119,7 @@ protected override void ProcessRecord() } } } +#if !UNIX // Unblock files foreach (string path in pathsToProcess) @@ -119,13 +130,36 @@ protected override void ProcessRecord() { AlternateDataStreamUtilities.DeleteFileStream(path, "Zone.Identifier"); } - catch (Win32Exception accessException) + catch (Exception e) { - WriteError(new ErrorRecord(accessException, "RemoveItemUnauthorizedAccessError", ErrorCategory.PermissionDenied, path)); + WriteError(new ErrorRecord(exception: e, errorId: "RemoveItemUnableToAccessFile", ErrorCategory.ResourceUnavailable, targetObject: path)); } + } + } +#else + if (Platform.IsLinux) + { + string errorMessage = UnblockFileStrings.LinuxNotSupported; + Exception e = new PlatformNotSupportedException(errorMessage); + ThrowTerminatingError(new ErrorRecord(exception: e, errorId: "LinuxNotSupported", ErrorCategory.NotImplemented, targetObject: null)); + return; + } + foreach (string path in pathsToProcess) + { + if (IsBlocked(path)) + { + UInt32 result = RemoveXattr(path, MacBlockAttribute, RemovexattrFollowSymLink); + if (result != 0) + { + string errorMessage = string.Format(CultureInfo.CurrentUICulture, UnblockFileStrings.UnblockError, path); + Exception e = new InvalidOperationException(errorMessage); + WriteError(new ErrorRecord(exception: e, errorId: "UnblockError", ErrorCategory.InvalidResult, targetObject: path)); + } } } + +#endif } /// @@ -146,7 +180,7 @@ private bool IsValidFileForUnblocking(string resolvedpath) { if (!System.IO.File.Exists(resolvedpath)) { - ErrorRecord errorRecord = new ErrorRecord( + ErrorRecord errorRecord = new( new System.IO.FileNotFoundException(resolvedpath), "FileNotFound", ErrorCategory.ObjectNotFound, @@ -155,12 +189,42 @@ private bool IsValidFileForUnblocking(string resolvedpath) } else { - isValidUnblockableFile = true; ; + isValidUnblockableFile = true; } } return isValidUnblockableFile; } + +#if UNIX + private static bool IsBlocked(string path) + { + const uint valueSize = 1024; + IntPtr value = Marshal.AllocHGlobal((int)valueSize); + try + { + var resultSize = GetXattr(path, MacBlockAttribute, value, valueSize, 0, RemovexattrFollowSymLink); + return resultSize != -1; + } + finally + { + Marshal.FreeHGlobal(value); + } + } + + // Ansi means UTF8 on Unix + // https://developer.apple.com/library/archive/documentation/System/Conceptual/ManPages_iPhoneOS/man2/RemoveXattr.2.html + [DllImport("libc", SetLastError = true, EntryPoint = "removexattr", CharSet = CharSet.Ansi)] + private static extern UInt32 RemoveXattr(string path, string name, int options); + + [DllImport("libc", EntryPoint = "getxattr", CharSet = CharSet.Ansi)] + private static extern long GetXattr( + [MarshalAs(UnmanagedType.LPStr)] string path, + [MarshalAs(UnmanagedType.LPStr)] string name, + IntPtr value, + ulong size, + uint position, + int options); +#endif } } -#endif diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/UnregisterEventCommand.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/UnregisterEventCommand.cs index 428c7a5843f..cf65a1f73b3 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/UnregisterEventCommand.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/UnregisterEventCommand.cs @@ -1,6 +1,5 @@ -// -// Copyright (C) Microsoft. All rights reserved. -// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. using System; using System.Management.Automation; @@ -10,13 +9,13 @@ namespace Microsoft.PowerShell.Commands /// /// Unregisters from an event on an object. /// - [Cmdlet(VerbsLifecycle.Unregister, "Event", SupportsShouldProcess = true, DefaultParameterSetName = "BySource", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=135269")] + [Cmdlet(VerbsLifecycle.Unregister, "Event", SupportsShouldProcess = true, DefaultParameterSetName = "BySource", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2097037")] public class UnregisterEventCommand : PSCmdlet { #region parameters /// - /// An identifier for this event subscription + /// An identifier for this event subscription. /// [Parameter(Mandatory = true, Position = 0, ValueFromPipelineByPropertyName = true, ParameterSetName = "BySource")] public string SourceIdentifier @@ -25,6 +24,7 @@ public string SourceIdentifier { return _sourceIdentifier; } + set { _sourceIdentifier = value; @@ -35,19 +35,19 @@ public string SourceIdentifier } } } + private string _sourceIdentifier = null; /// - /// An identifier for this event subscription + /// An identifier for this event subscription. /// [Parameter(Mandatory = true, Position = 0, ValueFromPipelineByPropertyName = true, ParameterSetName = "ById")] public int SubscriptionId { get; set; } = -1; /// - /// Flag that determines if we should include subscriptions used to support - /// other subscriptions + /// Flag that determines if we should include subscriptions used to support other subscriptions. /// - [Parameter()] + [Parameter] public SwitchParameter Force { get; set; } #endregion parameters @@ -56,7 +56,7 @@ public string SourceIdentifier private bool _foundMatch = false; /// - /// Unsubscribe from the event + /// Unsubscribe from the event. /// protected override void ProcessRecord() { @@ -80,7 +80,7 @@ protected override void ProcessRecord() _foundMatch = true; if (ShouldProcess( - String.Format( + string.Format( System.Globalization.CultureInfo.CurrentCulture, EventingStrings.EventSubscription, subscriber.SourceIdentifier), @@ -97,9 +97,9 @@ protected override void ProcessRecord() (!WildcardPattern.ContainsWildcardCharacters(_sourceIdentifier)) && (!_foundMatch)) { - ErrorRecord errorRecord = new ErrorRecord( + ErrorRecord errorRecord = new( new ArgumentException( - String.Format( + string.Format( System.Globalization.CultureInfo.CurrentCulture, EventingStrings.EventSubscriptionNotFound, _sourceIdentifier)), "INVALID_SOURCE_IDENTIFIER", @@ -111,9 +111,9 @@ protected override void ProcessRecord() else if ((SubscriptionId >= 0) && (!_foundMatch)) { - ErrorRecord errorRecord = new ErrorRecord( + ErrorRecord errorRecord = new( new ArgumentException( - String.Format( + string.Format( System.Globalization.CultureInfo.CurrentCulture, EventingStrings.EventSubscriptionNotFound, SubscriptionId)), "INVALID_SUBSCRIPTION_IDENTIFIER", @@ -124,4 +124,4 @@ protected override void ProcessRecord() } } } -} \ No newline at end of file +} diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Update-Data.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Update-Data.cs index 3db55e855b7..4a3198d2911 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Update-Data.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Update-Data.cs @@ -1,43 +1,41 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. using System; -using System.Management.Automation; using System.Collections.ObjectModel; - +using System.Management.Automation; namespace Microsoft.PowerShell.Commands { /// - /// This is the base class for update-typedata and update-formatdata + /// This is the base class for update-typedata and update-formatdata. /// public class UpdateData : PSCmdlet { /// - /// File parameter set name + /// File parameter set name. /// protected const string FileParameterSet = "FileSet"; /// - /// Files to append to the existing set + /// Files to append to the existing set. /// [Parameter(Position = 0, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true, ParameterSetName = FileParameterSet)] [Alias("PSPath", "Path")] [ValidateNotNull] - public string[] AppendPath { set; get; } = Utils.EmptyArray(); + public string[] AppendPath { get; set; } = Array.Empty(); /// - /// Files to prepend to the existing set + /// Files to prepend to the existing set. /// [Parameter(ParameterSetName = FileParameterSet)] [ValidateNotNull] - public string[] PrependPath { set; get; } = Utils.EmptyArray(); + public string[] PrependPath { get; set; } = Array.Empty(); private static void ReportWrongExtension(string file, string errorId, PSCmdlet cmdlet) { - ErrorRecord errorRecord = new ErrorRecord( + ErrorRecord errorRecord = new( PSTraceSource.NewInvalidOperationException(UpdateDataStrings.UpdateData_WrongExtension, file, "ps1xml"), errorId, ErrorCategory.InvalidArgument, @@ -47,7 +45,7 @@ private static void ReportWrongExtension(string file, string errorId, PSCmdlet c private static void ReportWrongProviderType(string providerId, string errorId, PSCmdlet cmdlet) { - ErrorRecord errorRecord = new ErrorRecord( + ErrorRecord errorRecord = new( PSTraceSource.NewInvalidOperationException(UpdateDataStrings.UpdateData_WrongProviderError, providerId), errorId, ErrorCategory.InvalidArgument, @@ -56,7 +54,6 @@ private static void ReportWrongProviderType(string providerId, string errorId, P } /// - /// /// /// /// @@ -64,7 +61,7 @@ private static void ReportWrongProviderType(string providerId, string errorId, P /// internal static Collection Glob(string[] files, string errorId, PSCmdlet cmdlet) { - Collection retValue = new Collection(); + Collection retValue = new(); foreach (string file in files) { Collection providerPaths; @@ -78,11 +75,13 @@ internal static Collection Glob(string[] files, string errorId, PSCmdlet cmdlet.WriteError(new ErrorRecord(e, errorId, ErrorCategory.InvalidOperation, file)); continue; } + if (!provider.NameEquals(cmdlet.Context.ProviderNames.FileSystem)) { ReportWrongProviderType(provider.FullName, errorId, cmdlet); continue; } + foreach (string providerPath in providerPaths) { if (!providerPath.EndsWith(".ps1xml", StringComparison.OrdinalIgnoreCase)) @@ -90,6 +89,7 @@ internal static Collection Glob(string[] files, string errorId, PSCmdlet ReportWrongExtension(providerPath, "WrongExtension", cmdlet); continue; } + retValue.Add(providerPath); } } diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Update-List.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Update-List.cs new file mode 100644 index 00000000000..99507d0461b --- /dev/null +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Update-List.cs @@ -0,0 +1,189 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections; +using System.Diagnostics.CodeAnalysis; +using System.Management.Automation; + +namespace Microsoft.PowerShell.Commands +{ + /// + /// This cmdlet updates the property of incoming objects and passes them to the + /// pipeline. This cmdlet also returns a .NET object with properties that + /// defines the update action on a list. + /// This cmdlet is most helpful when the cmdlet author wants the user to do + /// update action on object list that are not directly exposed through + /// cmdlet parameter. One wants to update a property value which is a list + /// (multi-valued parameter for a cmdlet), without exposing the list. + /// + [Cmdlet(VerbsData.Update, "List", DefaultParameterSetName = "AddRemoveSet", + HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2109383", RemotingCapability = RemotingCapability.None)] + public class UpdateListCommand : PSCmdlet + { + /// + /// The following is the definition of the input parameter "Add". + /// Objects to add to the list. + /// + [Parameter(ParameterSetName = "AddRemoveSet")] + [ValidateNotNullOrEmpty] + [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Justification = "Cmdlets use arrays for parameters.")] + public object[] Add { get; set; } + + /// + /// The following is the definition of the input parameter "Remove". + /// Objects to be removed from the list. + /// + [Parameter(ParameterSetName = "AddRemoveSet")] + [ValidateNotNullOrEmpty] + [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Justification = "Cmdlets use arrays for parameters.")] + public object[] Remove { get; set; } + + /// + /// The following is the definition of the input parameter "Replace". + /// Objects in this list replace the objects in the target list. + /// + [Parameter(Mandatory = true, ParameterSetName = "ReplaceSet")] + [ValidateNotNullOrEmpty] + [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Justification = "Cmdlets use arrays for parameters.")] + public object[] Replace { get; set; } + + /// + /// The following is the definition of the input parameter "InputObject". + /// List of InputObjects where the updates needs to applied to the specific property. + /// + // [Parameter(ValueFromPipeline = true, ParameterSetName = "AddRemoveSet")] + // [Parameter(ValueFromPipeline = true, ParameterSetName = "ReplaceSet")] + [Parameter(ValueFromPipeline = true)] + [ValidateNotNullOrEmpty] + public PSObject InputObject { get; set; } + + /// + /// The following is the definition of the input parameter "Property". + /// Defines which property of the input object should be updated with Add and Remove actions. + /// + // [Parameter(Position = 0, ParameterSetName = "AddRemoveSet")] + // [Parameter(Position = 0, ParameterSetName = "ReplaceSet")] + [Parameter(Position = 0)] + [ValidateNotNullOrEmpty] + public string Property { get; set; } + + private PSListModifier _listModifier; + + /// + /// ProcessRecord method. + /// + protected override void ProcessRecord() + { + if (Property != null) + { + if (InputObject == null) + { + WriteError(NewError("MissingInputObjectParameter", "MissingInputObjectParameter", null)); + } + else + { + _listModifier ??= CreatePSListModifier(); + + PSMemberInfo memberInfo = InputObject.Members[Property]; + if (memberInfo != null) + { + try + { + _listModifier.ApplyTo(memberInfo.Value); + WriteObject(InputObject); + } + catch (PSInvalidOperationException e) + { + WriteError(new ErrorRecord(e, "ApplyFailed", ErrorCategory.InvalidOperation, null)); + } + } + else + { + WriteError(NewError("MemberDoesntExist", "MemberDoesntExist", InputObject, Property)); + } + } + } + } + + /// + /// EndProcessing method. + /// + protected override void EndProcessing() + { + if (Property == null) + { + if (InputObject != null) + { + ThrowTerminatingError(NewError("MissingPropertyParameter", "MissingPropertyParameter", null)); + } + else + { + WriteObject(CreateHashtable()); + } + } + } + + private Hashtable CreateHashtable() + { + Hashtable hash = new(2); + if (Add != null) + { + hash.Add("Add", Add); + } + + if (Remove != null) + { + hash.Add("Remove", Remove); + } + + if (Replace != null) + { + hash.Add("Replace", Replace); + } + + return hash; + } + + private PSListModifier CreatePSListModifier() + { + PSListModifier listModifier = new(); + if (Add != null) + { + foreach (object obj in Add) + { + listModifier.Add.Add(obj); + } + } + + if (Remove != null) + { + foreach (object obj in Remove) + { + listModifier.Remove.Add(obj); + } + } + + if (Replace != null) + { + foreach (object obj in Replace) + { + listModifier.Replace.Add(obj); + } + } + + return listModifier; + } + + private ErrorRecord NewError(string errorId, string resourceId, object targetObject, params object[] args) + { + ErrorDetails details = new(this.GetType().Assembly, "UpdateListStrings", resourceId, args); + ErrorRecord errorRecord = new( + new InvalidOperationException(details.Message), + errorId, + ErrorCategory.InvalidOperation, + targetObject); + return errorRecord; + } + } +} diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Update-TypeData.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Update-TypeData.cs index b8097e486f2..b73d8570040 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Update-TypeData.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Update-TypeData.cs @@ -1,25 +1,25 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. using System; using System.Collections.Concurrent; using System.Collections.Generic; -using System.Globalization; -using System.Reflection; -using System.Management.Automation; using System.Collections.ObjectModel; using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Management.Automation; using System.Management.Automation.Runspaces; -using Dbg = System.Management.Automation.Diagnostics; +using System.Reflection; +using Dbg = System.Management.Automation.Diagnostics; namespace Microsoft.PowerShell.Commands { /// /// This class implements update-typeData command. /// - [Cmdlet(VerbsData.Update, "TypeData", SupportsShouldProcess = true, DefaultParameterSetName = FileParameterSet, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113421")] + [Cmdlet(VerbsData.Update, "TypeData", SupportsShouldProcess = true, ConfirmImpact = ConfirmImpact.Low, + DefaultParameterSetName = FileParameterSet, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2097131")] public class UpdateTypeDataCommand : UpdateData { #region dynamic type set @@ -28,7 +28,8 @@ public class UpdateTypeDataCommand : UpdateData private const string DynamicTypeSet = "DynamicTypeSet"; private const string TypeDataSet = "TypeDataSet"; - private static object s_notSpecified = new object(); + private static readonly object s_notSpecified = new(); + private static bool HasBeenSpecified(object obj) { return !System.Object.ReferenceEquals(obj, s_notSpecified); @@ -37,7 +38,7 @@ private static bool HasBeenSpecified(object obj) private PSMemberTypes _memberType; private bool _isMemberTypeSet = false; /// - /// The member type of to be added + /// The member type of to be added. /// [Parameter(ParameterSetName = DynamicTypeSet)] [ValidateNotNullOrEmpty] @@ -49,24 +50,29 @@ private static bool HasBeenSpecified(object obj) System.Management.Automation.Runspaces.TypeData.CodeMethod, IgnoreCase = true)] public PSMemberTypes MemberType { + get + { + return _memberType; + } + set { _memberType = value; _isMemberTypeSet = true; } - get { return _memberType; } } private string _memberName; /// - /// The name of the new member + /// The name of the new member. /// [Parameter(ParameterSetName = DynamicTypeSet)] [ValidateNotNullOrEmpty] public string MemberName { - set { _memberName = value; } get { return _memberName; } + + set { _memberName = value; } } private object _value1 = s_notSpecified; @@ -77,8 +83,9 @@ public string MemberName [Parameter(ParameterSetName = DynamicTypeSet)] public object Value { - set { _value1 = value; } get { return _value1; } + + set { _value1 = value; } } private object _value2; @@ -90,142 +97,147 @@ public object Value [ValidateNotNull] public object SecondValue { - set { _value2 = value; } get { return _value2; } + + set { _value2 = value; } } private Type _typeConverter; /// - /// The type converter to be added + /// The type converter to be added. /// [Parameter(ParameterSetName = DynamicTypeSet)] [ValidateNotNull] public Type TypeConverter { - set { _typeConverter = value; } get { return _typeConverter; } + + set { _typeConverter = value; } } private Type _typeAdapter; /// - /// The type adapter to be added + /// The type adapter to be added. /// [Parameter(ParameterSetName = DynamicTypeSet)] [ValidateNotNull] public Type TypeAdapter { - set { _typeAdapter = value; } get { return _typeAdapter; } + + set { _typeAdapter = value; } } /// - /// SerializationMethod + /// SerializationMethod. /// [Parameter(ParameterSetName = DynamicTypeSet)] [ValidateNotNullOrEmpty] public string SerializationMethod { - set { _serializationMethod = value; } get { return _serializationMethod; } - } + set { _serializationMethod = value; } + } /// - /// TargetTypeForDeserialization + /// TargetTypeForDeserialization. /// [Parameter(ParameterSetName = DynamicTypeSet)] [ValidateNotNull] public Type TargetTypeForDeserialization { - set { _targetTypeForDeserialization = value; } get { return _targetTypeForDeserialization; } - } + set { _targetTypeForDeserialization = value; } + } /// - /// SerializationDepth + /// SerializationDepth. /// [Parameter(ParameterSetName = DynamicTypeSet)] [ValidateNotNull] [ValidateRange(0, int.MaxValue)] public int SerializationDepth { - set { _serializationDepth = value; } get { return _serializationDepth; } - } + set { _serializationDepth = value; } + } /// - /// DefaultDisplayProperty + /// DefaultDisplayProperty. /// [Parameter(ParameterSetName = DynamicTypeSet)] [ValidateNotNullOrEmpty] public string DefaultDisplayProperty { - set { _defaultDisplayProperty = value; } get { return _defaultDisplayProperty; } - } + set { _defaultDisplayProperty = value; } + } /// - /// InheritPropertySerializationSet + /// InheritPropertySerializationSet. /// [Parameter(ParameterSetName = DynamicTypeSet)] [ValidateNotNull] - public Nullable InheritPropertySerializationSet + public bool? InheritPropertySerializationSet { - set { _inheritPropertySerializationSet = value; } get { return _inheritPropertySerializationSet; } - } + set { _inheritPropertySerializationSet = value; } + } /// - /// StringSerializationSource + /// StringSerializationSource. /// [Parameter(ParameterSetName = DynamicTypeSet)] [ValidateNotNullOrEmpty] public string StringSerializationSource { - set { _stringSerializationSource = value; } get { return _stringSerializationSource; } - } + set { _stringSerializationSource = value; } + } /// - /// DefaultDisplayPropertySet + /// DefaultDisplayPropertySet. /// [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Justification = "Cmdlets use arrays for parameters.")] [Parameter(ParameterSetName = DynamicTypeSet)] [ValidateNotNullOrEmpty] public string[] DefaultDisplayPropertySet { - set { _defaultDisplayPropertySet = value; } get { return _defaultDisplayPropertySet; } + + set { _defaultDisplayPropertySet = value; } } /// - /// DefaultKeyPropertySet + /// DefaultKeyPropertySet. /// [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Justification = "Cmdlets use arrays for parameters.")] [Parameter(ParameterSetName = DynamicTypeSet)] [ValidateNotNullOrEmpty] public string[] DefaultKeyPropertySet { - set { _defaultKeyPropertySet = value; } get { return _defaultKeyPropertySet; } - } + set { _defaultKeyPropertySet = value; } + } /// - /// PropertySerializationSet + /// PropertySerializationSet. /// [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Justification = "Cmdlets use arrays for parameters.")] [Parameter(ParameterSetName = DynamicTypeSet)] [ValidateNotNullOrEmpty] public string[] PropertySerializationSet { - set { _propertySerializationSet = value; } get { return _propertySerializationSet; } + + set { _propertySerializationSet = value; } } // These members are represented as NoteProperty in types.ps1xml @@ -233,7 +245,7 @@ public string[] PropertySerializationSet private Type _targetTypeForDeserialization; private int _serializationDepth = int.MinValue; private string _defaultDisplayProperty; - private Nullable _inheritPropertySerializationSet; + private bool? _inheritPropertySerializationSet; // These members are represented as AliasProperty in types.ps1xml private string _stringSerializationSource; @@ -245,27 +257,29 @@ public string[] PropertySerializationSet private string _typeName; /// - /// The type name we want to update on + /// The type name we want to update on. /// [Parameter(Mandatory = true, ValueFromPipeline = true, ParameterSetName = DynamicTypeSet)] - [ArgumentToTypeNameTransformationAttribute()] + [ArgumentToTypeNameTransformation] [ValidateNotNullOrEmpty] public string TypeName { - set { _typeName = value; } get { return _typeName; } + + set { _typeName = value; } } private bool _force = false; /// - /// True if we should overwrite a possibly existing member + /// True if we should overwrite a possibly existing member. /// [Parameter(ParameterSetName = DynamicTypeSet)] [Parameter(ParameterSetName = TypeDataSet)] public SwitchParameter Force { - set { _force = value; } get { return _force; } + + set { _force = value; } } #endregion dynamic type set @@ -274,20 +288,21 @@ public SwitchParameter Force private TypeData[] _typeData; /// - /// The TypeData instances + /// The TypeData instances. /// [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Justification = "Cmdlets use arrays for parameters.")] [Parameter(Mandatory = true, Position = 0, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true, ParameterSetName = TypeDataSet)] public TypeData[] TypeData { - set { _typeData = value; } get { return _typeData; } + + set { _typeData = value; } } #endregion strong type data set /// - /// This method verify if the Type Table is shared and cannot be updated + /// This method verify if the Type Table is shared and cannot be updated. /// protected override void BeginProcessing() { @@ -299,7 +314,7 @@ protected override void BeginProcessing() } /// - /// This method implements the ProcessRecord method for update-typeData command + /// This method implements the ProcessRecord method for update-typeData command. /// protected override void ProcessRecord() { @@ -318,7 +333,7 @@ protected override void ProcessRecord() } /// - /// This method implements the EndProcessing method for update-typeData command + /// This method implements the EndProcessing method for update-typeData command. /// protected override void EndProcessing() { @@ -339,6 +354,7 @@ private void ProcessStrongTypeData() { continue; } + TypeData type = item.Copy(); // Set property IsOverride to be true if -Force parameter is specified @@ -355,28 +371,24 @@ private void ProcessStrongTypeData() var errors = new ConcurrentBag(); this.Context.TypeTable.Update(type, errors, false); // Write out errors... - if (errors.Count > 0) + if (!errors.IsEmpty) { foreach (string s in errors) { - RuntimeException rte = new RuntimeException(s); + RuntimeException rte = new(s); this.WriteError(new ErrorRecord(rte, "TypesDynamicUpdateException", ErrorCategory.InvalidOperation, null)); } } else { // Update successfully, we add the TypeData into cache - if (Context.RunspaceConfiguration != null) - { - Context.RunspaceConfiguration.Types.Append(new TypeConfigurationEntry(type, false)); - } - else if (Context.InitialSessionState != null) + if (Context.InitialSessionState != null) { Context.InitialSessionState.Types.Add(new SessionStateTypeEntry(type, false)); } else { - Dbg.Assert(false, "Either RunspaceConfiguration or InitialSessionState must be non-null for Update-Typedata to work"); + Dbg.Assert(false, "InitialSessionState must be non-null for Update-Typedata to work"); } } } @@ -393,15 +405,16 @@ private void ProcessStrongTypeData() #region dynamic type processing /// - /// Process the dynamic type update + /// Process the dynamic type update. /// private void ProcessDynamicType() { - if (String.IsNullOrWhiteSpace(_typeName)) + if (string.IsNullOrWhiteSpace(_typeName)) { ThrowTerminatingError(NewError("TargetTypeNameEmpty", UpdateDataStrings.TargetTypeNameEmpty, _typeName)); } - TypeData type = new TypeData(_typeName) { IsOverride = _force }; + + TypeData type = new(_typeName) { IsOverride = _force }; GetMembers(type.Members); @@ -409,6 +422,7 @@ private void ProcessDynamicType() { type.TypeConverter = _typeConverter; } + if (_typeAdapter != null) { type.TypeAdapter = _typeAdapter; @@ -418,39 +432,47 @@ private void ProcessDynamicType() { type.SerializationMethod = _serializationMethod; } + if (_targetTypeForDeserialization != null) { type.TargetTypeForDeserialization = _targetTypeForDeserialization; } + if (_serializationDepth != int.MinValue) { type.SerializationDepth = (uint)_serializationDepth; } + if (_defaultDisplayProperty != null) { type.DefaultDisplayProperty = _defaultDisplayProperty; } + if (_inheritPropertySerializationSet != null) { type.InheritPropertySerializationSet = _inheritPropertySerializationSet.Value; } + if (_stringSerializationSource != null) { type.StringSerializationSource = _stringSerializationSource; } + if (_defaultDisplayPropertySet != null) { - PropertySetData defaultDisplayPropertySet = new PropertySetData(_defaultDisplayPropertySet); + PropertySetData defaultDisplayPropertySet = new(_defaultDisplayPropertySet); type.DefaultDisplayPropertySet = defaultDisplayPropertySet; } + if (_defaultKeyPropertySet != null) { - PropertySetData defaultKeyPropertySet = new PropertySetData(_defaultKeyPropertySet); + PropertySetData defaultKeyPropertySet = new(_defaultKeyPropertySet); type.DefaultKeyPropertySet = defaultKeyPropertySet; } + if (_propertySerializationSet != null) { - PropertySetData propertySerializationSet = new PropertySetData(_propertySerializationSet); + PropertySetData propertySerializationSet = new(_propertySerializationSet); type.PropertySerializationSet = propertySerializationSet; } @@ -473,28 +495,24 @@ private void ProcessDynamicType() var errors = new ConcurrentBag(); this.Context.TypeTable.Update(type, errors, false); // Write out errors... - if (errors.Count > 0) + if (!errors.IsEmpty) { foreach (string s in errors) { - RuntimeException rte = new RuntimeException(s); + RuntimeException rte = new(s); this.WriteError(new ErrorRecord(rte, "TypesDynamicUpdateException", ErrorCategory.InvalidOperation, null)); } } else { // Update successfully, we add the TypeData into cache - if (Context.RunspaceConfiguration != null) - { - Context.RunspaceConfiguration.Types.Append(new TypeConfigurationEntry(type, false)); - } - else if (Context.InitialSessionState != null) + if (Context.InitialSessionState != null) { Context.InitialSessionState.Types.Add(new SessionStateTypeEntry(type, false)); } else { - Dbg.Assert(false, "Either RunspaceConfiguration or InitialSessionState must be non-null for Update-Typedata to work"); + Dbg.Assert(false, "InitialSessionState must be non-null for Update-Typedata to work"); } } } @@ -506,7 +524,7 @@ private void ProcessDynamicType() } /// - /// Get the members for the TypeData + /// Get the members for the TypeData. /// /// private void GetMembers(Dictionary members) @@ -519,6 +537,7 @@ private void GetMembers(Dictionary members) { ThrowTerminatingError(NewError("MemberTypeIsMissing", UpdateDataStrings.MemberTypeIsMissing, null)); } + return; } @@ -554,14 +573,14 @@ private void GetMembers(Dictionary members) } } - private T GetParameterType(object sourceValue) + private static T GetParameterType(object sourceValue) { return (T)LanguagePrimitives.ConvertTo(sourceValue, typeof(T), CultureInfo.InvariantCulture); } private void EnsureMemberNameHasBeenSpecified() { - if (String.IsNullOrEmpty(_memberName)) + if (string.IsNullOrEmpty(_memberName)) { ThrowTerminatingError(NewError("MemberNameShouldBeSpecified", UpdateDataStrings.ShouldBeSpecified, null, "MemberName", _memberType)); } @@ -579,10 +598,11 @@ private void EnsureValue1NotNullOrEmpty() { if (_value1 is string) { - if (String.IsNullOrEmpty((string)_value1)) + if (string.IsNullOrEmpty((string)_value1)) { ThrowTerminatingError(NewError("ValueShouldBeSpecified", UpdateDataStrings.ShouldNotBeNull, null, "Value", _memberType)); } + return; } @@ -609,10 +629,10 @@ private void EnsureValue1AndValue2AreNotBothNull() } /// - /// Check if the TypeData instance contains no members + /// Check if the TypeData instance contains no members. /// /// - /// false if empty, true if not + /// False if empty, true if not. private bool EnsureTypeDataIsNotEmpty(TypeData typeData) { if (typeData.Members.Count == 0 && typeData.StandardMembers.Count == 0 @@ -624,6 +644,7 @@ private bool EnsureTypeDataIsNotEmpty(TypeData typeData) this.WriteError(NewError("TypeDataEmpty", UpdateDataStrings.TypeDataEmpty, null, typeData.TypeName)); return false; } + return true; } @@ -649,6 +670,7 @@ private AliasPropertyData GetAliasProperty() alias = new AliasPropertyData(_memberName, referencedName, type); return alias; } + alias = new AliasPropertyData(_memberName, referencedName); return alias; } @@ -671,7 +693,7 @@ private ScriptPropertyData GetScriptProperty() value2ScriptBlock = GetParameterType(_value2); } - ScriptPropertyData scriptProperty = new ScriptPropertyData(_memberName, value1ScriptBlock, value2ScriptBlock); + ScriptPropertyData scriptProperty = new(_memberName, value1ScriptBlock, value2ScriptBlock); return scriptProperty; } @@ -693,7 +715,7 @@ private CodePropertyData GetCodeProperty() value2CodeReference = GetParameterType(_value2); } - CodePropertyData codeProperty = new CodePropertyData(_memberName, value1CodeReference, value2CodeReference); + CodePropertyData codeProperty = new(_memberName, value1CodeReference, value2CodeReference); return codeProperty; } @@ -704,7 +726,7 @@ private ScriptMethodData GetScriptMethod() EnsureValue2HasNotBeenSpecified(); ScriptBlock method = GetParameterType(_value1); - ScriptMethodData scriptMethod = new ScriptMethodData(_memberName, method); + ScriptMethodData scriptMethod = new(_memberName, method); return scriptMethod; } @@ -715,22 +737,22 @@ private CodeMethodData GetCodeMethod() EnsureValue2HasNotBeenSpecified(); MethodInfo codeReference = GetParameterType(_value1); - CodeMethodData codeMethod = new CodeMethodData(_memberName, codeReference); + CodeMethodData codeMethod = new(_memberName, codeReference); return codeMethod; } /// - /// Generate error record + /// Generate error record. /// /// /// /// /// /// - private ErrorRecord NewError(string errorId, string template, object targetObject, params object[] args) + private static ErrorRecord NewError(string errorId, string template, object targetObject, params object[] args) { string message = string.Format(CultureInfo.CurrentCulture, template, args); - ErrorRecord errorRecord = new ErrorRecord( + ErrorRecord errorRecord = new( new InvalidOperationException(message), errorId, ErrorCategory.InvalidOperation, @@ -757,38 +779,7 @@ private void ProcessTypeFiles() // filename is available string target = UpdateDataStrings.UpdateTarget; - if (Context.RunspaceConfiguration != null) - { - for (int i = prependPathTotal.Count - 1; i >= 0; i--) - { - string formattedTarget = string.Format(CultureInfo.InvariantCulture, target, prependPathTotal[i]); - - if (ShouldProcess(formattedTarget, action)) - { - this.Context.RunspaceConfiguration.Types.Prepend(new TypeConfigurationEntry(prependPathTotal[i])); - } - } - - foreach (string appendPathTotalItem in appendPathTotal) - { - string formattedTarget = string.Format(CultureInfo.InvariantCulture, target, appendPathTotalItem); - - if (ShouldProcess(formattedTarget, action)) - { - this.Context.RunspaceConfiguration.Types.Append(new TypeConfigurationEntry(appendPathTotalItem)); - } - } - - try - { - this.Context.CurrentRunspace.RunspaceConfiguration.Types.Update(true); - } - catch (RuntimeException e) - { - this.WriteError(new ErrorRecord(e, "TypesXmlUpdateException", ErrorCategory.InvalidOperation, null)); - } - } - else if (Context.InitialSessionState != null) + if (Context.InitialSessionState != null) { // This hashSet is to detect if there are duplicate type files var fullFileNameHash = new HashSet(StringComparer.CurrentCultureIgnoreCase); @@ -801,9 +792,8 @@ private void ProcessTypeFiles() if (ShouldProcess(formattedTarget, action)) { - if (!fullFileNameHash.Contains(resolvedPath)) + if (fullFileNameHash.Add(resolvedPath)) { - fullFileNameHash.Add(resolvedPath); newTypes.Add(new SessionStateTypeEntry(prependPathTotal[i])); } } @@ -815,9 +805,8 @@ private void ProcessTypeFiles() if (entry.FileName != null) { string resolvedPath = ModuleCmdletBase.ResolveRootedFilePath(entry.FileName, Context) ?? entry.FileName; - if (!fullFileNameHash.Contains(resolvedPath)) + if (fullFileNameHash.Add(resolvedPath)) { - fullFileNameHash.Add(resolvedPath); newTypes.Add(entry); } } @@ -834,9 +823,8 @@ private void ProcessTypeFiles() if (ShouldProcess(formattedTarget, action)) { - if (!fullFileNameHash.Contains(resolvedPath)) + if (fullFileNameHash.Add(resolvedPath)) { - fullFileNameHash.Add(resolvedPath); newTypes.Add(new SessionStateTypeEntry(appendPathTotalItem)); } } @@ -858,8 +846,7 @@ private void ProcessTypeFiles() } else if (sste.FileName != null) { - bool unused; - Context.TypeTable.Update(sste.FileName, sste.FileName, errors, Context.AuthorizationManager, Context.InitialSessionState.Host, out unused); + Context.TypeTable.Update(sste.FileName, sste.FileName, errors, Context.AuthorizationManager, Context.InitialSessionState.Host, out _); } else { @@ -874,20 +861,21 @@ private void ProcessTypeFiles() Context.InitialSessionState.Types.Add(sste); // Write out any errors... - if (errors.Count > 0) + if (!errors.IsEmpty) { foreach (string s in errors) { - RuntimeException rte = new RuntimeException(s); + RuntimeException rte = new(s); this.WriteError(new ErrorRecord(rte, "TypesXmlUpdateException", ErrorCategory.InvalidOperation, null)); } + errors = new ConcurrentBag(); } } } else { - Dbg.Assert(false, "Either RunspaceConfiguration or InitialSessionState must be non-null for Update-Typedata to work"); + Dbg.Assert(false, "InitialSessionState must be non-null for Update-Typedata to work"); } } @@ -897,11 +885,12 @@ private void ProcessTypeFiles() /// /// This class implements update-typeData command. /// - [Cmdlet(VerbsData.Update, "FormatData", SupportsShouldProcess = true, DefaultParameterSetName = FileParameterSet, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113420")] + [Cmdlet(VerbsData.Update, "FormatData", SupportsShouldProcess = true, ConfirmImpact = ConfirmImpact.Low, + DefaultParameterSetName = FileParameterSet, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2097135")] public class UpdateFormatDataCommand : UpdateData { /// - /// This method verify if the Format database manager is shared and cannot be updated + /// This method verify if the Format database manager is shared and cannot be updated. /// protected override void BeginProcessing() { @@ -913,7 +902,7 @@ protected override void BeginProcessing() } /// - /// This method implements the ProcessRecord method for update-FormatData command + /// This method implements the ProcessRecord method for update-FormatData command. /// protected override void ProcessRecord() { @@ -931,38 +920,7 @@ protected override void ProcessRecord() // filename is available string target = UpdateDataStrings.UpdateTarget; - if (Context.RunspaceConfiguration != null) - { - for (int i = prependPathTotal.Count - 1; i >= 0; i--) - { - string formattedTarget = string.Format(CultureInfo.CurrentCulture, target, prependPathTotal[i]); - - if (ShouldProcess(formattedTarget, action)) - { - this.Context.RunspaceConfiguration.Formats.Prepend(new FormatConfigurationEntry(prependPathTotal[i])); - } - } - - foreach (string appendPathTotalItem in appendPathTotal) - { - string formattedTarget = string.Format(CultureInfo.CurrentCulture, target, appendPathTotalItem); - - if (ShouldProcess(formattedTarget, action)) - { - this.Context.RunspaceConfiguration.Formats.Append(new FormatConfigurationEntry(appendPathTotalItem)); - } - } - - try - { - this.Context.CurrentRunspace.RunspaceConfiguration.Formats.Update(true); - } - catch (RuntimeException e) - { - this.WriteError(new ErrorRecord(e, "FormatXmlUpdateException", ErrorCategory.InvalidOperation, null)); - } - } - else if (Context.InitialSessionState != null) + if (Context.InitialSessionState != null) { if (Context.InitialSessionState.DisableFormatUpdates) { @@ -1010,14 +968,14 @@ protected override void ProcessRecord() if (ShouldProcess(formattedTarget, action)) { - if (!fullFileNameHash.Contains(appendPathTotalItem)) + if (fullFileNameHash.Add(appendPathTotalItem)) { - fullFileNameHash.Add(appendPathTotalItem); newFormats.Add(new SessionStateFormatEntry(appendPathTotalItem)); } } } + var originalFormats = Context.InitialSessionState.Formats; try { // Always rebuild the format information @@ -1055,30 +1013,32 @@ protected override void ProcessRecord() if (entries.Count > 0) { Context.FormatDBManager.UpdateDataBase(entries, this.Context.AuthorizationManager, this.Context.EngineHostInterface, false); - FormatAndTypeDataHelper.ThrowExceptionOnError( - "ErrorsUpdatingFormats", - null, - entries, - RunspaceConfigurationCategory.Formats); + FormatAndTypeDataHelper.ThrowExceptionOnError("ErrorsUpdatingFormats", + null, + entries, + FormatAndTypeDataHelper.Category.Formats); } } catch (RuntimeException e) { + // revert Formats if there is a failure + Context.InitialSessionState.Formats.Clear(); + Context.InitialSessionState.Formats.Add(originalFormats); this.WriteError(new ErrorRecord(e, "FormatXmlUpdateException", ErrorCategory.InvalidOperation, null)); } } else { - Dbg.Assert(false, "Either RunspaceConfiguration or InitialSessionState must be non-null for Update-FormatData to work"); + Dbg.Assert(false, "InitialSessionState must be non-null for Update-FormatData to work"); } } } /// - /// Remove-TypeData cmdlet + /// Remove-TypeData cmdlet. /// [Cmdlet(VerbsCommon.Remove, "TypeData", SupportsShouldProcess = true, DefaultParameterSetName = RemoveTypeDataSet, - HelpUri = "https://go.microsoft.com/fwlink/?LinkID=217038")] + HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2096622")] public class RemoveTypeDataCommand : PSCmdlet { private const string RemoveTypeSet = "RemoveTypeSet"; @@ -1086,21 +1046,24 @@ public class RemoveTypeDataCommand : PSCmdlet private const string RemoveTypeDataSet = "RemoveTypeDataSet"; private string _typeName; + /// - /// The target type to remove + /// The target type to remove. /// [Parameter(Mandatory = true, Position = 0, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true, ParameterSetName = RemoveTypeSet)] - [ArgumentToTypeNameTransformationAttribute()] + [ArgumentToTypeNameTransformation] [ValidateNotNullOrEmpty] public string TypeName { get { return _typeName; } + set { _typeName = value; } } private string[] _typeFiles; + /// - /// The type xml file to remove from the cache + /// The type xml file to remove from the cache. /// [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Justification = "Cmdlets use arrays for parameters.")] [Parameter(Mandatory = true, ParameterSetName = RemoveFileSet)] @@ -1108,17 +1071,20 @@ public string TypeName public string[] Path { get { return _typeFiles; } + set { _typeFiles = value; } } private TypeData _typeData; + /// - /// The TypeData to remove + /// The TypeData to remove. /// [Parameter(Mandatory = true, ValueFromPipeline = true, ParameterSetName = RemoveTypeDataSet)] public TypeData TypeData { get { return _typeData; } + set { _typeData = value; } } @@ -1136,7 +1102,7 @@ private static void ConstructFileToIndexMap(string fileName, int index, Dictiona } /// - /// This method implements the ProcessRecord method for Remove-TypeData command + /// This method implements the ProcessRecord method for Remove-TypeData command. /// protected override void ProcessRecord() { @@ -1147,29 +1113,25 @@ protected override void ProcessRecord() string removeFileTarget = UpdateDataStrings.UpdateTarget; Collection typeFileTotal = UpdateData.Glob(_typeFiles, "TypePathException", this); - if (typeFileTotal.Count == 0) { return; } + if (typeFileTotal.Count == 0) + { + return; + } // Key of the map is the name of the file that is in the cache. Value of the map is a index list. Duplicate files might // exist in the cache because the user can add arbitrary files to the cache by $host.Runspace.InitialSessionState.Types.Add() - Dictionary> fileToIndexMap = new Dictionary>(StringComparer.OrdinalIgnoreCase); - List indicesToRemove = new List(); + Dictionary> fileToIndexMap = new(StringComparer.OrdinalIgnoreCase); + List indicesToRemove = new(); - if (Context.RunspaceConfiguration != null) - { - for (int index = 0; index < Context.RunspaceConfiguration.Types.Count; index++) - { - string fileName = Context.RunspaceConfiguration.Types[index].FileName; - if (fileName == null) { continue; } - - ConstructFileToIndexMap(fileName, index, fileToIndexMap); - } - } - else if (Context.InitialSessionState != null) + if (Context.InitialSessionState != null) { for (int index = 0; index < Context.InitialSessionState.Types.Count; index++) { string fileName = Context.InitialSessionState.Types[index].FileName; - if (fileName == null) { continue; } + if (fileName == null) + { + continue; + } // Resolving the file path because the path to the types file in module manifest is now specified as // ..\..\types.ps1xml which expands to C:\Windows\System32\WindowsPowerShell\v1.0\Modules\Microsoft.PowerShell.Core\..\..\types.ps1xml @@ -1200,23 +1162,12 @@ protected override void ProcessRecord() indicesToRemove.Sort(); for (int i = indicesToRemove.Count - 1; i >= 0; i--) { - if (Context.RunspaceConfiguration != null) - { - Context.RunspaceConfiguration.Types.RemoveItem(indicesToRemove[i]); - } - else if (Context.InitialSessionState != null) - { - Context.InitialSessionState.Types.RemoveItem(indicesToRemove[i]); - } + Context.InitialSessionState?.Types.RemoveItem(indicesToRemove[i]); } try { - if (Context.RunspaceConfiguration != null) - { - Context.RunspaceConfiguration.Types.Update(); - } - else if (Context.InitialSessionState != null) + if (Context.InitialSessionState != null) { bool oldRefreshTypeFormatSetting = Context.InitialSessionState.RefreshTypeAndFormatSetting; try @@ -1250,15 +1201,16 @@ protected override void ProcessRecord() } else { - if (String.IsNullOrWhiteSpace(_typeName)) + if (string.IsNullOrWhiteSpace(_typeName)) { ThrowTerminatingError(NewError("TargetTypeNameEmpty", UpdateDataStrings.TargetTypeNameEmpty, _typeName)); } + typeNameToRemove = _typeName; } - Dbg.Assert(!String.IsNullOrEmpty(typeNameToRemove), "TypeNameToRemove should be not null and not empty at this point"); - TypeData type = new TypeData(typeNameToRemove); + Dbg.Assert(!string.IsNullOrEmpty(typeNameToRemove), "TypeNameToRemove should be not null and not empty at this point"); + TypeData type = new(typeNameToRemove); string removeTypeFormattedTarget = string.Format(CultureInfo.InvariantCulture, removeTypeTarget, typeNameToRemove); if (ShouldProcess(removeTypeFormattedTarget, removeTypeAction)) @@ -1268,28 +1220,24 @@ protected override void ProcessRecord() var errors = new ConcurrentBag(); Context.TypeTable.Update(type, errors, true); // Write out errors... - if (errors.Count > 0) + if (!errors.IsEmpty) { foreach (string s in errors) { - RuntimeException rte = new RuntimeException(s); + RuntimeException rte = new(s); this.WriteError(new ErrorRecord(rte, "TypesDynamicRemoveException", ErrorCategory.InvalidOperation, null)); } } else { // Type is removed successfully, add it into the cache - if (Context.RunspaceConfiguration != null) - { - Context.RunspaceConfiguration.Types.Append(new TypeConfigurationEntry(type, true)); - } - else if (Context.InitialSessionState != null) + if (Context.InitialSessionState != null) { Context.InitialSessionState.Types.Add(new SessionStateTypeEntry(type, true)); } else { - Dbg.Assert(false, "Either RunspaceConfiguration or InitialSessionState must be non-null for Remove-Typedata to work"); + Dbg.Assert(false, "InitialSessionState must be non-null for Remove-Typedata to work"); } } } @@ -1301,17 +1249,17 @@ protected override void ProcessRecord() } /// - /// This method implements the EndProcessing method for Remove-TypeData command + /// This method implements the EndProcessing method for Remove-TypeData command. /// protected override void EndProcessing() { this.Context.TypeTable.ClearConsolidatedMembers(); } - private ErrorRecord NewError(string errorId, string template, object targetObject, params object[] args) + private static ErrorRecord NewError(string errorId, string template, object targetObject, params object[] args) { string message = string.Format(CultureInfo.CurrentCulture, template, args); - ErrorRecord errorRecord = new ErrorRecord( + ErrorRecord errorRecord = new( new InvalidOperationException(message), errorId, ErrorCategory.InvalidOperation, @@ -1321,17 +1269,16 @@ private ErrorRecord NewError(string errorId, string template, object targetObjec } /// - /// Get-TypeData cmdlet + /// Get-TypeData cmdlet. /// - [Cmdlet(VerbsCommon.Get, "TypeData", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=217033")] + [Cmdlet(VerbsCommon.Get, "TypeData", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2097018")] [OutputType(typeof(System.Management.Automation.PSObject))] public class GetTypeDataCommand : PSCmdlet { private WildcardPattern[] _filter; /// - /// Get Formatting information only for the specified - /// typename + /// Get Formatting information only for the specified typename. /// [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] [ValidateNotNullOrEmpty] @@ -1350,7 +1297,7 @@ private void ValidateTypeName() var exception = new InvalidOperationException(UpdateDataStrings.TargetTypeNameEmpty); foreach (string typeName in TypeName) { - if (String.IsNullOrWhiteSpace(typeName)) + if (string.IsNullOrWhiteSpace(typeName)) { WriteError( new ErrorRecord( @@ -1368,6 +1315,7 @@ private void ValidateTypeName() { typeNameInUse = type.FullName; } + typeNames.Add(typeNameInUse); } @@ -1380,15 +1328,13 @@ private void ValidateTypeName() } /// - /// Takes out the content from the database and writes them - /// out + /// Takes out the content from the database and writes it out. /// protected override void ProcessRecord() { ValidateTypeName(); Dictionary alltypes = Context.TypeTable.GetAllTypeData(); - Collection typedefs = new Collection(); foreach (string type in alltypes.Keys) { @@ -1396,17 +1342,11 @@ protected override void ProcessRecord() { if (pattern.IsMatch(type)) { - typedefs.Add(alltypes[type]); + WriteObject(alltypes[type]); break; } } } - - // write out all the available type definitions - foreach (TypeData typedef in typedefs) - { - WriteObject(typedef); - } } } @@ -1414,7 +1354,7 @@ protected override void ProcessRecord() /// To make it easier to specify a TypeName, we add an ArgumentTransformationAttribute here. /// * string: return the string /// * Type: return the Type.ToString() - /// * instance: return instance.GetType().ToString() + /// * instance: return instance.GetType().ToString() . /// internal sealed class ArgumentToTypeNameTransformationAttribute : ArgumentTransformationAttribute { @@ -1449,4 +1389,3 @@ public override object Transform(EngineIntrinsics engineIntrinsics, object input } } } - diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/UtilityCommon.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/UtilityCommon.cs index 36b9576b516..1e3cc038750 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/UtilityCommon.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/UtilityCommon.cs @@ -1,21 +1,18 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. using System; -using System.Management.Automation; -using System.Management.Automation.Runspaces; -using System.Text; using System.Diagnostics.CodeAnalysis; using System.Globalization; +using System.Management.Automation; +using System.Text; - -[module: SuppressMessage("Microsoft.Naming", "CA1711:IdentifiersShouldNotHaveIncorrectSuffix", Scope = "type", Target = "Microsoft.PowerShell.Commands.ByteCollection")] +[module: SuppressMessage("Microsoft.Naming", "CA1711:IdentifiersShouldNotHaveIncorrectSuffix", Scope = "type", Target = "~T:Microsoft.PowerShell.Commands.ByteCollection")] namespace Microsoft.PowerShell.Commands { /// - /// Don't use! The API is obsolete! + /// Don't use! The API is obsolete!. /// [Obsolete("This class is included in this SDK for completeness only. The members of this class cannot be used directly, nor should this class be used to derive other classes.", true)] public enum TextEncodingType @@ -45,6 +42,11 @@ public enum TextEncodingType /// BigEndianUnicode, + /// + /// Big Endian UTF32 encoding. + /// + BigEndianUTF32, + /// /// UTF8 encoding. /// @@ -62,8 +64,9 @@ public enum TextEncodingType } /// - /// Utility class to contain resources for the Microsoft.PowerShell.Utility module + /// Utility class to contain resources for the Microsoft.PowerShell.Utility module. /// + [Obsolete("This class is obsolete", true)] public static class UtilityResources { /// @@ -75,15 +78,10 @@ public static class UtilityResources public static string FileReadError { get { return UtilityCommonStrings.FileReadError; } } /// - /// The resource string used to indicate 'PATH:' in the formating header. + /// The resource string used to indicate 'PATH:' in the formatting header. /// public static string FormatHexPathPrefix { get { return UtilityCommonStrings.FormatHexPathPrefix; } } - /// - /// Error message to indicate that requested algorithm is not supported on the target platform. - /// - public static string AlgorithmTypeNotSupported { get { return UtilityCommonStrings.AlgorithmTypeNotSupported; } } - /// /// The file '{0}' could not be parsed as a PowerShell Data File. /// @@ -96,56 +94,199 @@ public static class UtilityResources public class ByteCollection { /// - /// ByteCollection constructor. + /// Initializes a new instance of the class. + /// + /// The Offset address to be used while displaying the bytes in the collection. + /// Underlying bytes stored in the collection. + /// Indicates the path of the file whose contents are wrapped in the ByteCollection. + [Obsolete("The constructor is deprecated.", true)] + public ByteCollection(uint offset, byte[] value, string path) + : this((ulong)offset, value, path) + { + } + + /// + /// Initializes a new instance of the class. /// /// The Offset address to be used while displaying the bytes in the collection. /// Underlying bytes stored in the collection. /// Indicates the path of the file whose contents are wrapped in the ByteCollection. - public ByteCollection(UInt32 offset, Byte[] value, string path) + public ByteCollection(ulong offset, byte[] value, string path) + { + if (value == null) + { + throw PSTraceSource.NewArgumentNullException(nameof(value)); + } + + Offset64 = offset; + Bytes = value; + Path = path; + Label = path; + } + + /// + /// Initializes a new instance of the class. + /// + /// The Offset address to be used while displaying the bytes in the collection. + /// Underlying bytes stored in the collection. + [Obsolete("The constructor is deprecated.", true)] + public ByteCollection(uint offset, byte[] value) + : this((ulong)offset, value) { - this.Offset = offset; - _initialOffSet = offset; - this.Bytes = value; - this.Path = path; } /// - /// ByteCollection constructor. + /// Initializes a new instance of the class. + /// + /// The Offset address to be used while displaying the bytes in the collection. + /// Underlying bytes stored in the collection. + public ByteCollection(ulong offset, byte[] value) + { + if (value == null) + { + throw PSTraceSource.NewArgumentNullException(nameof(value)); + } + + Offset64 = offset; + Bytes = value; + } + + /// + /// Initializes a new instance of the class. /// /// The Offset address to be used while displaying the bytes in the collection. + /// + /// The label for the byte group. This may be a file path or a formatted identifying string for the group. + /// /// Underlying bytes stored in the collection. - public ByteCollection(UInt32 offset, Byte[] value) + public ByteCollection(ulong offset, string label, byte[] value) + : this(offset, value) { - this.Offset = offset; - _initialOffSet = offset; - this.Bytes = value; + Label = label; } /// - /// ByteCollection constructor. + /// Initializes a new instance of the class. /// /// Underlying bytes stored in the collection. - public ByteCollection(Byte[] value) + public ByteCollection(byte[] value) { - this.Bytes = value; + if (value == null) + { + throw PSTraceSource.NewArgumentNullException(nameof(value)); + } + + Bytes = value; + } + + /// + /// Gets the Offset address to be used while displaying the bytes in the collection. + /// + [Obsolete("The property is deprecated, please use Offset64 instead.", true)] + public uint Offset + { + get + { + return (uint)Offset64; + } + + private set + { + Offset64 = value; + } } /// - /// The Offset address to be used while displaying the bytes in the collection. + /// Gets the Offset address to be used while displaying the bytes in the collection. /// - public UInt32 Offset { get; private set; } - private UInt32 _initialOffSet = 0; + public ulong Offset64 { get; private set; } /// - /// Underlying bytes stored in the collection. + /// Gets underlying bytes stored in the collection. /// [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] - public Byte[] Bytes { get; private set; } + public byte[] Bytes { get; } /// - /// Indicates the path of the file whose contents are wrapped in the ByteCollection. + /// Gets the path of the file whose contents are wrapped in the ByteCollection. /// - public string Path { get; private set; } + public string Path { get; } + + /// + /// Gets the hexadecimal representation of the value. + /// + public string HexOffset => string.Create(CultureInfo.CurrentCulture, $"{Offset64:X16}"); + + /// + /// Gets the type of the input objects used to create the . + /// + public string Label { get; } + + private const int BytesPerLine = 16; + + private string _hexBytes = string.Empty; + + /// + /// Gets a space-delimited string of the in this + /// in hexadecimal format. + /// + public string HexBytes + { + get + { + if (_hexBytes == string.Empty) + { + StringBuilder line = new(BytesPerLine * 3); + + foreach (var currentByte in Bytes) + { + line.AppendFormat(CultureInfo.CurrentCulture, "{0:X2} ", currentByte); + } + + _hexBytes = line.ToString().Trim(); + } + + return _hexBytes; + } + } + + private string _ascii = string.Empty; + + /// + /// Gets the ASCII string representation of the in this . + /// + /// + public string Ascii + { + get + { + if (_ascii == string.Empty) + { + StringBuilder ascii = new(BytesPerLine); + + foreach (var currentByte in Bytes) + { + var currentChar = (char)currentByte; + if (currentChar == 0x0) + { + ascii.Append(' '); + } + else if (char.IsControl(currentChar)) + { + ascii.Append((char)0xFFFD); + } + else + { + ascii.Append(currentChar); + } + } + + _ascii = ascii.ToString(); + } + + return _ascii; + } + } /// /// Displays the hexadecimal format of the bytes stored in the collection. @@ -153,71 +294,83 @@ public ByteCollection(Byte[] value) /// public override string ToString() { - StringBuilder result = new StringBuilder(); - StringBuilder nextLine = new StringBuilder(); - StringBuilder asciiEnd = new StringBuilder(); + const int BytesPerLine = 16; + const string LineFormat = "{0:X16} "; + + // '16 + 3' comes from format "{0:X16} ". + // '16' comes from '[Uint64]::MaxValue.ToString("X").Length'. + StringBuilder nextLine = new(16 + 3 + (BytesPerLine * 3)); + StringBuilder asciiEnd = new(BytesPerLine); + + // '+1' comes from 'result.Append(nextLine.ToString() + " " + asciiEnd.ToString());' below. + StringBuilder result = new(nextLine.Capacity + asciiEnd.Capacity + 1); if (Bytes.Length > 0) { - UInt32 charCounter = 0; + long charCounter = 0; + + var currentOffset = Offset64; - // ToString() in invoked thrice by the F&O for the same content. - // Hence making sure that Offset is not getting incremented thrice for the same bytes being displayed. - Offset = _initialOffSet; + nextLine.AppendFormat(CultureInfo.InvariantCulture, LineFormat, currentOffset); - nextLine.AppendFormat("{0:X2} ", CultureInfo.InvariantCulture.TextInfo.ToUpper(Convert.ToString(Offset, 16)).PadLeft(8, '0')); - foreach (Byte currentByte in Bytes) + foreach (byte currentByte in Bytes) { // Display each byte, in 2-digit hexadecimal, and add that to the left-hand side. nextLine.AppendFormat("{0:X2} ", currentByte); // If the character is printable, add its ascii representation to // the right-hand side. Otherwise, add a dot to the right hand side. - if ((currentByte >= 0x20) && (currentByte <= 0xFE)) + var currentChar = (char)currentByte; + if (currentChar == 0x0) { - asciiEnd.Append((char)currentByte); + asciiEnd.Append(' '); + } + else if (char.IsControl(currentChar)) + { + asciiEnd.Append((char)0xFFFD); } else { - asciiEnd.Append('.'); + asciiEnd.Append(currentChar); } + charCounter++; // If we've hit the end of a line, combine the right half with the // left half, and start a new line. - if ((charCounter % 16) == 0) + if ((charCounter % BytesPerLine) == 0) { - result.Append(nextLine.ToString() + " " + asciiEnd.ToString()); + result.Append(nextLine).Append(' ').Append(asciiEnd); nextLine.Clear(); asciiEnd.Clear(); - Offset += 0x10; - nextLine.AppendFormat("{0:X2} ", CultureInfo.InvariantCulture.TextInfo.ToUpper(Convert.ToString(Offset, 16)).PadLeft(8, '0')); + currentOffset += BytesPerLine; + nextLine.AppendFormat(CultureInfo.InvariantCulture, LineFormat, currentOffset); // Adding a newline to support long inputs strings flowing through InputObject parameterset. - if ((charCounter <= Bytes.Length) && string.IsNullOrEmpty(this.Path)) + if ((charCounter <= Bytes.Length) && string.IsNullOrEmpty(Path)) { - result.Append("\r\n"); + result.AppendLine(); } } } // At the end of the file, we might not have had the chance to output - // the end of the line yet. Only do this if we didn't exit on the 16-byte + // the end of the line yet. Only do this if we didn't exit on the 16-byte // boundary, though. if ((charCounter % 16) != 0) { while ((charCounter % 16) != 0) { - nextLine.Append(" "); + nextLine.Append(' ', 3); asciiEnd.Append(' '); charCounter++; } - result.Append(nextLine.ToString() + " " + asciiEnd.ToString()); + + result.Append(nextLine).Append(' ').Append(asciiEnd); } } return result.ToString(); } } -} // namespace Microsoft.PowerShell.Commands - +} diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Var.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Var.cs index ae911ae063c..a41b284f568 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Var.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Var.cs @@ -1,9 +1,7 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. using System; -using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Management.Automation; @@ -13,11 +11,8 @@ namespace Microsoft.PowerShell.Commands { /// /// Base class for all variable commands. - /// - /// Because -Scope is defined in VariableCommandBase, all derived commands - /// must implement -Scope. + /// Because -Scope is defined in VariableCommandBase, all derived commands must implement -Scope. /// - public abstract class VariableCommandBase : PSCmdlet { #region Parameters @@ -27,14 +22,14 @@ public abstract class VariableCommandBase : PSCmdlet /// [Parameter] [ValidateNotNullOrEmpty] + [ArgumentCompleter(typeof(ScopeArgumentCompleter))] public string Scope { get; set; } #endregion parameters /// - /// The Include parameter for all the variable commands + /// The Include parameter for all the variable commands. /// - /// protected string[] IncludeFilters { get @@ -44,19 +39,17 @@ protected string[] IncludeFilters set { - if (value == null) - { - value = new string[0]; - } + value ??= Array.Empty(); + _include = value; } } - private string[] _include = new string[0]; + + private string[] _include = Array.Empty(); /// - /// The Exclude parameter for all the variable commands + /// The Exclude parameter for all the variable commands. /// - /// protected string[] ExcludeFilters { get @@ -66,15 +59,13 @@ protected string[] ExcludeFilters set { - if (value == null) - { - value = new string[0]; - } + value ??= Array.Empty(); + _exclude = value; } } - private string[] _exclude = new string[0]; + private string[] _exclude = Array.Empty(); #region helpers @@ -82,37 +73,30 @@ protected string[] ExcludeFilters /// Gets the matching variable for the specified name, using the /// Include, Exclude, and Scope parameters defined in the base class. /// - /// /// /// The name or pattern of the variables to retrieve. /// - /// /// - /// The scope to do the lookup in. If null or empty the normal scoping - /// rules apply. + /// The scope to do the lookup in. If null or empty the normal scoping rules apply. /// - /// /// /// True is returned if a variable exists of the given name but was filtered /// out via globbing, include, or exclude. /// - /// /// /// If true, don't report errors when trying to access private variables. /// - /// /// /// A collection of the variables matching the name, include, and exclude /// pattern in the specified scope. /// - /// internal List GetMatchingVariables(string name, string lookupScope, out bool wasFiltered, bool quiet) { wasFiltered = false; - List result = new List(); + List result = new(); - if (String.IsNullOrEmpty(name)) + if (string.IsNullOrEmpty(name)) { name = "*"; } @@ -166,7 +150,7 @@ internal List GetMatchingVariables(string name, string lookupScope, // view. IDictionary variableTable = null; - if (String.IsNullOrEmpty(lookupScope)) + if (string.IsNullOrEmpty(lookupScope)) { variableTable = SessionState.Internal.GetVariableTable(); } @@ -223,6 +207,7 @@ internal List GetMatchingVariables(string name, string lookupScope, } } } + result.Add(entry.Value); } else @@ -238,24 +223,24 @@ internal List GetMatchingVariables(string name, string lookupScope, } } } + return result; } #endregion helpers } - /// /// Implements get-variable command. /// - [Cmdlet(VerbsCommon.Get, "Variable", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113336")] + [Cmdlet(VerbsCommon.Get, "Variable", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2096711")] [OutputType(typeof(PSVariable))] public class GetVariableCommand : VariableCommandBase { #region parameters /// - /// Name of the PSVariable + /// Name of the PSVariable. /// [Parameter(Position = 0, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true)] [ValidateNotNullOrEmpty()] @@ -268,16 +253,13 @@ public string[] Name set { - if (value == null) - { - value = new string[] { "*" }; - } + value ??= new string[] { "*" }; + _name = value; } } - private string[] _name = new string[] { "*" }; - + private string[] _name = new string[] { "*" }; /// /// Output only the value(s) of the requested variable(s). @@ -289,18 +271,18 @@ public SwitchParameter ValueOnly { return _valueOnly; } + set { _valueOnly = value; } } - private bool _valueOnly; + private bool _valueOnly; /// - /// The Include parameter for all the variable commands + /// The Include parameter for all the variable commands. /// - /// [Parameter] public string[] Include { @@ -316,9 +298,8 @@ public string[] Include } /// - /// The Exclude parameter for all the variable commands + /// The Exclude parameter for all the variable commands. /// - /// [Parameter] public string[] Exclude { @@ -347,10 +328,7 @@ protected override void ProcessRecord() GetMatchingVariables(varName, Scope, out wasFiltered, /*quiet*/ false); matchingVariables.Sort( - delegate (PSVariable left, PSVariable right) - { - return StringComparer.CurrentCultureIgnoreCase.Compare(left.Name, right.Name); - }); + static (PSVariable left, PSVariable right) => StringComparer.CurrentCultureIgnoreCase.Compare(left.Name, right.Name)); bool matchFound = false; foreach (PSVariable matchingVariable in matchingVariables) @@ -369,7 +347,7 @@ protected override void ProcessRecord() if (!matchFound && !wasFiltered) { ItemNotFoundException itemNotFound = - new ItemNotFoundException( + new( varName, "VariableNotFound", SessionStateStrings.VariableNotFound); @@ -384,37 +362,37 @@ protected override void ProcessRecord() } /// - /// Class implementing new-variable command + /// Class implementing new-variable command. /// - [Cmdlet(VerbsCommon.New, "Variable", SupportsShouldProcess = true, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113361")] + [Cmdlet(VerbsCommon.New, "Variable", SupportsShouldProcess = true, ConfirmImpact = ConfirmImpact.Low, + HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2097121")] + [OutputType(typeof(PSVariable))] public sealed class NewVariableCommand : VariableCommandBase { #region parameters /// - /// Name of the PSVariable + /// Name of the PSVariable. /// [Parameter(Position = 0, ValueFromPipelineByPropertyName = true, Mandatory = true)] public string Name { get; set; } /// - /// Value of the PSVariable + /// Value of the PSVariable. /// [Parameter(Position = 1, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true)] public object Value { get; set; } /// - /// Description of the variable + /// Description of the variable. /// [Parameter] public string Description { get; set; } - /// /// The options for the variable to specify if the variable should /// be ReadOnly, Constant, and/or Private. /// - /// [Parameter] public ScopedItemOptions Option { get; set; } = ScopedItemOptions.None; @@ -434,6 +412,7 @@ public SessionStateEntryVisibility Visibility _visibility = value; } } + private SessionStateEntryVisibility? _visibility; /// @@ -452,6 +431,7 @@ public SwitchParameter Force _force = value; } } + private bool _force; /// @@ -464,11 +444,13 @@ public SwitchParameter PassThru { return _passThru; } + set { _passThru = value; } } + private bool _passThru; #endregion parameters @@ -478,7 +460,6 @@ public SwitchParameter PassThru /// take the place of the Value parameter if none was specified on the /// command line. /// - /// protected override void ProcessRecord() { // If Force is not specified, see if the variable already exists @@ -488,7 +469,7 @@ protected override void ProcessRecord() if (!Force) { PSVariable varFound = null; - if (String.IsNullOrEmpty(Scope)) + if (string.IsNullOrEmpty(Scope)) { varFound = SessionState.PSVariable.GetAtScope(Name, "local"); @@ -502,7 +483,7 @@ protected override void ProcessRecord() if (varFound != null) { SessionStateException sessionStateException = - new SessionStateException( + new( Name, SessionStateCategory.Variable, "VariableAlreadyExists", @@ -526,7 +507,7 @@ protected override void ProcessRecord() if (ShouldProcess(target, action)) { - PSVariable newVariable = new PSVariable(Name, Value, Option); + PSVariable newVariable = new(Name, Value, Option); if (_visibility != null) { @@ -540,7 +521,7 @@ protected override void ProcessRecord() try { - if (String.IsNullOrEmpty(Scope)) + if (string.IsNullOrEmpty(Scope)) { SessionState.Internal.NewVariable(newVariable, Force); } @@ -571,35 +552,33 @@ protected override void ProcessRecord() WriteObject(newVariable); } } - } // ProcessRecord - } // NewVariableCommand - + } + } /// - /// This class implements set-variable command + /// This class implements set-variable command. /// - [Cmdlet(VerbsCommon.Set, "Variable", SupportsShouldProcess = true, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113401")] + [Cmdlet(VerbsCommon.Set, "Variable", SupportsShouldProcess = true, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2096624")] [OutputType(typeof(PSVariable))] public sealed class SetVariableCommand : VariableCommandBase { #region parameters /// - /// Name of the PSVariable(s) to set + /// Name of the PSVariable(s) to set. /// [Parameter(Position = 0, ValueFromPipelineByPropertyName = true, Mandatory = true)] public string[] Name { get; set; } /// - /// Value of the PSVariable + /// Value of the PSVariable. /// [Parameter(Position = 1, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true)] public object Value { get; set; } = AutomationNull.Value; /// - /// The Include parameter for all the variable commands + /// The Include parameter for all the variable commands. /// - /// [Parameter] public string[] Include { @@ -615,9 +594,8 @@ public string[] Include } /// - /// The Exclude parameter for all the variable commands + /// The Exclude parameter for all the variable commands. /// - /// [Parameter] public string[] Exclude { @@ -633,17 +611,15 @@ public string[] Exclude } /// - /// Description of the variable + /// Description of the variable. /// [Parameter] public string Description { get; set; } - /// /// The options for the variable to specify if the variable should /// be ReadOnly, Constant, and/or Private. /// - /// [Parameter] public ScopedItemOptions Option { @@ -651,12 +627,14 @@ public ScopedItemOptions Option { return (ScopedItemOptions)_options; } + set { _options = value; } } - private Nullable _options; + + private ScopedItemOptions? _options; /// /// Force the operation to make the best attempt at setting the variable. @@ -674,6 +652,7 @@ public SwitchParameter Force _force = value; } } + private bool _force; /// @@ -692,8 +671,8 @@ public SessionStateEntryVisibility Visibility _visibility = value; } } - private SessionStateEntryVisibility? _visibility; + private SessionStateEntryVisibility? _visibility; /// /// The variable object should be passed down the pipeline. @@ -705,20 +684,27 @@ public SwitchParameter PassThru { return _passThru; } + set { _passThru = value; } } + private bool _passThru; + /// + /// Gets whether we will append to the variable if it exists. + /// + [Parameter] + public SwitchParameter Append { get; set; } + private bool _nameIsFormalParameter; private bool _valueIsFormalParameter; #endregion parameters /// - /// Checks to see if the name and value parameters were - /// bound as formal parameters. + /// Checks to see if the name and value parameters were bound as formal parameters. /// protected override void BeginProcessing() { @@ -731,6 +717,33 @@ protected override void BeginProcessing() { _valueIsFormalParameter = true; } + + if (Append) + { + // create the list here and add to it if it has a value + // but if they have more than one name, produce an error + if (Name.Length != 1) + { + ErrorRecord appendVariableError = new ErrorRecord(new InvalidOperationException(), "SetVariableAppend", ErrorCategory.InvalidOperation, Name); + appendVariableError.ErrorDetails = new ErrorDetails("SetVariableAppend"); + appendVariableError.ErrorDetails.RecommendedAction = VariableCommandStrings.UseSingleVariable; + ThrowTerminatingError(appendVariableError); + } + + _valueList = new List(); + var currentValue = Context.SessionState.PSVariable.Get(Name[0]); + if (currentValue is not null) + { + if (currentValue.Value is IList ilist) + { + _valueList.AddRange(ilist); + } + else + { + _valueList.Add(currentValue.Value); + } + } + } } /// @@ -742,11 +755,20 @@ protected override void BeginProcessing() /// If name is not a formal parameter, then set /// the variable each time ProcessRecord is called. /// - /// protected override void ProcessRecord() { if (_nameIsFormalParameter && _valueIsFormalParameter) { + if (Append) + { + if (Value != AutomationNull.Value) + { + _valueList ??= new List(); + + _valueList.Add(Value); + } + } + return; } @@ -754,10 +776,8 @@ protected override void ProcessRecord() { if (Value != AutomationNull.Value) { - if (_valueList == null) - { - _valueList = new ArrayList(); - } + _valueList ??= new List(); + _valueList.Add(Value); } } @@ -766,7 +786,8 @@ protected override void ProcessRecord() SetVariable(Name, Value); } } - private ArrayList _valueList; + + private List _valueList; /// /// Sets the variable if the name was specified as a formal parameter @@ -778,7 +799,14 @@ protected override void EndProcessing() { if (_valueIsFormalParameter) { - SetVariable(Name, Value); + if (Append) + { + SetVariable(Name, _valueList); + } + else + { + SetVariable(Name, Value); + } } else { @@ -808,15 +836,12 @@ protected override void EndProcessing() /// /// Sets the variables of the given names to the specified value. /// - /// /// /// The name(s) of the variables to set. /// - /// /// /// The value to set the variable to. /// - /// private void SetVariable(string[] varNames, object varValue) { CommandOrigin origin = MyInvocation.CommandOrigin; @@ -825,11 +850,11 @@ private void SetVariable(string[] varNames, object varValue) { // First look for existing variables to set. - List matchingVariables = new List(); + List matchingVariables = new(); bool wasFiltered = false; - if (!String.IsNullOrEmpty(Scope)) + if (!string.IsNullOrEmpty(Scope)) { // We really only need to find matches if the scope was specified. // If the scope wasn't specified then we need to create the @@ -862,8 +887,8 @@ private void SetVariable(string[] varNames, object varValue) { ScopedItemOptions newOptions = ScopedItemOptions.None; - if (!String.IsNullOrEmpty(Scope) && - String.Equals("private", Scope, StringComparison.OrdinalIgnoreCase)) + if (!string.IsNullOrEmpty(Scope) && + string.Equals("private", Scope, StringComparison.OrdinalIgnoreCase)) { newOptions = ScopedItemOptions.Private; } @@ -880,15 +905,13 @@ private void SetVariable(string[] varNames, object varValue) } PSVariable varToSet = - new PSVariable( + new( varName, newVarValue, newOptions); - if (Description == null) - { - Description = String.Empty; - } + Description ??= string.Empty; + varToSet.Description = Description; // If visibility was specified, set it on the variable @@ -905,7 +928,7 @@ private void SetVariable(string[] varNames, object varValue) { object result = null; - if (String.IsNullOrEmpty(Scope)) + if (string.IsNullOrEmpty(Scope)) { result = SessionState.Internal.SetVariable(varToSet, Force, origin); @@ -974,8 +997,17 @@ private void SetVariable(string[] varNames, object varValue) if (varValue != AutomationNull.Value) { matchingVariable.Value = varValue; - } + if (Context.LanguageMode == PSLanguageMode.ConstrainedLanguage) + { + // In 'ConstrainedLanguage' we want to monitor untrusted values assigned to 'Global:' variables + // and 'Script:' variables, because they may be set from 'ConstrainedLanguage' environment and + // referenced within trusted script block, and thus result in security issues. + // Here we are setting the value of an existing variable and don't know what scope this variable + // is from, so we mark the value as untrusted, regardless of the scope. + ExecutionContext.MarkObjectAsUntrusted(matchingVariable.Value); + } + } if (Description != null) { @@ -1027,27 +1059,26 @@ private void SetVariable(string[] varNames, object varValue) } } } - } // ProcessRecord - } // SetVariableCommand + } + } /// - /// The Remove-Variable cmdlet implementation + /// The Remove-Variable cmdlet implementation. /// - [Cmdlet(VerbsCommon.Remove, "Variable", SupportsShouldProcess = true, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113380")] + [Cmdlet(VerbsCommon.Remove, "Variable", SupportsShouldProcess = true, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2097123")] public sealed class RemoveVariableCommand : VariableCommandBase { #region parameters /// - /// Name of the PSVariable(s) to set + /// Name of the PSVariable(s) to set. /// [Parameter(Position = 0, ValueFromPipelineByPropertyName = true, Mandatory = true)] public string[] Name { get; set; } /// - /// The Include parameter for all the variable commands + /// The Include parameter for all the variable commands. /// - /// [Parameter] public string[] Include { @@ -1063,9 +1094,8 @@ public string[] Include } /// - /// The Exclude parameter for all the variable commands + /// The Exclude parameter for all the variable commands. /// - /// [Parameter] public string[] Exclude { @@ -1081,9 +1111,8 @@ public string[] Exclude } /// - /// If true, the variable is removed even if it is ReadOnly + /// If true, the variable is removed even if it is ReadOnly. /// - /// [Parameter] public SwitchParameter Force { @@ -1091,29 +1120,26 @@ public SwitchParameter Force { return _force; } + set { _force = value; } } + private bool _force; #endregion parameters /// - /// Removes the matching variables from the specified scope + /// Removes the matching variables from the specified scope. /// - /// protected override void ProcessRecord() { // Removal of variables only happens in the local scope if the // scope wasn't explicitly specified by the user. - if (Scope == null) - { - Scope = "local"; - } - + Scope ??= "local"; foreach (string varName in Name) { @@ -1129,7 +1155,7 @@ protected override void ProcessRecord() // characters were specified, write an error. ItemNotFoundException itemNotFound = - new ItemNotFoundException( + new( varName, "VariableNotFound", SessionStateStrings.VariableNotFound); @@ -1155,7 +1181,7 @@ protected override void ProcessRecord() { try { - if (String.IsNullOrEmpty(Scope)) + if (string.IsNullOrEmpty(Scope)) { SessionState.Internal.RemoveVariable(matchingVariable, _force); } @@ -1181,28 +1207,27 @@ protected override void ProcessRecord() } } } - } // ProcessRecord - } // RemoveVariableCommand + } + } /// - /// This class implements set-variable command + /// This class implements set-variable command. /// - [Cmdlet(VerbsCommon.Clear, "Variable", SupportsShouldProcess = true, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113285")] + [Cmdlet(VerbsCommon.Clear, "Variable", SupportsShouldProcess = true, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2096923")] [OutputType(typeof(PSVariable))] public sealed class ClearVariableCommand : VariableCommandBase { #region parameters /// - /// Name of the PSVariable(s) to set + /// Name of the PSVariable(s) to set. /// [Parameter(Position = 0, ValueFromPipelineByPropertyName = true, Mandatory = true)] public string[] Name { get; set; } /// - /// The Include parameter for all the variable commands + /// The Include parameter for all the variable commands. /// - /// [Parameter] public string[] Include { @@ -1218,9 +1243,8 @@ public string[] Include } /// - /// The Exclude parameter for all the variable commands + /// The Exclude parameter for all the variable commands. /// - /// [Parameter] public string[] Exclude { @@ -1251,6 +1275,7 @@ public SwitchParameter Force _force = value; } } + private bool _force; /// @@ -1263,19 +1288,20 @@ public SwitchParameter PassThru { return _passThru; } + set { _passThru = value; } } + private bool _passThru; #endregion parameters /// - /// The implementation of the Clear-Variable command + /// The implementation of the Clear-Variable command. /// - /// protected override void ProcessRecord() { foreach (string varName in Name) @@ -1291,7 +1317,7 @@ protected override void ProcessRecord() // characters were specified, write an error. ItemNotFoundException itemNotFound = - new ItemNotFoundException( + new( varName, "VariableNotFound", SessionStateStrings.VariableNotFound); @@ -1359,17 +1385,15 @@ protected override void ProcessRecord() } } } - } // ProcessRecord + } /// /// Clears the value of the variable using the PSVariable instance if the scope /// was specified or using standard variable lookup if the scope was not specified. /// - /// /// /// The variable that matched the name parameter(s). /// - /// private PSVariable ClearValue(PSVariable matchingVariable) { PSVariable result = matchingVariable; @@ -1382,8 +1406,8 @@ private PSVariable ClearValue(PSVariable matchingVariable) SessionState.PSVariable.Set(matchingVariable.Name, null); result = SessionState.PSVariable.Get(matchingVariable.Name); } + return result; } - } // ClearVariableCommand + } } - diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WaitEventCommand.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WaitEventCommand.cs index 6538a0bd716..3c4336f07d0 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WaitEventCommand.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WaitEventCommand.cs @@ -1,6 +1,5 @@ -// -// Copyright (C) Microsoft. All rights reserved. -// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. using System; using System.Management.Automation; @@ -11,14 +10,14 @@ namespace Microsoft.PowerShell.Commands /// /// Waits for a given event to arrive. /// - [Cmdlet(VerbsLifecycle.Wait, "Event", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=135276")] + [Cmdlet(VerbsLifecycle.Wait, "Event", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2097042")] [OutputType(typeof(PSEventArgs))] public class WaitEventCommand : PSCmdlet { #region parameters /// - /// An identifier for this event subscription + /// An identifier for this event subscription. /// [Parameter(Position = 0, ValueFromPipelineByPropertyName = true)] public string SourceIdentifier @@ -27,12 +26,14 @@ public string SourceIdentifier { return _sourceIdentifier; } + set { _sourceIdentifier = value; _matchPattern = WildcardPattern.Get(value, WildcardOptions.IgnoreCase); } } + private string _sourceIdentifier = null; /// @@ -41,29 +42,31 @@ public string SourceIdentifier /// [Parameter] [Alias("TimeoutSec")] - [ValidateRangeAttribute(-1, Int32.MaxValue)] + [ValidateRange(-1, int.MaxValue)] public int Timeout { get { return _timeoutInSeconds; } + set { _timeoutInSeconds = value; } } + private int _timeoutInSeconds = -1; // -1: infinite, this default is to wait for as long as it takes. #endregion parameters - private AutoResetEvent _eventArrived = new AutoResetEvent(false); + private readonly AutoResetEvent _eventArrived = new(false); private PSEventArgs _receivedEvent = null; - private Object _receivedEventLock = new Object(); + private readonly object _receivedEventLock = new(); private WildcardPattern _matchPattern; /// - /// Wait for the event to arrive + /// Wait for the event to arrive. /// protected override void ProcessRecord() { @@ -102,14 +105,14 @@ protected override void ProcessRecord() } /// - /// Handle Control-C + /// Handle Control-C. /// protected override void StopProcessing() { _eventArrived.Set(); } - private void ReceivedEvents_PSEventReceived(Object sender, PSEventArgs e) + private void ReceivedEvents_PSEventReceived(object sender, PSEventArgs e) { // If they want to wait on just any event if (_sourceIdentifier == null) @@ -123,7 +126,6 @@ private void ReceivedEvents_PSEventReceived(Object sender, PSEventArgs e) } } - // Go through all the received events. If one matches the subscription identifier, // break. private void ScanEventQueue() @@ -158,4 +160,4 @@ private void NotifyEvent(PSEventArgs e) } } } -} \ No newline at end of file +} diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/Common/BasicHtmlWebResponseObject.Common.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/Common/BasicHtmlWebResponseObject.Common.cs index ae91ff0012c..9bd76f99413 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/Common/BasicHtmlWebResponseObject.Common.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/Common/BasicHtmlWebResponseObject.Common.cs @@ -1,42 +1,77 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#nullable enable using System; -using System.Management.Automation; -using System.Net; +using System.Collections.Generic; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; using System.IO; +using System.Management.Automation; +using System.Net.Http; using System.Text; using System.Text.RegularExpressions; -using System.Collections.Generic; -using System.Diagnostics; +using System.Threading; namespace Microsoft.PowerShell.Commands { /// - /// Response object for html content without DOM parsing + /// Response object for html content without DOM parsing. /// - public partial class BasicHtmlWebResponseObject : WebResponseObject + public class BasicHtmlWebResponseObject : WebResponseObject { + #region Constructors + + /// + /// Initializes a new instance of the class. + /// + /// The response. + /// Time permitted between reads or Timeout.InfiniteTimeSpan for no timeout. + /// Cancellation token. + public BasicHtmlWebResponseObject(HttpResponseMessage response, TimeSpan perReadTimeout, CancellationToken cancellationToken) : this(response, null, perReadTimeout, cancellationToken) { } + + /// + /// Initializes a new instance of the class + /// with the specified . + /// + /// The response. + /// The content stream associated with the response. + /// Time permitted between reads or Timeout.InfiniteTimeSpan for no timeout. + /// Cancellation token. + public BasicHtmlWebResponseObject(HttpResponseMessage response, Stream? contentStream, TimeSpan perReadTimeout, CancellationToken cancellationToken) : base(response, contentStream, perReadTimeout, cancellationToken) + { + InitializeContent(cancellationToken); + InitializeRawContent(response); + } + + #endregion Constructors + #region Properties /// - /// gets or protected sets the Content property + /// Gets the text body content of this response. /// + /// + /// Content of the response body, decoded using , + /// if the Content-Type response header is a recognized text + /// type. Otherwise . + /// public new string Content { get; private set; } /// - /// Gets the Encoding that was used to decode the Content + /// Gets the encoding of the text body content of this response. /// /// - /// The Encoding used to decode the Content; otherwise, a null reference if the content is not text. + /// Encoding of the response body from the Content-Type header, + /// or if the encoding could not be determined. /// - public Encoding Encoding { get; private set; } + public Encoding? Encoding { get; private set; } - private WebCmdletElementCollection _inputFields; + private WebCmdletElementCollection? _inputFields; /// - /// gets the Fields property + /// Gets the HTML input field elements parsed from . /// public WebCmdletElementCollection InputFields { @@ -44,10 +79,8 @@ public WebCmdletElementCollection InputFields { if (_inputFields == null) { - EnsureHtmlParser(); - - List parsedFields = new List(); - MatchCollection fieldMatch = s_inputFieldRegex.Matches(Content); + List parsedFields = new(); + MatchCollection fieldMatch = HtmlParser.InputFieldRegex.Matches(Content); foreach (Match field in fieldMatch) { parsedFields.Add(CreateHtmlObject(field.Value, "INPUT")); @@ -60,10 +93,10 @@ public WebCmdletElementCollection InputFields } } - private WebCmdletElementCollection _links; + private WebCmdletElementCollection? _links; /// - /// gets the Links property + /// Gets the HTML a link elements parsed from . /// public WebCmdletElementCollection Links { @@ -71,10 +104,8 @@ public WebCmdletElementCollection Links { if (_links == null) { - EnsureHtmlParser(); - - List parsedLinks = new List(); - MatchCollection linkMatch = s_linkRegex.Matches(Content); + List parsedLinks = new(); + MatchCollection linkMatch = HtmlParser.LinkRegex.Matches(Content); foreach (Match link in linkMatch) { parsedLinks.Add(CreateHtmlObject(link.Value, "A")); @@ -87,10 +118,10 @@ public WebCmdletElementCollection Links } } - private WebCmdletElementCollection _images; + private WebCmdletElementCollection? _images; /// - /// gets the Images property + /// Gets the HTML img elements parsed from . /// public WebCmdletElementCollection Images { @@ -98,10 +129,8 @@ public WebCmdletElementCollection Images { if (_images == null) { - EnsureHtmlParser(); - - List parsedImages = new List(); - MatchCollection imageMatch = s_imageRegex.Matches(Content); + List parsedImages = new(); + MatchCollection imageMatch = HtmlParser.ImageRegex.Matches(Content); foreach (Match image in imageMatch) { parsedImages.Add(CreateHtmlObject(image.Value, "IMG")); @@ -116,61 +145,33 @@ public WebCmdletElementCollection Images #endregion Properties - #region Private Fields - - private static Regex s_tagRegex; - private static Regex s_attribsRegex; - private static Regex s_attribNameValueRegex; - private static Regex s_inputFieldRegex; - private static Regex s_linkRegex; - private static Regex s_imageRegex; - - #endregion Private Fields - #region Methods - private void EnsureHtmlParser() + /// + /// Reads the response content from the web response. + /// + /// The cancellation token. + [MemberNotNull(nameof(Content))] + protected void InitializeContent(CancellationToken cancellationToken) { - if (s_tagRegex == null) - { - s_tagRegex = new Regex(@"<\w+((\s+[^""'>/=\s\p{Cc}]+(\s*=\s*(?:"".*?""|'.*?'|[^'"">\s]+))?)+\s*|\s*)/?>", - RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.Compiled); - } - - if (s_attribsRegex == null) - { - s_attribsRegex = new Regex(@"(?<=\s+)([^""'>/=\s\p{Cc}]+(\s*=\s*(?:"".*?""|'.*?'|[^'"">\s]+))?)", - RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.Compiled); - } - - if (s_attribNameValueRegex == null) - { - s_attribNameValueRegex = new Regex(@"([^""'>/=\s\p{Cc}]+)(?:\s*=\s*(?:""(.*?)""|'(.*?)'|([^'"">\s]+)))?", - RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.Compiled); - } - - if (s_inputFieldRegex == null) + string? contentType = ContentHelper.GetContentType(BaseResponse); + if (ContentHelper.IsText(contentType)) { - s_inputFieldRegex = new Regex(@"]*(/>|>.*?)", - RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.Compiled); - } + // Fill the Content buffer + string? characterSet = WebResponseHelper.GetCharacterSet(BaseResponse); - if (s_linkRegex == null) - { - s_linkRegex = new Regex(@"]*(/>|>.*?)", - RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.Compiled); + Content = StreamHelper.DecodeStream(RawContentStream, characterSet, out Encoding encoding, perReadTimeout, cancellationToken); + Encoding = encoding; } - - if (s_imageRegex == null) + else { - s_imageRegex = new Regex(@"]*>", - RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.Compiled); + Content = string.Empty; } } - private PSObject CreateHtmlObject(string html, string tagName) + private static PSObject CreateHtmlObject(string html, string tagName) { - PSObject elementObject = new PSObject(); + PSObject elementObject = new(); elementObject.Properties.Add(new PSNoteProperty("outerHTML", html)); elementObject.Properties.Add(new PSNoteProperty("tagName", tagName)); @@ -180,30 +181,37 @@ private PSObject CreateHtmlObject(string html, string tagName) return elementObject; } - private void ParseAttributes(string outerHtml, PSObject elementObject) + private void InitializeRawContent(HttpResponseMessage baseResponse) + { + StringBuilder raw = ContentHelper.GetRawContentHeader(baseResponse); + raw.Append(Content); + RawContent = raw.ToString(); + } + + private static void ParseAttributes(string outerHtml, PSObject elementObject) { // We might get an empty input for a directive from the HTML file if (!string.IsNullOrEmpty(outerHtml)) { // Extract just the opening tag of the HTML element (omitting the closing tag and any contents, // including contained HTML elements) - var match = s_tagRegex.Match(outerHtml); + Match match = HtmlParser.TagRegex.Match(outerHtml); // Extract all the attribute specifications within the HTML element opening tag - var attribMatches = s_attribsRegex.Matches(match.Value); + MatchCollection attribMatches = HtmlParser.AttribsRegex.Matches(match.Value); foreach (Match attribMatch in attribMatches) { // Extract the name and value for this attribute (allowing for variations like single/double/no // quotes, and no value at all) - var nvMatches = s_attribNameValueRegex.Match(attribMatch.Value); + Match nvMatches = HtmlParser.AttribNameValueRegex.Match(attribMatch.Value); Debug.Assert(nvMatches.Groups.Count == 5); // Name is always captured by group #1 string name = nvMatches.Groups[1].Value; // The value (if any) is captured by group #2, #3, or #4, depending on quoting or lack thereof - string value = null; + string? value = null; if (nvMatches.Groups[2].Success) { value = nvMatches.Groups[2].Value; @@ -222,26 +230,22 @@ private void ParseAttributes(string outerHtml, PSObject elementObject) } } - /// - /// Reads the response content from the web response. - /// - protected void InitializeContent() + #endregion Methods + + // This class is needed so the static Regexes are initialized only the first time they are used + private static class HtmlParser { - string contentType = ContentHelper.GetContentType(BaseResponse); - if (ContentHelper.IsText(contentType)) - { - Encoding encoding = null; - // fill the Content buffer - string characterSet = WebResponseHelper.GetCharacterSet(BaseResponse); - this.Content = StreamHelper.DecodeStream(RawContentStream, characterSet, out encoding); - this.Encoding = encoding; - } - else - { - this.Content = string.Empty; - } - } + internal static readonly Regex AttribsRegex = new Regex(@"(?<=\s+)([^""'>/=\s\p{Cc}]+(\s*=\s*(?:"".*?""|'.*?'|[^'"">\s]+))?)", RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.Compiled); - #endregion Methods + internal static readonly Regex AttribNameValueRegex = new Regex(@"([^""'>/=\s\p{Cc}]+)(?:\s*=\s*(?:""(.*?)""|'(.*?)'|([^'"">\s]+)))?", RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.Compiled); + + internal static readonly Regex ImageRegex = new Regex(@"]*?>", RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.Compiled); + + internal static readonly Regex InputFieldRegex = new Regex(@"]*(/?>|>.*?)", RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.Compiled); + + internal static readonly Regex LinkRegex = new Regex(@"]*(/>|>.*?)", RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.Compiled); + + internal static readonly Regex TagRegex = new Regex(@"<\w+((\s+[^""'>/=\s\p{Cc}]+(\s*=\s*(?:"".*?""|'.*?'|[^'"">\s]+))?)+\s*|\s*)/?>", RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.Compiled); + } } } diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/Common/ContentHelper.Common.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/Common/ContentHelper.Common.cs index dc3a673ef46..9eca5ce187a 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/Common/ContentHelper.Common.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/Common/ContentHelper.Common.cs @@ -1,110 +1,123 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#nullable enable using System; +using System.Diagnostics.CodeAnalysis; using System.Management.Automation; +using System.Net.Http; +using System.Net.Http.Headers; using System.Text; +using Humanizer; using Microsoft.Win32; namespace Microsoft.PowerShell.Commands { - internal static partial class ContentHelper + internal static class ContentHelper { - #region Constants - - // used to split contentType arguments - private static readonly char[] s_contentTypeParamSeparator = { ';' }; - - // default codepage encoding for web content. See RFC 2616. - private const string _defaultCodePage = "ISO-8859-1"; - - #endregion Constants - #region Internal Methods - internal static bool IsText(string contentType) - { - contentType = GetContentTypeSignature(contentType); - return CheckIsText(contentType); - } + // ContentType may not exist in response header. Return null if not. + internal static string? GetContentType(HttpResponseMessage response) => response.Content.Headers.ContentType?.MediaType; - internal static bool IsXml(string contentType) - { - contentType = GetContentTypeSignature(contentType); - return CheckIsXml(contentType); - } + internal static string? GetContentType(HttpRequestMessage request) => request.Content?.Headers.ContentType?.MediaType; - internal static bool IsJson(string contentType) - { - contentType = GetContentTypeSignature(contentType); - return CheckIsJson(contentType); - } + internal static Encoding GetDefaultEncoding() => Encoding.UTF8; + + internal static string GetFriendlyContentLength(long? length) => + length.HasValue + ? $"{length.Value.Bytes().Humanize()} ({length.Value:#,0} bytes)" + : "unknown size"; - internal static Encoding GetEncodingOrDefault(string characterSet) + internal static StringBuilder GetRawContentHeader(HttpResponseMessage response) { - // get the name of the codepage to use for response content - string codepage = (string.IsNullOrEmpty(characterSet) ? _defaultCodePage : characterSet); - Encoding encoding = null; + StringBuilder raw = new(); - try + string protocol = WebResponseHelper.GetProtocol(response); + if (!string.IsNullOrEmpty(protocol)) { - encoding = Encoding.GetEncoding(codepage); + int statusCode = WebResponseHelper.GetStatusCode(response); + string statusDescription = WebResponseHelper.GetStatusDescription(response); + raw.AppendLine($"{protocol} {statusCode} {statusDescription}"); } - catch (ArgumentException) + + HttpHeaders[] headerCollections = + { + response.Headers, + response.Content.Headers + }; + + foreach (var headerCollection in headerCollections) { - // 0, default code page - encoding = Encoding.GetEncoding(0); + if (headerCollection == null) + { + continue; + } + + foreach (var header in headerCollection) + { + // Headers may have multiple entries with different values + foreach (string headerValue in header.Value) + { + raw.AppendLine($"{header.Key}: {headerValue}"); + } + } } - return encoding; + raw.AppendLine(); + return raw; } - internal static Encoding GetDefaultEncoding() + internal static bool IsJson([NotNullWhen(true)] string? contentType) { - return GetEncodingOrDefault((string)null); - } - - #endregion Internal Methods + if (string.IsNullOrEmpty(contentType)) + { + return false; + } - #region Private Helper Methods + // The correct type for JSON content, as specified in RFC 4627 + bool isJson = contentType.Equals("application/json", StringComparison.OrdinalIgnoreCase); - private static string GetContentTypeSignature(string contentType) - { - if (String.IsNullOrEmpty(contentType)) - return null; + // Add in these other "javascript" related types that + // sometimes get sent down as the mime type for JSON content + isJson |= contentType.Equals("text/json", StringComparison.OrdinalIgnoreCase) + || contentType.Equals("application/x-javascript", StringComparison.OrdinalIgnoreCase) + || contentType.Equals("text/x-javascript", StringComparison.OrdinalIgnoreCase) + || contentType.Equals("application/javascript", StringComparison.OrdinalIgnoreCase) + || contentType.Equals("text/javascript", StringComparison.OrdinalIgnoreCase); - string sig = contentType.Split(s_contentTypeParamSeparator, 2)[0].ToUpperInvariant(); - return (sig); + return isJson; } - private static bool CheckIsText(string contentType) + internal static bool IsText([NotNullWhen(true)] string? contentType) { - if (String.IsNullOrEmpty(contentType)) + if (string.IsNullOrEmpty(contentType)) + { return false; + } - // any text, xml or json types are text + // Any text, xml or json types are text bool isText = contentType.StartsWith("text/", StringComparison.OrdinalIgnoreCase) - || CheckIsXml(contentType) - || CheckIsJson(contentType); + || IsXml(contentType) + || IsJson(contentType); // Further content type analysis is available on Windows if (Platform.IsWindows && !isText) { // Media types registered with Windows as having a perceived type of text, are text - using (RegistryKey contentTypeKey = Registry.ClassesRoot.OpenSubKey(@"MIME\Database\Content Type\" + contentType)) + using (RegistryKey? contentTypeKey = Registry.ClassesRoot.OpenSubKey(@"MIME\Database\Content Type\" + contentType)) { if (contentTypeKey != null) { - string extension = contentTypeKey.GetValue("Extension") as string; - if (extension != null) + if (contentTypeKey.GetValue("Extension") is string extension) { - using (RegistryKey extensionKey = Registry.ClassesRoot.OpenSubKey(extension)) + using (RegistryKey? extensionKey = Registry.ClassesRoot.OpenSubKey(extension)) { if (extensionKey != null) { - string perceivedType = extensionKey.GetValue("PerceivedType") as string; - isText = (perceivedType == "text"); + string? perceivedType = extensionKey.GetValue("PerceivedType") as string; + isText = perceivedType == "text"; } } } @@ -112,42 +125,28 @@ private static bool CheckIsText(string contentType) } } - return (isText); + return isText; } - private static bool CheckIsXml(string contentType) + internal static bool IsXml([NotNullWhen(true)] string? contentType) { - if (String.IsNullOrEmpty(contentType)) + if (string.IsNullOrEmpty(contentType)) + { return false; + } // RFC 3023: Media types with the suffix "+xml" are XML - bool isXml = (contentType.Equals("application/xml", StringComparison.OrdinalIgnoreCase) - || contentType.Equals("application/xml-external-parsed-entity", StringComparison.OrdinalIgnoreCase) - || contentType.Equals("application/xml-dtd", StringComparison.OrdinalIgnoreCase)); + bool isXml = contentType.Equals("application/xml", StringComparison.OrdinalIgnoreCase) + || contentType.Equals("application/xml-external-parsed-entity", StringComparison.OrdinalIgnoreCase) + || contentType.Equals("application/xml-dtd", StringComparison.OrdinalIgnoreCase) + || contentType.EndsWith("+xml", StringComparison.OrdinalIgnoreCase); - isXml |= contentType.EndsWith("+xml", StringComparison.OrdinalIgnoreCase); - return (isXml); + return isXml; } - private static bool CheckIsJson(string contentType) - { - if (String.IsNullOrEmpty(contentType)) - return false; - - // the correct type for JSON content, as specified in RFC 4627 - bool isJson = contentType.Equals("application/json", StringComparison.OrdinalIgnoreCase); + internal static bool IsTextBasedContentType([NotNullWhen(true)] string? contentType) + => IsText(contentType) || IsJson(contentType) || IsXml(contentType); - // add in these other "javascript" related types that - // sometimes get sent down as the mime type for JSON content - isJson |= contentType.Equals("text/json", StringComparison.OrdinalIgnoreCase) - || contentType.Equals("application/x-javascript", StringComparison.OrdinalIgnoreCase) - || contentType.Equals("text/x-javascript", StringComparison.OrdinalIgnoreCase) - || contentType.Equals("application/javascript", StringComparison.OrdinalIgnoreCase) - || contentType.Equals("text/javascript", StringComparison.OrdinalIgnoreCase); - - return (isJson); - } - - #endregion Internal Helper Methods + #endregion Internal Methods } } diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/Common/HtmlWebResponseObject.Common.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/Common/HtmlWebResponseObject.Common.cs deleted file mode 100644 index be101d482fe..00000000000 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/Common/HtmlWebResponseObject.Common.cs +++ /dev/null @@ -1,511 +0,0 @@ -#if !CORECLR - -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -using System; -using System.Management.Automation; -using System.Text.RegularExpressions; -using System.Collections.Generic; -using mshtml; -using System.Diagnostics; -using System.Threading; -using ExecutionContext = System.Management.Automation.ExecutionContext; - -namespace Microsoft.PowerShell.Commands -{ - /// - /// Response object for html content - /// - public partial class HtmlWebResponseObject : WebResponseObject, IDisposable - { -#region Properties - - /// - /// gets or protected sets the Content property - /// - public new string Content { get; private set; } - - // The HTML document - private IHTMLDocument2 _parsedHtml; - - // The reset event for synchronizing the 'IHTMLDocument2.write()' call - private ManualResetEventSlim _stateChangeResetEvent; - - // The reset event for synchronizing loading the document - private ManualResetEventSlim _loadDocumentResetEvent; - - // The handler for the 'onreadystatechange' event - private HTMLDocumentEvents2_onreadystatechangeEventHandler _onreadystatechangeEventHandler; - - // The exception thrown during the parsing - private Exception _parsingException; - - // The current execution context - private readonly ExecutionContext _executionContext; - - // The flag that notifies the worker thread to stop loading the document - private bool _stopWorkerThread; - - // The flag that indicates the html is parsed - private bool _htmlParsed = false; - - /// - /// gets the ParsedHtml property - /// - public IHTMLDocument2 ParsedHtml - { - get - { - EnsureHtmlParser(); - - return _parsedHtml; - } - } - - private FormObjectCollection _forms; - - /// - /// gets the Forms property - /// - public FormObjectCollection Forms - { - get - { - if (_forms == null) - { - _forms = BuildFormsCollection(); - } - - return _forms; - } - } - - private WebCmdletElementCollection _inputFields; - - /// - /// gets the Fields property - /// - public WebCmdletElementCollection InputFields - { - get - { - if (_inputFields == null) - { - EnsureHtmlParser(); - - List parsedFields = new List(); - foreach (IHTMLElement element in _parsedHtml.all) - { - if (element.tagName.Equals("INPUT", StringComparison.OrdinalIgnoreCase)) - { - parsedFields.Add(CreateHtmlObject(element, true)); - } - System.Runtime.InteropServices.Marshal.ReleaseComObject(element); - } - - _inputFields = new WebCmdletElementCollection(parsedFields); - } - - return _inputFields; - } - } - - private WebCmdletElementCollection _links; - - /// - /// gets the Links property - /// - public WebCmdletElementCollection Links - { - get - { - if (_links == null) - { - EnsureHtmlParser(); - - List parsedLinks = new List(); - foreach (IHTMLElement element in _parsedHtml.links) - { - parsedLinks.Add(CreateHtmlObject(element, true)); - System.Runtime.InteropServices.Marshal.ReleaseComObject(element); - } - - _links = new WebCmdletElementCollection(parsedLinks); - } - - return _links; - } - } - - private WebCmdletElementCollection _images; - - /// - /// gets the Images property - /// - public WebCmdletElementCollection Images - { - get - { - if (_images == null) - { - EnsureHtmlParser(); - - List parsedImages = new List(); - foreach (IHTMLElement element in _parsedHtml.images) - { - parsedImages.Add(CreateHtmlObject(element, true)); - System.Runtime.InteropServices.Marshal.ReleaseComObject(element); - } - - _images = new WebCmdletElementCollection(parsedImages); - } - - return _images; - } - } - - private WebCmdletElementCollection _scripts; - - /// - /// gets the Scripts property - /// - public WebCmdletElementCollection Scripts - { - get - { - if (_scripts == null) - { - EnsureHtmlParser(); - - List parsedScripts = new List(); - foreach (IHTMLElement element in _parsedHtml.scripts) - { - parsedScripts.Add(CreateHtmlObject(element, true)); - System.Runtime.InteropServices.Marshal.ReleaseComObject(element); - } - - _scripts = new WebCmdletElementCollection(parsedScripts); - } - - return _scripts; - } - } - - private WebCmdletElementCollection _allElements; - - /// - /// gets the Elements property - /// - public WebCmdletElementCollection AllElements - { - get - { - if (_allElements == null) - { - EnsureHtmlParser(); - - List parsedElements = new List(); - foreach (IHTMLElement element in _parsedHtml.all) - { - parsedElements.Add(CreateHtmlObject(element, true)); - System.Runtime.InteropServices.Marshal.ReleaseComObject(element); - } - - _allElements = new WebCmdletElementCollection(parsedElements); - } - - return _allElements; - } - } - -#endregion Properties - -#region Private Fields - - private static Regex _tagRegex; - private static Regex _attribsRegex; - private static Regex _attribNameValueRegex; - -#endregion Private Fields - -#region Methods - - // The "onreadystatechange" event handler - private void ReadyStateChanged(IHTMLEventObj obj) - { - if (String.Equals("complete", _parsedHtml.readyState, StringComparison.OrdinalIgnoreCase)) - { - _stateChangeResetEvent.Set(); - } - System.Runtime.InteropServices.Marshal.ReleaseComObject(obj); - } - - // Load the document in a worker thread - private void LoadDocumentInMtaThread(Object state) - { - try - { - // Create a new IHTMLDocument2 object - _parsedHtml = (IHTMLDocument2)new HTMLDocument(); - - // Attach the event handler - var events = (HTMLDocumentEvents2_Event)_parsedHtml; - events.onreadystatechange += _onreadystatechangeEventHandler; - - // Write the content and close the document - _parsedHtml.write(Content); - _parsedHtml.close(); - - // Wait for the onReadyStateChange event to be fired. On IE9, this never happens - // so we check the readyState directly as well. - bool wait = true; - while (wait && !_stopWorkerThread) - { - if (String.Equals("complete", _parsedHtml.readyState, StringComparison.OrdinalIgnoreCase)) - { - break; - } - - wait = !_stateChangeResetEvent.Wait(100); - } - - // Detach the event handler - events.onreadystatechange -= _onreadystatechangeEventHandler; - } - catch (Exception e) - { - _parsingException = e; - } - finally - { - _loadDocumentResetEvent.Set(); - } - } - - private void EnsureHtmlParser() - { - if (_htmlParsed == false) - { - // Initialization - _stopWorkerThread = false; - _parsingException = null; - _stateChangeResetEvent = new ManualResetEventSlim(); - _loadDocumentResetEvent = new ManualResetEventSlim(); - _onreadystatechangeEventHandler = new HTMLDocumentEvents2_onreadystatechangeEventHandler(ReadyStateChanged); - - // The IHTMLDocument events cannot be handled in STA ApartmentState, so we use a worker thread to load the document - ThreadPool.QueueUserWorkItem(new WaitCallback(LoadDocumentInMtaThread)); - - // Wait for the worker thread to finish loading the document. In the meantime, we check the Ctrl-C every 500ms - bool wait = true; - while (wait) - { - if (_executionContext.CurrentPipelineStopping) - { - // Signal and wait for the worker thread to exit, then break out the loop - _stopWorkerThread = true; - _loadDocumentResetEvent.Wait(); - break; - } - - wait = !_loadDocumentResetEvent.Wait(500); - } - - // Ctrl-C is typed - if (_executionContext.CurrentPipelineStopping) - { - throw new PipelineStoppedException(); - } - - // If there is no Ctrl-C, throw if an exception happened during the parsing - if (_parsingException != null) - { - throw _parsingException; - } - - // Parsing was successful - _htmlParsed = true; - } - - if (_tagRegex == null) - { - _tagRegex = new Regex(@"<\w+((\s+[^""'>/=\s\p{Cc}]+(\s*=\s*(?:"".*?""|'.*?'|[^'"">\s]+))?)+\s*|\s*)/?>", - RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.Compiled); - } - - if (_attribsRegex == null) - { - _attribsRegex = new Regex(@"(?<=\s+)([^""'>/=\s\p{Cc}]+(\s*=\s*(?:"".*?""|'.*?'|[^'"">\s]+))?)", - RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.Compiled); - } - - if (_attribNameValueRegex == null) - { - _attribNameValueRegex = new Regex(@"([^""'>/=\s\p{Cc}]+)(?:\s*=\s*(?:""(.*?)""|'(.*?)'|([^'"">\s]+)))?", - RegexOptions.Singleline | RegexOptions.IgnoreCase | RegexOptions.Compiled); - } - } - - private PSObject CreateHtmlObject(IHTMLElement element, bool addTagName) - { - PSObject elementObject = new PSObject(); - - elementObject.Properties.Add(new PSNoteProperty("innerHTML", element.innerHTML)); - elementObject.Properties.Add(new PSNoteProperty("innerText", element.innerText)); - elementObject.Properties.Add(new PSNoteProperty("outerHTML", element.outerHTML)); - elementObject.Properties.Add(new PSNoteProperty("outerText", element.outerText)); - if (addTagName) - { - elementObject.Properties.Add(new PSNoteProperty("tagName", element.tagName)); - } - - ParseAttributes(element.outerHTML, elementObject); - - return elementObject; - } - - - private void ParseAttributes(string outerHtml, PSObject elementObject) - { - // We might get an empty input for a directive from the HTML file - if (!string.IsNullOrEmpty(outerHtml)) - { - // Extract just the opening tag of the HTML element (omitting the closing tag and any contents, - // including contained HTML elements) - var match = _tagRegex.Match(outerHtml); - - // Extract all the attribute specifications within the HTML element opening tag - var attribMatches = _attribsRegex.Matches(match.Value); - - foreach (Match attribMatch in attribMatches) - { - // Extract the name and value for this attribute (allowing for variations like single/double/no - // quotes, and no value at all) - var nvMatches = _attribNameValueRegex.Match(attribMatch.Value); - Debug.Assert(nvMatches.Groups.Count == 5); - - // Name is always captured by group #1 - string name = nvMatches.Groups[1].Value; - - // The value (if any) is captured by group #2, #3, or #4, depending on quoting or lack thereof - string value = null; - if (nvMatches.Groups[2].Success) - { - value = nvMatches.Groups[2].Value; - } - else if (nvMatches.Groups[3].Success) - { - value = nvMatches.Groups[3].Value; - } - else if (nvMatches.Groups[4].Success) - { - value = nvMatches.Groups[4].Value; - } - - elementObject.Properties.Add(new PSNoteProperty(name, value)); - } - } - } - - private FormObjectCollection BuildFormsCollection() - { - FormObjectCollection forms = new FormObjectCollection(); - - EnsureHtmlParser(); - foreach (IHTMLFormElement form in _parsedHtml.forms) - { - string id = GetElementId(form as IHTMLElement); - if (null == id) - { - id = form.name; - } - - FormObject f = new FormObject(id, form.method, form.action); - foreach (IHTMLElement element in form) - { - IHTMLInputElement input = element as IHTMLInputElement; - if (null != input) - { - id = GetElementId(input as IHTMLElement); - if (null == id) - { - id = input.name; - } - - f.AddField(id, input.value); - } - - System.Runtime.InteropServices.Marshal.ReleaseComObject(element); - } - - forms.Add(f); - - System.Runtime.InteropServices.Marshal.ReleaseComObject(form); - } - - return (forms); - } - - private string GetElementId(IHTMLElement element) - { - return (null == element ? null : element.id); - } - - /// - /// Reads the response content from the web response. - /// - private void InitializeContent() - { - string contentType = ContentHelper.GetContentType(BaseResponse); - if (ContentHelper.IsText(contentType)) - { - // fill the Content buffer - string characterSet = WebResponseHelper.GetCharacterSet(BaseResponse); - this.Content = StreamHelper.DecodeStream(RawContentStream, characterSet); - } - else - { - this.Content = string.Empty; - } - } -#endregion Methods - - /// - /// Dispose the the instance of the class. - /// - public void Dispose() - { - CleanupNativeResources(); - - if (_loadDocumentResetEvent != null) - { - _loadDocumentResetEvent.Dispose(); - } - if (_stateChangeResetEvent != null) - { - _stateChangeResetEvent.Dispose(); - } - - GC.SuppressFinalize(this); - } - - /// - /// Finalizer to free the COM objects. - /// - ~HtmlWebResponseObject() - { - CleanupNativeResources(); - } - - private void CleanupNativeResources() - { - if (_parsedHtml != null) - { - System.Runtime.InteropServices.Marshal.ReleaseComObject(_parsedHtml); - } - } - } -} -#endif \ No newline at end of file diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/Common/HttpVersionCompletionsAttribute.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/Common/HttpVersionCompletionsAttribute.cs new file mode 100644 index 00000000000..903ff4d8f80 --- /dev/null +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/Common/HttpVersionCompletionsAttribute.cs @@ -0,0 +1,51 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#nullable enable + +using System; +using System.Collections.Generic; +using System.Management.Automation; +using System.Net; +using System.Reflection; + +namespace Microsoft.PowerShell.Commands +{ + /// + /// A completer for HTTP version names. + /// + internal sealed class HttpVersionCompletionsAttribute : ArgumentCompletionsAttribute + { + public static readonly string[] AllowedVersions; + + static HttpVersionCompletionsAttribute() + { + FieldInfo[] fields = typeof(HttpVersion).GetFields(BindingFlags.Static | BindingFlags.Public); + + var versions = new List(fields.Length - 1); + + for (int i = 0; i < fields.Length; i++) + { + // skip field Unknown and not Version type + if (fields[i].Name == nameof(HttpVersion.Unknown) || fields[i].FieldType != typeof(Version)) + { + continue; + } + + var version = (Version?)fields[i].GetValue(null); + + if (version is not null) + { + versions.Add(version.ToString()); + } + } + + AllowedVersions = versions.ToArray(); + } + + /// + public HttpVersionCompletionsAttribute() : base(AllowedVersions) + { + } + } +} diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/Common/InvokeRestMethodCommand.Common.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/Common/InvokeRestMethodCommand.Common.cs index 90abdf78bba..22ffaef288c 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/Common/InvokeRestMethodCommand.Common.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/Common/InvokeRestMethodCommand.Common.cs @@ -1,70 +1,196 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#nullable enable using System; -using System.Management.Automation; +using System.Diagnostics.CodeAnalysis; using System.IO; +using System.Management.Automation; +using System.Net.Http; +using System.Text; +using System.Threading; using System.Xml; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + namespace Microsoft.PowerShell.Commands { - public partial class InvokeRestMethodCommand + /// + /// The Invoke-RestMethod command + /// This command makes an HTTP or HTTPS request to a web service, + /// and returns the response in an appropriate way. + /// Intended to work against the wide spectrum of "RESTful" web services + /// currently deployed across the web. + /// + [Cmdlet(VerbsLifecycle.Invoke, "RestMethod", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2096706", DefaultParameterSetName = "StandardMethod")] + public class InvokeRestMethodCommand : WebRequestPSCmdlet { #region Parameters /// - /// gets or sets the parameter Method + /// Enable automatic following of rel links. /// - [Parameter(ParameterSetName = "StandardMethod")] - [Parameter(ParameterSetName = "StandardMethodNoProxy")] - public override WebRequestMethod Method + [Parameter] + [Alias("FL")] + public SwitchParameter FollowRelLink { - get { return base.Method; } - set { base.Method = value; } + get => base._followRelLink; + + set => base._followRelLink = value; } /// - /// gets or sets the parameter CustomMethod + /// Gets or sets the maximum number of rel links to follow. /// - [Parameter(Mandatory=true,ParameterSetName = "CustomMethod")] - [Parameter(Mandatory=true,ParameterSetName = "CustomMethodNoProxy")] - [Alias("CM")] - [ValidateNotNullOrEmpty] - public override string CustomMethod + [Parameter] + [Alias("ML")] + [ValidateRange(1, int.MaxValue)] + public int MaximumFollowRelLink { - get { return base.CustomMethod; } - set { base.CustomMethod = value; } + get => base._maximumFollowRelLink; + + set => base._maximumFollowRelLink = value; } /// - /// enable automatic following of rel links + /// Gets or sets the ResponseHeadersVariable property. /// [Parameter] - [Alias("FL")] - public SwitchParameter FollowRelLink - { - get { return base._followRelLink; } - set { base._followRelLink = value; } - } + [Alias("RHV")] + public string? ResponseHeadersVariable { get; set; } /// - /// gets or sets the maximum number of rel links to follow + /// Gets or sets the variable name to use for storing the status code from the response. /// [Parameter] - [Alias("ML")] - [ValidateRange(1, Int32.MaxValue)] - public int MaximumFollowRelLink + public string? StatusCodeVariable { get; set; } + + #endregion Parameters + + #region Virtual Method Overrides + + /// + /// Process the web response and output corresponding objects. + /// + /// + internal override void ProcessResponse(HttpResponseMessage response) { - get { return base._maximumFollowRelLink; } - set { base._maximumFollowRelLink = value; } + ArgumentNullException.ThrowIfNull(response); + ArgumentNullException.ThrowIfNull(_cancelToken); + + TimeSpan perReadTimeout = ConvertTimeoutSecondsToTimeSpan(OperationTimeoutSeconds); + Stream responseStream = StreamHelper.GetResponseStream(response, _cancelToken.Token); + + if (ShouldWriteToPipeline) + { + responseStream = new BufferingStreamReader(responseStream, perReadTimeout, _cancelToken.Token); + + // First see if it is an RSS / ATOM feed, in which case we can + // stream it - unless the user has overridden it with a return type of "XML" + if (TryProcessFeedStream(responseStream)) + { + // Do nothing, content has been processed. + } + else + { + // Try to get the response encoding from the ContentType header. + string? characterSet = WebResponseHelper.GetCharacterSet(response); + string str = StreamHelper.DecodeStream(responseStream, characterSet, out Encoding encoding, perReadTimeout, _cancelToken.Token); + + string friendlyName = "unknown"; + string encodingWebName = "unknown"; + string encodingPage = encoding.CodePage == -1 ? "unknown" : encoding.CodePage.ToString(); + try + { + // NOTE: These are getter methods that may possibly throw a NotSupportedException exception, + // hence the try/catch + encodingWebName = encoding.WebName; + friendlyName = encoding.EncodingName; + } + catch + { + } + + // NOTE: Tests use this debug output to verify the encoding. + WriteDebug($"WebResponse content encoding: {encodingWebName} ({friendlyName}) CodePage: {encodingPage}"); + + // Determine the response type + RestReturnType returnType = CheckReturnType(response); + + bool convertSuccess = false; + object? obj = null; + Exception? ex = null; + + if (returnType == RestReturnType.Json) + { + convertSuccess = TryConvertToJson(str, out obj, ref ex) || TryConvertToXml(str, out obj, ref ex); + } + // Default to try xml first since it's more common + else + { + convertSuccess = TryConvertToXml(str, out obj, ref ex) || TryConvertToJson(str, out obj, ref ex); + } + + if (!convertSuccess) + { + // Fallback to string + obj = str; + } + + WriteObject(obj); + } + + responseStream.Position = 0; + } + + if (ShouldSaveToOutFile) + { + string outFilePath = WebResponseHelper.GetOutFilePath(response, _qualifiedOutFile); + + WriteVerbose($"File Name: {Path.GetFileName(outFilePath)}"); + + StreamHelper.SaveStreamToFile(responseStream, outFilePath, this, response.Content.Headers.ContentLength.GetValueOrDefault(), perReadTimeout, _cancelToken.Token); + } + + if (!string.IsNullOrEmpty(StatusCodeVariable)) + { + PSVariableIntrinsics vi = SessionState.PSVariable; + vi.Set(StatusCodeVariable, (int)response.StatusCode); + } + + if (!string.IsNullOrEmpty(ResponseHeadersVariable)) + { + PSVariableIntrinsics vi = SessionState.PSVariable; + vi.Set(ResponseHeadersVariable, WebResponseHelper.GetHeadersDictionary(response)); + } } - #endregion Parameters + #endregion Virtual Method Overrides #region Helper Methods - private bool TryProcessFeedStream(BufferingStreamReader responseStream) + private static RestReturnType CheckReturnType(HttpResponseMessage response) + { + ArgumentNullException.ThrowIfNull(response); + + RestReturnType rt = RestReturnType.Detect; + string? contentType = ContentHelper.GetContentType(response); + + if (ContentHelper.IsJson(contentType)) + { + rt = RestReturnType.Json; + } + else if (ContentHelper.IsXml(contentType)) + { + rt = RestReturnType.Xml; + } + + return rt; + } + + private bool TryProcessFeedStream(Stream responseStream) { bool isRssOrFeed = false; @@ -77,8 +203,8 @@ private bool TryProcessFeedStream(BufferingStreamReader responseStream) int readCount = 0; while ((readCount < 10) && reader.Read()) { - if (String.Equals("rss", reader.Name, StringComparison.OrdinalIgnoreCase) || - String.Equals("feed", reader.Name, StringComparison.OrdinalIgnoreCase)) + if (string.Equals("rss", reader.Name, StringComparison.OrdinalIgnoreCase) || + string.Equals("feed", reader.Name, StringComparison.OrdinalIgnoreCase)) { isRssOrFeed = true; break; @@ -89,8 +215,9 @@ private bool TryProcessFeedStream(BufferingStreamReader responseStream) if (isRssOrFeed) { - XmlDocument workingDocument = new XmlDocument(); - // performing a Read() here to avoid rrechecking + XmlDocument workingDocument = new(); + + // Performing a Read() here to avoid rechecking // "rss" or "feed" items reader.Read(); while (!reader.EOF) @@ -101,8 +228,8 @@ private bool TryProcessFeedStream(BufferingStreamReader responseStream) string.Equals("Entry", reader.Name, StringComparison.OrdinalIgnoreCase)) ) { - // this one will do reader.Read() internally - XmlNode result = workingDocument.ReadNode(reader); + // This one will do reader.Read() internally + XmlNode? result = workingDocument.ReadNode(reader); WriteObject(result); } else @@ -112,7 +239,10 @@ private bool TryProcessFeedStream(BufferingStreamReader responseStream) } } } - catch (XmlException) { } + catch (XmlException) + { + // Catch XmlException + } finally { responseStream.Seek(0, SeekOrigin.Begin); @@ -122,14 +252,14 @@ private bool TryProcessFeedStream(BufferingStreamReader responseStream) } // Mostly cribbed from Serialization.cs#GetXmlReaderSettingsForCliXml() - private XmlReaderSettings GetSecureXmlReaderSettings() + private static XmlReaderSettings GetSecureXmlReaderSettings() { - XmlReaderSettings xrs = new XmlReaderSettings(); + XmlReaderSettings xrs = new(); xrs.CheckCharacters = false; xrs.CloseInput = false; - //The XML data needs to be in conformance to the rules for a well-formed XML 1.0 document. + // The XML data needs to be in conformance to the rules for a well-formed XML 1.0 document. xrs.IgnoreProcessingInstructions = true; xrs.MaxCharactersFromEntities = 1024; xrs.DtdProcessing = DtdProcessing.Ignore; @@ -138,14 +268,14 @@ private XmlReaderSettings GetSecureXmlReaderSettings() return xrs; } - private bool TryConvertToXml(string xml, out object doc, ref Exception exRef) + private static bool TryConvertToXml(string xml, [NotNullWhen(true)] out object? doc, ref Exception? exRef) { try { XmlReaderSettings settings = GetSecureXmlReaderSettings(); XmlReader xmlReader = XmlReader.Create(new StringReader(xml), settings); - var xmlDoc = new XmlDocument(); + XmlDocument xmlDoc = new(); xmlDoc.PreserveWhitespace = true; xmlDoc.Load(xmlReader); @@ -156,104 +286,112 @@ private bool TryConvertToXml(string xml, out object doc, ref Exception exRef) exRef = ex; doc = null; } - return (null != doc); + + return doc != null; } - private bool TryConvertToJson(string json, out object obj, ref Exception exRef) + private static bool TryConvertToJson(string json, [NotNullWhen(true)] out object? obj, ref Exception? exRef) { + bool converted = false; try { - ErrorRecord error; - obj = JsonObject.ConvertFromJson(json, out error); + obj = JsonObject.ConvertFromJson(json, out ErrorRecord error); + + if (obj == null) + { + // This ensures that a null returned by ConvertFromJson() is the actual JSON null literal. + // if not, the ArgumentException will be caught. + JToken.Parse(json); + } if (error != null) { exRef = error.Exception; obj = null; } + else + { + converted = true; + } } - catch (ArgumentException ex) + catch (Exception ex) when (ex is ArgumentException || ex is InvalidOperationException) { exRef = ex; obj = null; } - catch (InvalidOperationException ex) + catch (JsonException ex) { - exRef = ex; + string msg = string.Format(System.Globalization.CultureInfo.CurrentCulture, WebCmdletStrings.JsonDeserializationFailed, ex.Message); + exRef = new ArgumentException(msg, ex); obj = null; } - return (null != obj); + + return converted; } - #endregion + #endregion Helper Methods /// - /// enum for rest return type. + /// Enum for rest return type. /// public enum RestReturnType { /// /// Return type not defined in response, - /// best effort detect + /// best effort detect. /// Detect, /// - /// Json return type + /// Json return type. /// - [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] Json, /// - /// Xml return type + /// Xml return type. /// Xml, } - internal class BufferingStreamReader : Stream + internal sealed class BufferingStreamReader : Stream { - internal BufferingStreamReader(Stream baseStream) + internal BufferingStreamReader(Stream baseStream, TimeSpan perReadTimeout, CancellationToken cancellationToken) { _baseStream = baseStream; _streamBuffer = new MemoryStream(); _length = long.MaxValue; _copyBuffer = new byte[4096]; + _perReadTimeout = perReadTimeout; + _cancellationToken = cancellationToken; } - private Stream _baseStream; - private MemoryStream _streamBuffer; - private byte[] _copyBuffer; + private readonly Stream _baseStream; + private readonly MemoryStream _streamBuffer; + private readonly byte[] _copyBuffer; + private readonly TimeSpan _perReadTimeout; + private readonly CancellationToken _cancellationToken; - public override bool CanRead - { - get { return true; } - } + public override bool CanRead => true; - public override bool CanSeek - { - get { return true; } - } + public override bool CanSeek => true; - public override bool CanWrite - { - get { return false; } - } + public override bool CanWrite => false; public override void Flush() { _streamBuffer.SetLength(0); } - public override long Length - { - get { return _length; } - } + public override long Length => _length; + private long _length; public override long Position { - get { return _streamBuffer.Position; } - set { _streamBuffer.Position = value; } + get => _streamBuffer.Position; + + set => _streamBuffer.Position = value; } public override int Read(byte[] buffer, int offset, int count) @@ -261,13 +399,12 @@ public override int Read(byte[] buffer, int offset, int count) long previousPosition = Position; bool consumedStream = false; int totalCount = count; - while ((!consumedStream) && - ((Position + totalCount) > _streamBuffer.Length)) + while (!consumedStream && (Position + totalCount) > _streamBuffer.Length) { // If we don't have enough data to fill this from memory, cache more. // We try to read 4096 bytes from base stream every time, so at most we // may cache 4095 bytes more than what is required by the Read operation. - int bytesRead = _baseStream.Read(_copyBuffer, 0, _copyBuffer.Length); + int bytesRead = _baseStream.ReadAsync(_copyBuffer.AsMemory(), _perReadTimeout, _cancellationToken).GetAwaiter().GetResult(); if (_streamBuffer.Position < _streamBuffer.Length) { @@ -316,4 +453,4 @@ public override void Write(byte[] buffer, int offset, int count) } } } -} \ No newline at end of file +} diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/Common/WebRequestPSCmdlet.Common.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/Common/WebRequestPSCmdlet.Common.cs index e9eb8f77e56..f1a455974b9 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/Common/WebRequestPSCmdlet.Common.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/Common/WebRequestPSCmdlet.Common.cs @@ -1,199 +1,418 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. using System; +using System.Collections; +using System.Collections.Generic; using System.Collections.ObjectModel; +using System.Globalization; +using System.IO; using System.Management.Automation; using System.Net; -using System.IO; -using System.Text; -using System.Collections; -using System.Globalization; +using System.Net.Http; +using System.Net.Http.Headers; +using System.Net.Sockets; +using System.Security; +using System.Security.Authentication; using System.Security.Cryptography; using System.Security.Cryptography.X509Certificates; -#if !CORECLR -using mshtml; -#endif -using Microsoft.Win32; +using System.Text; +using System.Text.Json; +using System.Text.Json.Nodes; +using System.Text.RegularExpressions; +using System.Threading; +using System.Threading.Tasks; +using System.Xml; namespace Microsoft.PowerShell.Commands { + /// + /// The valid values for the -Authentication parameter for Invoke-RestMethod and Invoke-WebRequest. + /// + public enum WebAuthenticationType + { + /// + /// No authentication. Default. + /// + None, + + /// + /// RFC-7617 Basic Authentication. Requires -Credential. + /// + Basic, + + /// + /// RFC-6750 OAuth 2.0 Bearer Authentication. Requires -Token. + /// + Bearer, + + /// + /// RFC-6750 OAuth 2.0 Bearer Authentication. Requires -Token. + /// + OAuth, + } + + // WebSslProtocol is used because not all SslProtocols are supported by HttpClientHandler. + // Also SslProtocols.Default is not the "default" for HttpClientHandler as SslProtocols.Ssl3 is not supported. + /// + /// The valid values for the -SslProtocol parameter for Invoke-RestMethod and Invoke-WebRequest. + /// + [Flags] + public enum WebSslProtocol + { + /// + /// No SSL protocol will be set and the system defaults will be used. + /// + Default = SslProtocols.None, + + /// + /// Specifies the TLS 1.0 is obsolete. Using this value now defaults to TLS 1.2. + /// + Tls = SslProtocols.Tls12, + + /// + /// Specifies the TLS 1.1 is obsolete. Using this value now defaults to TLS 1.2. + /// + Tls11 = SslProtocols.Tls12, + + /// + /// Specifies the TLS 1.2 security protocol. The TLS protocol is defined in IETF RFC 5246. + /// + Tls12 = SslProtocols.Tls12, + + /// + /// Specifies the TLS 1.3 security protocol. The TLS protocol is defined in IETF RFC 8446. + /// + Tls13 = SslProtocols.Tls13 + } + /// /// Base class for Invoke-RestMethod and Invoke-WebRequest commands. /// - public abstract partial class WebRequestPSCmdlet : PSCmdlet + public abstract class WebRequestPSCmdlet : PSCmdlet, IDisposable { + #region Fields + + /// + /// Used to prefix the headers in debug and verbose messaging. + /// + internal const string DebugHeaderPrefix = "--- "; + + /// + /// Cancellation token source. + /// + internal CancellationTokenSource _cancelToken = null; + + /// + /// Automatically follow Rel Links. + /// + internal bool _followRelLink = false; + + /// + /// Maximum number of Rel Links to follow. + /// + internal int _maximumFollowRelLink = int.MaxValue; + + /// + /// Maximum number of Redirects to follow. + /// + internal int _maximumRedirection; + + /// + /// Parse Rel Links. + /// + internal bool _parseRelLink = false; + + /// + /// Automatically follow Rel Links. + /// + internal Dictionary _relationLink = null; + + /// + /// The current size of the local file being resumed. + /// + private long _resumeFileSize = 0; + + /// + /// The remote endpoint returned a 206 status code indicating successful resume. + /// + private bool _resumeSuccess = false; + + /// + /// True if the Dispose() method has already been called to cleanup Disposable fields. + /// + private bool _disposed = false; + + #endregion Fields + #region Virtual Properties #region URI /// - /// gets or sets the parameter UseBasicParsing + /// Deprecated. Gets or sets UseBasicParsing. This has no affect on the operation of the Cmdlet. /// - [Parameter] - public virtual SwitchParameter UseBasicParsing { get; set; } + [Parameter(DontShow = true)] + public virtual SwitchParameter UseBasicParsing { get; set; } = true; /// - /// gets or sets the Uri property + /// Gets or sets the Uri property. /// [Parameter(Position = 0, Mandatory = true)] [ValidateNotNullOrEmpty] public virtual Uri Uri { get; set; } - #endregion + #endregion URI + + #region HTTP Version + + /// + /// Gets or sets the HTTP Version property. + /// + [Parameter] + [ArgumentToVersionTransformation] + [HttpVersionCompletions] + public virtual Version HttpVersion { get; set; } + + #endregion HTTP Version #region Session /// - /// gets or sets the Session property + /// Gets or sets the Session property. /// [Parameter] public virtual WebRequestSession WebSession { get; set; } /// - /// gets or sets the SessionVariable property + /// Gets or sets the SessionVariable property. /// [Parameter] [Alias("SV")] public virtual string SessionVariable { get; set; } - #endregion + #endregion Session #region Authorization and Credentials /// - /// gets or sets the Credential property + /// Gets or sets the AllowUnencryptedAuthentication property. + /// + [Parameter] + public virtual SwitchParameter AllowUnencryptedAuthentication { get; set; } + + /// + /// Gets or sets the Authentication property used to determine the Authentication method for the web session. + /// Authentication does not work with UseDefaultCredentials. + /// Authentication over unencrypted sessions requires AllowUnencryptedAuthentication. + /// Basic: Requires Credential. + /// OAuth/Bearer: Requires Token. + /// + [Parameter] + public virtual WebAuthenticationType Authentication { get; set; } = WebAuthenticationType.None; + + /// + /// Gets or sets the Credential property. /// [Parameter] [Credential] public virtual PSCredential Credential { get; set; } /// - /// gets or sets the UseDefaultCredentials property + /// Gets or sets the UseDefaultCredentials property. /// [Parameter] public virtual SwitchParameter UseDefaultCredentials { get; set; } /// - /// gets or sets the CertificateThumbprint property + /// Gets or sets the CertificateThumbprint property. /// [Parameter] [ValidateNotNullOrEmpty] public virtual string CertificateThumbprint { get; set; } /// - /// gets or sets the Certificate property + /// Gets or sets the Certificate property. /// [Parameter] [ValidateNotNull] public virtual X509Certificate Certificate { get; set; } /// - /// gets or sets the SkipCertificateCheck property + /// Gets or sets the SkipCertificateCheck property. /// [Parameter] public virtual SwitchParameter SkipCertificateCheck { get; set; } - #endregion + /// + /// Gets or sets the TLS/SSL protocol used by the Web Cmdlet. + /// + [Parameter] + public virtual WebSslProtocol SslProtocol { get; set; } = WebSslProtocol.Default; + + /// + /// Gets or sets the Token property. Token is required by Authentication OAuth and Bearer. + /// + [Parameter] + public virtual SecureString Token { get; set; } + + #endregion Authorization and Credentials #region Headers /// - /// gets or sets the UserAgent property + /// Gets or sets the UserAgent property. /// [Parameter] public virtual string UserAgent { get; set; } /// - /// gets or sets the DisableKeepAlive property + /// Gets or sets the DisableKeepAlive property. /// [Parameter] public virtual SwitchParameter DisableKeepAlive { get; set; } /// - /// gets or sets the TimeOut property + /// Gets or sets the ConnectionTimeoutSeconds property. + /// + /// + /// This property applies to sending the request and receiving the response headers only. + /// + [Alias("TimeoutSec")] + [Parameter] + [ValidateRange(0, int.MaxValue)] + public virtual int ConnectionTimeoutSeconds { get; set; } + + /// + /// Gets or sets the OperationTimeoutSeconds property. /// + /// + /// This property applies to each read operation when receiving the response body. + /// [Parameter] - [ValidateRange(0, Int32.MaxValue)] - public virtual int TimeoutSec { get; set; } + [ValidateRange(0, int.MaxValue)] + public virtual int OperationTimeoutSeconds { get; set; } /// - /// gets or sets the Headers property + /// Gets or sets the Headers property. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")] [Parameter] public virtual IDictionary Headers { get; set; } - #endregion + /// + /// Gets or sets the SkipHeaderValidation property. + /// + /// + /// This property adds headers to the request's header collection without validation. + /// + [Parameter] + public virtual SwitchParameter SkipHeaderValidation { get; set; } + + #endregion Headers #region Redirect /// - /// gets or sets the RedirectMax property + /// Gets or sets the AllowInsecureRedirect property used to follow HTTP redirects from HTTPS. /// [Parameter] - [ValidateRange(0, Int32.MaxValue)] - public virtual int MaximumRedirection - { - get { return _maximumRedirection; } - set { _maximumRedirection = value; } - } - private int _maximumRedirection = -1; + public virtual SwitchParameter AllowInsecureRedirect { get; set; } + + /// + /// Gets or sets the RedirectMax property. + /// + [Parameter] + [ValidateRange(0, int.MaxValue)] + public virtual int MaximumRedirection { get; set; } = -1; - #endregion + /// + /// Gets or sets the MaximumRetryCount property, which determines the number of retries of a failed web request. + /// + [Parameter] + [ValidateRange(0, int.MaxValue)] + public virtual int MaximumRetryCount { get; set; } + + /// + /// Gets or sets the PreserveAuthorizationOnRedirect property. + /// + /// + /// This property overrides compatibility with web requests on Windows. + /// On FullCLR (WebRequest), authorization headers are stripped during redirect. + /// CoreCLR (HTTPClient) does not have this behavior so web requests that work on + /// PowerShell/FullCLR can fail with PowerShell/CoreCLR. To provide compatibility, + /// we'll detect requests with an Authorization header and automatically strip + /// the header when the first redirect occurs. This switch turns off this logic for + /// edge cases where the authorization header needs to be preserved across redirects. + /// + [Parameter] + public virtual SwitchParameter PreserveAuthorizationOnRedirect { get; set; } + + /// + /// Gets or sets the RetryIntervalSec property, which determines the number seconds between retries. + /// + [Parameter] + [ValidateRange(1, int.MaxValue)] + public virtual int RetryIntervalSec { get; set; } = 5; + + #endregion Redirect #region Method /// - /// gets or sets the Method property + /// Gets or sets the Method property. /// [Parameter(ParameterSetName = "StandardMethod")] [Parameter(ParameterSetName = "StandardMethodNoProxy")] - public virtual WebRequestMethod Method - { - get { return _method; } - set { _method = value; } - } - private WebRequestMethod _method = WebRequestMethod.Default; + public virtual WebRequestMethod Method { get; set; } = WebRequestMethod.Default; /// - /// gets or sets the CustomMethod property + /// Gets or sets the CustomMethod property. /// - [Parameter(Mandatory=true,ParameterSetName = "CustomMethod")] - [Parameter(Mandatory=true,ParameterSetName = "CustomMethodNoProxy")] + [Parameter(Mandatory = true, ParameterSetName = "CustomMethod")] + [Parameter(Mandatory = true, ParameterSetName = "CustomMethodNoProxy")] [Alias("CM")] [ValidateNotNullOrEmpty] - public virtual string CustomMethod - { - get { return _customMethod; } - set { _customMethod = value; } - } + public virtual string CustomMethod { get => _customMethod; set => _customMethod = value.ToUpperInvariant(); } + private string _customMethod; - #endregion + /// + /// Gets or sets the PreserveHttpMethodOnRedirect property. + /// + [Parameter] + public virtual SwitchParameter PreserveHttpMethodOnRedirect { get; set; } + + /// + /// Gets or sets the UnixSocket property. + /// + [Parameter] + [ValidateNotNullOrEmpty] + public virtual UnixDomainSocketEndPoint UnixSocket { get; set; } + + #endregion Method #region NoProxy /// - /// gets or sets the NoProxy property + /// Gets or sets the NoProxy property. /// - [Parameter(Mandatory=true,ParameterSetName = "CustomMethodNoProxy")] - [Parameter(Mandatory=true,ParameterSetName = "StandardMethodNoProxy")] + [Parameter(Mandatory = true, ParameterSetName = "CustomMethodNoProxy")] + [Parameter(Mandatory = true, ParameterSetName = "StandardMethodNoProxy")] public virtual SwitchParameter NoProxy { get; set; } - #endregion + #endregion NoProxy #region Proxy /// - /// gets or sets the Proxy property + /// Gets or sets the Proxy property. /// [Parameter(ParameterSetName = "StandardMethod")] [Parameter(ParameterSetName = "CustomMethod")] public virtual Uri Proxy { get; set; } /// - /// gets or sets the ProxyCredential property + /// Gets or sets the ProxyCredential property. /// [Parameter(ParameterSetName = "StandardMethod")] [Parameter(ParameterSetName = "CustomMethod")] @@ -201,220 +420,536 @@ public virtual string CustomMethod public virtual PSCredential ProxyCredential { get; set; } /// - /// gets or sets the ProxyUseDefaultCredentials property + /// Gets or sets the ProxyUseDefaultCredentials property. /// [Parameter(ParameterSetName = "StandardMethod")] [Parameter(ParameterSetName = "CustomMethod")] public virtual SwitchParameter ProxyUseDefaultCredentials { get; set; } - #endregion + #endregion Proxy #region Input /// - /// gets or sets the Body property + /// Gets or sets the Body property. /// [Parameter(ValueFromPipeline = true)] public virtual object Body { get; set; } /// - /// gets or sets the ContentType property + /// Dictionary for use with RFC-7578 multipart/form-data submissions. + /// Keys are form fields and their respective values are form values. + /// A value may be a collection of form values or single form value. + /// + [Parameter] + public virtual IDictionary Form { get; set; } + + /// + /// Gets or sets the ContentType property. /// [Parameter] public virtual string ContentType { get; set; } /// - /// gets or sets the TransferEncoding property + /// Gets or sets the TransferEncoding property. /// [Parameter] [ValidateSet("chunked", "compress", "deflate", "gzip", "identity", IgnoreCase = true)] public virtual string TransferEncoding { get; set; } /// - /// gets or sets the InFile property + /// Gets or sets the InFile property. /// [Parameter] + [ValidateNotNullOrEmpty] public virtual string InFile { get; set; } /// - /// keep the original file path after the resolved provider path is - /// assigned to InFile + /// Keep the original file path after the resolved provider path is assigned to InFile. /// private string _originalFilePath; - #endregion + #endregion Input #region Output /// - /// gets or sets the OutFile property + /// Gets or sets the OutFile property. /// [Parameter] + [ValidateNotNullOrEmpty] public virtual string OutFile { get; set; } /// - /// gets or sets the PassThrough property + /// Gets or sets the PassThrough property. /// [Parameter] public virtual SwitchParameter PassThru { get; set; } - #endregion + /// + /// Resumes downloading a partial or incomplete file. OutFile is required. + /// + [Parameter] + public virtual SwitchParameter Resume { get; set; } + + /// + /// Gets or sets whether to skip checking HTTP status for error codes. + /// + [Parameter] + public virtual SwitchParameter SkipHttpErrorCheck { get; set; } + + #endregion Output #endregion Virtual Properties - #region Virtual Methods + #region Helper Properties - internal virtual void ValidateParameters() - { - // sessions - if ((null != WebSession) && (null != SessionVariable)) - { - ErrorRecord error = GetValidationError(WebCmdletStrings.SessionConflict, - "WebCmdletSessionConflictException"); - ThrowTerminatingError(error); - } + internal string QualifiedOutFile => QualifyFilePath(OutFile); - // credentials - if (UseDefaultCredentials && (null != Credential)) - { - ErrorRecord error = GetValidationError(WebCmdletStrings.CredentialConflict, - "WebCmdletCredentialConflictException"); - ThrowTerminatingError(error); - } + internal string _qualifiedOutFile; - // Proxy server - if (ProxyUseDefaultCredentials && (null != ProxyCredential)) - { - ErrorRecord error = GetValidationError(WebCmdletStrings.ProxyCredentialConflict, - "WebCmdletProxyCredentialConflictException"); - ThrowTerminatingError(error); - } - else if ((null == Proxy) && ((null != ProxyCredential) || ProxyUseDefaultCredentials)) - { - ErrorRecord error = GetValidationError(WebCmdletStrings.ProxyUriNotSupplied, - "WebCmdletProxyUriNotSuppliedException"); - ThrowTerminatingError(error); - } + internal bool ShouldCheckHttpStatus => !SkipHttpErrorCheck; - // request body content - if ((null != Body) && (null != InFile)) - { - ErrorRecord error = GetValidationError(WebCmdletStrings.BodyConflict, - "WebCmdletBodyConflictException"); - ThrowTerminatingError(error); - } + /// + /// Determines whether writing to a file should Resume and append rather than overwrite. + /// + internal bool ShouldResume => Resume.IsPresent && _resumeSuccess; + + internal bool ShouldSaveToOutFile => !string.IsNullOrEmpty(OutFile); + + internal bool ShouldWriteToPipeline => !ShouldSaveToOutFile || PassThru; - // validate InFile path - if (InFile != null) + #endregion Helper Properties + + #region Abstract Methods + + /// + /// Read the supplied WebResponse object and push the resulting output into the pipeline. + /// + /// Instance of a WebResponse object to be processed. + internal abstract void ProcessResponse(HttpResponseMessage response); + + #endregion Abstract Methods + + #region Overrides + + /// + /// The main execution method for cmdlets derived from WebRequestPSCmdlet. + /// + protected override void ProcessRecord() + { + try { - ProviderInfo provider = null; - ErrorRecord errorRecord = null; + // Set cmdlet context for write progress + ValidateParameters(); + PrepareSession(); - try - { - Collection providerPaths = GetResolvedProviderPathFromPSPath(InFile, out provider); + // If the request contains an authorization header and PreserveAuthorizationOnRedirect is not set, + // it needs to be stripped on the first redirect. + bool keepAuthorizationOnRedirect = PreserveAuthorizationOnRedirect.IsPresent + && WebSession.Headers.ContainsKey(HttpKnownHeaderNames.Authorization); - if (!provider.Name.Equals(FileSystemProvider.ProviderName, StringComparison.OrdinalIgnoreCase)) + bool handleRedirect = keepAuthorizationOnRedirect || AllowInsecureRedirect || PreserveHttpMethodOnRedirect; + + HttpClient client = GetHttpClient(handleRedirect); + + int followedRelLink = 0; + Uri uri = Uri; + do + { + if (followedRelLink > 0) { - errorRecord = GetValidationError(WebCmdletStrings.NotFilesystemPath, - "WebCmdletInFileNotFilesystemPathException", InFile); + string linkVerboseMsg = string.Format( + CultureInfo.CurrentCulture, + WebCmdletStrings.FollowingRelLinkVerboseMsg, + uri.AbsoluteUri); + + WriteVerbose(linkVerboseMsg); } - else + + using (HttpRequestMessage request = GetRequest(uri)) { - if (providerPaths.Count > 1) + FillRequestStream(request); + try { - errorRecord = GetValidationError(WebCmdletStrings.MultiplePathsResolved, - "WebCmdletInFileMultiplePathsResolvedException", InFile); + _maximumRedirection = WebSession.MaximumRedirection; + + using HttpResponseMessage response = GetResponse(client, request, handleRedirect); + + bool _isSuccess = response.IsSuccessStatusCode; + + // Check if the Resume range was not satisfiable because the file already completed downloading. + // This happens when the local file is the same size as the remote file. + if (Resume.IsPresent + && response.StatusCode == HttpStatusCode.RequestedRangeNotSatisfiable + && response.Content.Headers.ContentRange.HasLength + && response.Content.Headers.ContentRange.Length == _resumeFileSize) + { + _isSuccess = true; + WriteVerbose(string.Format( + CultureInfo.CurrentCulture, + WebCmdletStrings.OutFileWritingSkipped, + OutFile)); + + // Disable writing to the OutFile. + OutFile = null; + } + + // Detect insecure redirection. + if (!AllowInsecureRedirect) + { + // We will skip detection if either of the URIs is relative, because the 'Scheme' property is not supported on a relative URI. + // If we have to skip the check, an error may be thrown later if it's actually an insecure https-to-http redirect. + bool originIsHttps = response.RequestMessage.RequestUri.IsAbsoluteUri && response.RequestMessage.RequestUri.Scheme == "https"; + bool destinationIsHttp = response.Headers.Location is not null && response.Headers.Location.IsAbsoluteUri && response.Headers.Location.Scheme == "http"; + + if (originIsHttps && destinationIsHttp) + { + ErrorRecord er = new(new InvalidOperationException(), "InsecureRedirection", ErrorCategory.InvalidOperation, request); + er.ErrorDetails = new ErrorDetails(WebCmdletStrings.InsecureRedirection); + ThrowTerminatingError(er); + } + } + + if (ShouldCheckHttpStatus && !_isSuccess) + { + string message = string.Format( + CultureInfo.CurrentCulture, + WebCmdletStrings.ResponseStatusCodeFailure, + (int)response.StatusCode, + response.ReasonPhrase); + + HttpResponseException httpEx = new(message, response); + ErrorRecord er = new(httpEx, "WebCmdletWebResponseException", ErrorCategory.InvalidOperation, request); + string detailMsg = string.Empty; + try + { + string contentType = ContentHelper.GetContentType(response); + long? contentLength = response.Content.Headers.ContentLength; + + // We can't use ReadAsStringAsync because it doesn't have per read timeouts + TimeSpan perReadTimeout = ConvertTimeoutSecondsToTimeSpan(OperationTimeoutSeconds); + string characterSet = WebResponseHelper.GetCharacterSet(response); + var responseStream = StreamHelper.GetResponseStream(response, _cancelToken.Token); + int initialCapacity = (int)Math.Min(contentLength ?? StreamHelper.DefaultReadBuffer, StreamHelper.DefaultReadBuffer); + var bufferedStream = new WebResponseContentMemoryStream(responseStream, initialCapacity, this, contentLength, perReadTimeout, _cancelToken.Token); + string error = StreamHelper.DecodeStream(bufferedStream, characterSet, out Encoding encoding, perReadTimeout, _cancelToken.Token); + detailMsg = FormatErrorMessage(error, contentType); + } + catch (Exception ex) + { + // Catch all + er.ErrorDetails = new ErrorDetails(ex.ToString()); + } + + if (!string.IsNullOrEmpty(detailMsg)) + { + er.ErrorDetails = new ErrorDetails(detailMsg); + } + + ThrowTerminatingError(er); + } + + if (_parseRelLink || _followRelLink) + { + ParseLinkHeader(response); + } + + ProcessResponse(response); + UpdateSession(response); + + // If we hit our maximum redirection count, generate an error. + // Errors with redirection counts of greater than 0 are handled automatically by .NET, but are + // impossible to detect programmatically when we hit this limit. By handling this ourselves + // (and still writing out the result), users can debug actual HTTP redirect problems. + if (_maximumRedirection == 0 && IsRedirectCode(response.StatusCode)) + { + ErrorRecord er = new(new InvalidOperationException(), "MaximumRedirectExceeded", ErrorCategory.InvalidOperation, request); + er.ErrorDetails = new ErrorDetails(WebCmdletStrings.MaximumRedirectionCountExceeded); + WriteError(er); + } } - else if (providerPaths.Count == 0) + catch (TimeoutException ex) { - errorRecord = GetValidationError(WebCmdletStrings.NoPathResolved, - "WebCmdletInFileNoPathResolvedException", InFile); + ErrorRecord er = new(ex, "OperationTimeoutReached", ErrorCategory.OperationTimeout, null); + ThrowTerminatingError(er); } - else + catch (HttpRequestException ex) { - if (Directory.Exists(providerPaths[0])) + ErrorRecord er = new(ex, "WebCmdletWebResponseException", ErrorCategory.InvalidOperation, request); + if (ex.InnerException is not null) { - errorRecord = GetValidationError(WebCmdletStrings.DirecotryPathSpecified, - "WebCmdletInFileNotFilePathException", InFile); + er.ErrorDetails = new ErrorDetails(ex.InnerException.Message); } - _originalFilePath = InFile; - InFile = providerPaths[0]; + + ThrowTerminatingError(er); + } + finally + { + _cancelToken?.Dispose(); + _cancelToken = null; } - } - } - catch (ItemNotFoundException pathNotFound) - { - errorRecord = new ErrorRecord(pathNotFound.ErrorRecord, pathNotFound); - } - catch (ProviderNotFoundException providerNotFound) - { - errorRecord = new ErrorRecord(providerNotFound.ErrorRecord, providerNotFound); - } - catch (System.Management.Automation.DriveNotFoundException driveNotFound) - { - errorRecord = new ErrorRecord(driveNotFound.ErrorRecord, driveNotFound); - } - if (errorRecord != null) - { - ThrowTerminatingError(errorRecord); + if (_followRelLink) + { + if (!_relationLink.ContainsKey("next")) + { + return; + } + + uri = new Uri(_relationLink["next"]); + followedRelLink++; + } + } } + while (_followRelLink && (followedRelLink < _maximumFollowRelLink)); } - - // output ?? - if (PassThru && (OutFile == null)) + catch (CryptographicException ex) { - ErrorRecord error = GetValidationError(WebCmdletStrings.OutFileMissing, - "WebCmdletOutFileMissingException"); - ThrowTerminatingError(error); + ErrorRecord er = new(ex, "WebCmdletCertificateException", ErrorCategory.SecurityError, null); + ThrowTerminatingError(er); + } + catch (NotSupportedException ex) + { + ErrorRecord er = new(ex, "WebCmdletIEDomNotSupportedException", ErrorCategory.NotImplemented, null); + ThrowTerminatingError(er); } } - internal virtual void PrepareSession() + /// + /// To implement ^C. + /// + protected override void StopProcessing() => _cancelToken?.Cancel(); + + /// + /// Disposes the associated WebSession if it is not being used as part of a persistent session. + /// + /// True when called from Dispose() and false when called from finalizer. + protected virtual void Dispose(bool disposing) { - // make sure we have a valid WebRequestSession object to work with - if (null == WebSession) + if (!_disposed) { - WebSession = new WebRequestSession(); + if (disposing && !IsPersistentSession()) + { + WebSession?.Dispose(); + WebSession = null; + } + + _disposed = true; + } + } + + /// + /// Disposes the associated WebSession if it is not being used as part of a persistent session. + /// + public void Dispose() + { + Dispose(disposing: true); + GC.SuppressFinalize(this); + } + + #endregion Overrides + + #region Virtual Methods + + internal virtual void ValidateParameters() + { + // Sessions + if (WebSession is not null && SessionVariable is not null) + { + ErrorRecord error = GetValidationError(WebCmdletStrings.SessionConflict, "WebCmdletSessionConflictException"); + ThrowTerminatingError(error); + } + + // Authentication + if (UseDefaultCredentials && Authentication != WebAuthenticationType.None) + { + ErrorRecord error = GetValidationError(WebCmdletStrings.AuthenticationConflict, "WebCmdletAuthenticationConflictException"); + ThrowTerminatingError(error); + } + + if (Authentication != WebAuthenticationType.None && Token is not null && Credential is not null) + { + ErrorRecord error = GetValidationError(WebCmdletStrings.AuthenticationTokenConflict, "WebCmdletAuthenticationTokenConflictException"); + ThrowTerminatingError(error); + } + + if (Authentication == WebAuthenticationType.Basic && Credential is null) + { + ErrorRecord error = GetValidationError(WebCmdletStrings.AuthenticationCredentialNotSupplied, "WebCmdletAuthenticationCredentialNotSuppliedException"); + ThrowTerminatingError(error); + } + + if ((Authentication == WebAuthenticationType.OAuth || Authentication == WebAuthenticationType.Bearer) && Token is null) + { + ErrorRecord error = GetValidationError(WebCmdletStrings.AuthenticationTokenNotSupplied, "WebCmdletAuthenticationTokenNotSuppliedException"); + ThrowTerminatingError(error); + } + + if (!AllowUnencryptedAuthentication && (Authentication != WebAuthenticationType.None || Credential is not null || UseDefaultCredentials) && Uri.Scheme != "https") + { + ErrorRecord error = GetValidationError(WebCmdletStrings.AllowUnencryptedAuthenticationRequired, "WebCmdletAllowUnencryptedAuthenticationRequiredException"); + ThrowTerminatingError(error); + } + + // Credentials + if (UseDefaultCredentials && Credential is not null) + { + ErrorRecord error = GetValidationError(WebCmdletStrings.CredentialConflict, "WebCmdletCredentialConflictException"); + ThrowTerminatingError(error); + } + + // Proxy server + if (ProxyUseDefaultCredentials && ProxyCredential is not null) + { + ErrorRecord error = GetValidationError(WebCmdletStrings.ProxyCredentialConflict, "WebCmdletProxyCredentialConflictException"); + ThrowTerminatingError(error); + } + else if (Proxy is null && (ProxyCredential is not null || ProxyUseDefaultCredentials)) + { + ErrorRecord error = GetValidationError(WebCmdletStrings.ProxyUriNotSupplied, "WebCmdletProxyUriNotSuppliedException"); + ThrowTerminatingError(error); + } + + // Request body content + if (Body is not null && InFile is not null) + { + ErrorRecord error = GetValidationError(WebCmdletStrings.BodyConflict, "WebCmdletBodyConflictException"); + ThrowTerminatingError(error); + } + + if (Body is not null && Form is not null) + { + ErrorRecord error = GetValidationError(WebCmdletStrings.BodyFormConflict, "WebCmdletBodyFormConflictException"); + ThrowTerminatingError(error); + } + + if (InFile is not null && Form is not null) + { + ErrorRecord error = GetValidationError(WebCmdletStrings.FormInFileConflict, "WebCmdletFormInFileConflictException"); + ThrowTerminatingError(error); + } + + // Validate InFile path + if (InFile is not null) + { + ErrorRecord errorRecord = null; + + try + { + Collection providerPaths = GetResolvedProviderPathFromPSPath(InFile, out ProviderInfo provider); + + if (!provider.Name.Equals(FileSystemProvider.ProviderName, StringComparison.OrdinalIgnoreCase)) + { + errorRecord = GetValidationError(WebCmdletStrings.NotFilesystemPath, "WebCmdletInFileNotFilesystemPathException", InFile); + } + else + { + if (providerPaths.Count > 1) + { + errorRecord = GetValidationError(WebCmdletStrings.MultiplePathsResolved, "WebCmdletInFileMultiplePathsResolvedException", InFile); + } + else if (providerPaths.Count == 0) + { + errorRecord = GetValidationError(WebCmdletStrings.NoPathResolved, "WebCmdletInFileNoPathResolvedException", InFile); + } + else + { + if (Directory.Exists(providerPaths[0])) + { + errorRecord = GetValidationError(WebCmdletStrings.DirectoryPathSpecified, "WebCmdletInFileNotFilePathException", InFile); + } + + _originalFilePath = InFile; + InFile = providerPaths[0]; + } + } + } + catch (ItemNotFoundException pathNotFound) + { + errorRecord = new ErrorRecord(pathNotFound.ErrorRecord, pathNotFound); + } + catch (ProviderNotFoundException providerNotFound) + { + errorRecord = new ErrorRecord(providerNotFound.ErrorRecord, providerNotFound); + } + catch (System.Management.Automation.DriveNotFoundException driveNotFound) + { + errorRecord = new ErrorRecord(driveNotFound.ErrorRecord, driveNotFound); + } + + if (errorRecord is not null) + { + ThrowTerminatingError(errorRecord); + } + } + + // Output ?? + if (PassThru.IsPresent && OutFile is null) + { + ErrorRecord error = GetValidationError(WebCmdletStrings.OutFileMissing, "WebCmdletOutFileMissingException", nameof(PassThru)); + ThrowTerminatingError(error); } - if (null != SessionVariable) + // Resume requires OutFile. + if (Resume.IsPresent && OutFile is null) { - // save the session back to the PS environment if requested + ErrorRecord error = GetValidationError(WebCmdletStrings.OutFileMissing, "WebCmdletOutFileMissingException", nameof(Resume)); + ThrowTerminatingError(error); + } + + _qualifiedOutFile = ShouldSaveToOutFile ? QualifiedOutFile : null; + + // OutFile must not be a directory to use Resume. + if (Resume.IsPresent && Directory.Exists(_qualifiedOutFile)) + { + ErrorRecord error = GetValidationError(WebCmdletStrings.ResumeNotFilePath, "WebCmdletResumeNotFilePathException", _qualifiedOutFile); + ThrowTerminatingError(error); + } + } + + internal virtual void PrepareSession() + { + // Make sure we have a valid WebRequestSession object to work with + WebSession ??= new WebRequestSession(); + + if (SessionVariable is not null) + { + // Save the session back to the PS environment if requested PSVariableIntrinsics vi = SessionState.PSVariable; vi.Set(SessionVariable, WebSession); } - // - // handle credentials - // - if (null != Credential) + // Handle credentials + if (Credential is not null && Authentication == WebAuthenticationType.None) { - // get the relevant NetworkCredential + // Get the relevant NetworkCredential NetworkCredential netCred = Credential.GetNetworkCredential(); WebSession.Credentials = netCred; - // supplying a credential overrides the UseDefaultCredentials setting + // Supplying a credential overrides the UseDefaultCredentials setting WebSession.UseDefaultCredentials = false; } + else if ((Credential is not null || Token is not null) && Authentication != WebAuthenticationType.None) + { + ProcessAuthentication(); + } else if (UseDefaultCredentials) { WebSession.UseDefaultCredentials = true; } - - if (null != CertificateThumbprint) + if (CertificateThumbprint is not null) { - X509Store store = new X509Store(StoreName.My, StoreLocation.CurrentUser); + using X509Store store = new(StoreName.My, StoreLocation.CurrentUser); store.Open(OpenFlags.ReadOnly | OpenFlags.OpenExistingOnly); X509Certificate2Collection collection = (X509Certificate2Collection)store.Certificates; X509Certificate2Collection tbCollection = (X509Certificate2Collection)collection.Find(X509FindType.FindByThumbprint, CertificateThumbprint, false); if (tbCollection.Count == 0) { - CryptographicException ex = new CryptographicException(WebCmdletStrings.ThumbprintNotFound); - throw ex; + throw new CryptographicException(WebCmdletStrings.ThumbprintNotFound); } + foreach (X509Certificate2 tbCert in tbCollection) { X509Certificate certificate = (X509Certificate)tbCert; @@ -422,250 +957,1169 @@ internal virtual void PrepareSession() } } - if (null != Certificate) + if (Certificate is not null) { WebSession.AddCertificate(Certificate); } - // - // handle the user agent - // - if (null != UserAgent) + // Handle the user agent + if (UserAgent is not null) { - // store the UserAgent string + // Store the UserAgent string WebSession.UserAgent = UserAgent; } - if (null != Proxy) + // Proxy and NoProxy parameters are mutually exclusive. + // If NoProxy is provided, WebSession will turn off the proxy + // and if Proxy is provided NoProxy will be turned off. + if (NoProxy.IsPresent) { - WebProxy webProxy = new WebProxy(Proxy); - webProxy.BypassProxyOnLocal = false; - if (null != ProxyCredential) - { - webProxy.Credentials = ProxyCredential.GetNetworkCredential(); - } - else if (ProxyUseDefaultCredentials) + WebSession.NoProxy = true; + } + else + { + if (Proxy is not null) { - // If both ProxyCredential and ProxyUseDefaultCredentials are passed, - // UseDefaultCredentials will overwrite the supplied credentials. - webProxy.UseDefaultCredentials = true; + WebProxy webProxy = new(Proxy); + webProxy.BypassProxyOnLocal = false; + if (ProxyCredential is not null) + { + webProxy.Credentials = ProxyCredential.GetNetworkCredential(); + } + else + { + webProxy.UseDefaultCredentials = ProxyUseDefaultCredentials; + } + + // We don't want to update the WebSession unless the proxies are different + // as that will require us to create a new HttpClientHandler and lose connection + // persistence. + if (!webProxy.Equals(WebSession.Proxy)) + { + WebSession.Proxy = webProxy; + } } - WebSession.Proxy = webProxy; } - if (-1 < MaximumRedirection) + if (MyInvocation.BoundParameters.ContainsKey(nameof(SslProtocol))) + { + WebSession.SslProtocol = SslProtocol; + } + + if (MaximumRedirection > -1) { WebSession.MaximumRedirection = MaximumRedirection; } - // store the other supplied headers - if (null != Headers) + WebSession.UnixSocket = UnixSocket; + + WebSession.SkipCertificateCheck = SkipCertificateCheck.IsPresent; + + // Store the other supplied headers + if (Headers is not null) { foreach (string key in Headers.Keys) { - // add the header value (or overwrite it if already present) - WebSession.Headers[key] = Headers[key].ToString(); + object value = Headers[key]; + + // null is not valid value for header. + // We silently ignore header if value is null. + if (value is not null) + { + // Add the header value (or overwrite it if already present). + WebSession.Headers[key] = value.ToString(); + } } } - } - - #endregion Virtual Methods - #region Helper Properties + if (MaximumRetryCount > 0) + { + WebSession.MaximumRetryCount = MaximumRetryCount; - internal string QualifiedOutFile - { - get { return (QualifyFilePath(OutFile)); } - } + // Only set retry interval if retry count is set. + WebSession.RetryIntervalInSeconds = RetryIntervalSec; + } - internal bool ShouldSaveToOutFile - { - get { return (!string.IsNullOrEmpty(OutFile)); } + WebSession.ConnectionTimeout = ConvertTimeoutSecondsToTimeSpan(ConnectionTimeoutSeconds); } - internal bool ShouldWriteToPipeline + internal virtual HttpClient GetHttpClient(bool handleRedirect) { - get { return (!ShouldSaveToOutFile || PassThru); } - } + HttpClient client = WebSession.GetHttpClient(handleRedirect, out bool clientWasReset); - #endregion Helper Properties + if (clientWasReset) + { + WriteVerbose(WebCmdletStrings.WebSessionConnectionRecreated); + } - #region Helper Methods + return client; + } - /// - /// Verifies that Internet Explorer is available, and that its first-run - /// configuration is complete. - /// - /// True if we should try to access IE's COM object. Not - /// needed if an HtmlDocument will be created shortly. - protected bool VerifyInternetExplorerAvailable(bool checkComObject) + internal virtual HttpRequestMessage GetRequest(Uri uri) { - // TODO: Remove this code once the dependency on mshtml has been resolved. -#if CORECLR - return false; -#else - bool isInternetExplorerConfigurationComplete = false; - // Check for IE for both PS Full and PS Core on windows. - // The registry key DisableFirstRunCustomize can exits at one of the following path. - // IE uses the same descending order (as mentioned) to check for the presence of this key. - // If the value of DisableFirstRunCustomize key is set to greater than zero then Run first - // is disabled. - string[] disableFirstRunCustomizePaths = new string[] { - @"HKEY_LOCAL_MACHINE\Software\Policies\Microsoft\Internet Explorer\Main", - @"HKEY_CURRENT_USER\Software\Policies\Microsoft\Internet Explorer\Main", - @"HKEY_CURRENT_USER\Software\Microsoft\Internet Explorer\Main", - @"HKEY_LOCAL_MACHINE\Software\Microsoft\Internet Explorer\Main" }; + Uri requestUri = PrepareUri(uri); + HttpMethod httpMethod = string.IsNullOrEmpty(CustomMethod) ? GetHttpMethod(Method) : new HttpMethod(CustomMethod); - foreach (string currentRegPath in disableFirstRunCustomizePaths) + // Create the base WebRequest object + HttpRequestMessage request = new(httpMethod, requestUri); + + if (HttpVersion is not null) { - object val = Registry.GetValue(currentRegPath, "DisableFirstRunCustomize", string.Empty); - if (val != null && !string.Empty.Equals(val) && Convert.ToInt32(val, CultureInfo.InvariantCulture) > 0) - { - isInternetExplorerConfigurationComplete = true; - break; - } + request.Version = HttpVersion; } - if (!isInternetExplorerConfigurationComplete) + // Pull in session data + if (WebSession.Headers.Count > 0) { - // Verify that if IE is installed, it has been through the RunOnce check. - // Otherwise, the call will hang waiting for users to go through First Run - // personalization. - using (RegistryKey key = Registry.CurrentUser.OpenSubKey("Software\\Microsoft\\Internet Explorer\\Main")) + WebSession.ContentHeaders.Clear(); + foreach (var entry in WebSession.Headers) { - if (key != null) + if (HttpKnownHeaderNames.ContentHeaders.Contains(entry.Key)) + { + WebSession.ContentHeaders.Add(entry.Key, entry.Value); + } + else { - foreach (string setting in key.GetValueNames()) + if (SkipHeaderValidation) { - if (setting.IndexOf("RunOnce", StringComparison.OrdinalIgnoreCase) > -1) - { - isInternetExplorerConfigurationComplete = true; - break; - } + request.Headers.TryAddWithoutValidation(entry.Key, entry.Value); + } + else + { + request.Headers.Add(entry.Key, entry.Value); } } } } - if (isInternetExplorerConfigurationComplete && checkComObject) + // Set 'Transfer-Encoding: chunked' if 'Transfer-Encoding' is specified + if (WebSession.Headers.ContainsKey(HttpKnownHeaderNames.TransferEncoding)) { - try + request.Headers.TransferEncodingChunked = true; + } + + // Set 'User-Agent' if WebSession.Headers doesn't already contain it + if (WebSession.Headers.TryGetValue(HttpKnownHeaderNames.UserAgent, out string userAgent)) + { + WebSession.UserAgent = userAgent; + } + else + { + if (SkipHeaderValidation) { - IHTMLDocument2 ieCheck = (IHTMLDocument2)new HTMLDocument(); - System.Runtime.InteropServices.Marshal.ReleaseComObject(ieCheck); + request.Headers.TryAddWithoutValidation(HttpKnownHeaderNames.UserAgent, WebSession.UserAgent); } - catch (System.Runtime.InteropServices.COMException) + else { - isInternetExplorerConfigurationComplete = false; + request.Headers.Add(HttpKnownHeaderNames.UserAgent, WebSession.UserAgent); } } - // Throw exception in PS Full only - if (!isInternetExplorerConfigurationComplete) - throw new NotSupportedException(WebCmdletStrings.IEDomNotSupported); - return isInternetExplorerConfigurationComplete; -#endif - } + // Set 'Keep-Alive' to false. This means set the Connection to 'Close'. + if (DisableKeepAlive) + { + request.Headers.Add(HttpKnownHeaderNames.Connection, "Close"); + } - private Uri PrepareUri(Uri uri) - { - uri = CheckProtocol(uri); + // Set 'Transfer-Encoding' + if (TransferEncoding is not null) + { + request.Headers.TransferEncodingChunked = true; + TransferCodingHeaderValue headerValue = new(TransferEncoding); + if (!request.Headers.TransferEncoding.Contains(headerValue)) + { + request.Headers.TransferEncoding.Add(headerValue); + } + } - // before creating the web request, - // preprocess Body if content is a dictionary and method is GET (set as query) - IDictionary bodyAsDictionary; - LanguagePrimitives.TryConvertTo(Body, out bodyAsDictionary); - if ((null != bodyAsDictionary) - && ((IsStandardMethodSet() && (Method == WebRequestMethod.Default || Method == WebRequestMethod.Get)) - || (IsCustomMethodSet() && CustomMethod.ToUpperInvariant() == "GET"))) + // If the file to resume downloading exists, create the Range request header using the file size. + // If not, create a Range to request the entire file. + if (Resume.IsPresent) { - UriBuilder uriBuilder = new UriBuilder(uri); - if (uriBuilder.Query != null && uriBuilder.Query.Length > 1) + FileInfo fileInfo = new(QualifiedOutFile); + + if (fileInfo.Exists) { - uriBuilder.Query = uriBuilder.Query.Substring(1) + "&" + FormatDictionary(bodyAsDictionary); + request.Headers.Range = new RangeHeaderValue(fileInfo.Length, null); + _resumeFileSize = fileInfo.Length; } else { - uriBuilder.Query = FormatDictionary(bodyAsDictionary); + request.Headers.Range = new RangeHeaderValue(0, null); } - uri = uriBuilder.Uri; - // set body to null to prevent later FillRequestStream - Body = null; } - return uri; + return request; } - private Uri CheckProtocol(Uri uri) + internal virtual void FillRequestStream(HttpRequestMessage request) { - if (null == uri) { throw new ArgumentNullException("uri"); } + ArgumentNullException.ThrowIfNull(request); - if (!uri.IsAbsoluteUri) + // Set the request content type + if (ContentType is not null) { - uri = new Uri("http://" + uri.OriginalString); + WebSession.ContentHeaders[HttpKnownHeaderNames.ContentType] = ContentType; + } + else if (request.Method == HttpMethod.Post) + { + // Win8:545310 Invoke-WebRequest does not properly set MIME type for POST + WebSession.ContentHeaders.TryGetValue(HttpKnownHeaderNames.ContentType, out string contentType); + if (string.IsNullOrEmpty(contentType)) + { + WebSession.ContentHeaders[HttpKnownHeaderNames.ContentType] = "application/x-www-form-urlencoded"; + } } - return (uri); - } - private string QualifyFilePath(string path) - { - string resolvedFilePath = PathUtils.ResolveFilePath(path, this, false); - return resolvedFilePath; - } + if (Form is not null) + { + MultipartFormDataContent formData = new(); + foreach (DictionaryEntry formEntry in Form) + { + // AddMultipartContent will handle PSObject unwrapping, Object type determination and enumerateing top level IEnumerables. + AddMultipartContent(fieldName: formEntry.Key, fieldValue: formEntry.Value, formData: formData, enumerate: true); + } - private string FormatDictionary(IDictionary content) - { - if (content == null) - throw new ArgumentNullException("content"); + SetRequestContent(request, formData); + } + else if (Body is not null) + { + // Coerce body into a usable form + // Make sure we're using the base object of the body, not the PSObject wrapper + object content = Body is PSObject psBody ? psBody.BaseObject : Body; - StringBuilder bodyBuilder = new StringBuilder(); - foreach (string key in content.Keys) + switch (content) + { + case FormObject form: + SetRequestContent(request, form.Fields); + break; + case IDictionary dictionary when request.Method != HttpMethod.Get: + SetRequestContent(request, dictionary); + break; + case XmlNode xmlNode: + SetRequestContent(request, xmlNode); + break; + case Stream stream: + SetRequestContent(request, stream); + break; + case byte[] bytes: + SetRequestContent(request, bytes); + break; + case MultipartFormDataContent multipartFormDataContent: + SetRequestContent(request, multipartFormDataContent); + break; + default: + SetRequestContent(request, (string)LanguagePrimitives.ConvertTo(content, typeof(string), CultureInfo.InvariantCulture)); + break; + } + } + else if (InFile is not null) { - if (0 < bodyBuilder.Length) + // Copy InFile data + try { - bodyBuilder.Append("&"); + // Open the input file + SetRequestContent(request, new FileStream(InFile, FileMode.Open, FileAccess.Read, FileShare.Read)); } + catch (UnauthorizedAccessException) + { + string msg = string.Format(CultureInfo.InvariantCulture, WebCmdletStrings.AccessDenied, _originalFilePath); - object value = content[key]; + throw new UnauthorizedAccessException(msg); + } + } - // URLEncode the key and value - string encodedKey = WebUtility.UrlEncode(key); - string encodedValue = String.Empty; - if (null != value) + // For other methods like Put where empty content has meaning, we need to fill in the content + if (request.Content is null) + { + // If this is a Get request and there is no content, then don't fill in the content as empty content gets rejected by some web services per RFC7230 + if (request.Method == HttpMethod.Get && ContentType is null) { - encodedValue = WebUtility.UrlEncode(value.ToString()); + return; } - bodyBuilder.AppendFormat("{0}={1}", encodedKey, encodedValue); + request.Content = new StringContent(string.Empty); + request.Content.Headers.Clear(); } - return bodyBuilder.ToString(); - } - private ErrorRecord GetValidationError(string msg, string errorId) - { - var ex = new ValidationMetadataException(msg); - var error = new ErrorRecord(ex, errorId, ErrorCategory.InvalidArgument, this); - return (error); + foreach (KeyValuePair entry in WebSession.ContentHeaders) + { + if (!string.IsNullOrWhiteSpace(entry.Value)) + { + if (SkipHeaderValidation) + { + request.Content.Headers.TryAddWithoutValidation(entry.Key, entry.Value); + } + else + { + try + { + request.Content.Headers.Add(entry.Key, entry.Value); + } + catch (FormatException ex) + { + ValidationMetadataException outerEx = new(WebCmdletStrings.ContentTypeException, ex); + ErrorRecord er = new(outerEx, "WebCmdletContentTypeException", ErrorCategory.InvalidArgument, ContentType); + ThrowTerminatingError(er); + } + } + } + } } - private ErrorRecord GetValidationError(string msg, string errorId, params object[] args) + internal virtual HttpResponseMessage GetResponse(HttpClient client, HttpRequestMessage request, bool handleRedirect) { - msg = string.Format(CultureInfo.InvariantCulture, msg, args); - var ex = new ValidationMetadataException(msg); - var error = new ErrorRecord(ex, errorId, ErrorCategory.InvalidArgument, this); - return (error); - } + ArgumentNullException.ThrowIfNull(client); + ArgumentNullException.ThrowIfNull(request); - private bool IsStandardMethodSet() - { - return (ParameterSetName == "StandardMethod"); - } - - private bool IsCustomMethodSet() - { - return (ParameterSetName == "CustomMethod"); - } + // Add 1 to account for the first request. + int totalRequests = WebSession.MaximumRetryCount + 1; + HttpRequestMessage currentRequest = request; + HttpResponseMessage response = null; - #endregion Helper Methods + do + { + // Track the current URI being used by various requests and re-requests. + Uri currentUri = currentRequest.RequestUri; + + _cancelToken = new CancellationTokenSource(); + try + { + if (IsWriteVerboseEnabled()) + { + WriteWebRequestVerboseInfo(currentRequest); + } + + if (IsWriteDebugEnabled()) + { + WriteWebRequestDebugInfo(currentRequest); + } + + // codeql[cs/ssrf] - This is expected Poweshell behavior where user inputted Uri is supported for the context of this method. The user assumes trust for the Uri and invocation is done on the user's machine, not a web application. If there is concern for remoting, they should use restricted remoting. + response = client.SendAsync(currentRequest, HttpCompletionOption.ResponseHeadersRead, _cancelToken.Token).GetAwaiter().GetResult(); + + if (IsWriteVerboseEnabled()) + { + WriteWebResponseVerboseInfo(response); + } + + if (IsWriteDebugEnabled()) + { + WriteWebResponseDebugInfo(response); + } + } + catch (TaskCanceledException ex) + { + if (ex.InnerException is TimeoutException) + { + // HTTP Request timed out + ErrorRecord er = new(ex, "ConnectionTimeoutReached", ErrorCategory.OperationTimeout, null); + ThrowTerminatingError(er); + } + else + { + throw; + } + + } + if (handleRedirect + && _maximumRedirection is not 0 + && IsRedirectCode(response.StatusCode) + && response.Headers.Location is not null) + { + _cancelToken.Cancel(); + _cancelToken = null; + + // If explicit count was provided, reduce it for this redirection. + if (_maximumRedirection > 0) + { + _maximumRedirection--; + } + + // For selected redirects, GET must be used with the redirected Location. + if (RequestRequiresForceGet(response.StatusCode, currentRequest.Method) && !PreserveHttpMethodOnRedirect) + { + Method = WebRequestMethod.Get; + CustomMethod = string.Empty; + } + + currentUri = new Uri(request.RequestUri, response.Headers.Location); + + // Continue to handle redirection + using HttpRequestMessage redirectRequest = GetRequest(currentUri); + response.Dispose(); + response = GetResponse(client, redirectRequest, handleRedirect); + } + + // Request again without the Range header because the server indicated the range was not satisfiable. + // This happens when the local file is larger than the remote file. + // If the size of the remote file is the same as the local file, there is nothing to resume. + if (Resume.IsPresent + && response.StatusCode == HttpStatusCode.RequestedRangeNotSatisfiable + && (response.Content.Headers.ContentRange.HasLength + && response.Content.Headers.ContentRange.Length != _resumeFileSize)) + { + _cancelToken.Cancel(); + + WriteVerbose(WebCmdletStrings.WebMethodResumeFailedVerboseMsg); + + // Disable the Resume switch so the subsequent calls to GetResponse() and FillRequestStream() + // are treated as a standard -OutFile request. This also disables appending local file. + Resume = new SwitchParameter(false); + + using (HttpRequestMessage requestWithoutRange = GetRequest(currentUri)) + { + FillRequestStream(requestWithoutRange); + + response.Dispose(); + response = GetResponse(client, requestWithoutRange, handleRedirect); + } + } + + _resumeSuccess = response.StatusCode == HttpStatusCode.PartialContent; + + // When MaximumRetryCount is not specified, the totalRequests is 1. + if (totalRequests > 1 && ShouldRetry(response.StatusCode)) + { + int retryIntervalInSeconds = WebSession.RetryIntervalInSeconds; + + // If the status code is 429 get the retry interval from the Headers. + // Ignore broken header and its value. + if (response.StatusCode is HttpStatusCode.TooManyRequests && response.Headers.TryGetValues(HttpKnownHeaderNames.RetryAfter, out IEnumerable retryAfter)) + { + try + { + IEnumerator enumerator = retryAfter.GetEnumerator(); + if (enumerator.MoveNext()) + { + retryIntervalInSeconds = Convert.ToInt32(enumerator.Current); + } + } + catch + { + // Ignore broken header. + } + } + + string retryMessage = string.Format( + CultureInfo.CurrentCulture, + WebCmdletStrings.RetryVerboseMsg, + retryIntervalInSeconds, + response.StatusCode); + + WriteVerbose(retryMessage); + + _cancelToken = new CancellationTokenSource(); + Task.Delay(retryIntervalInSeconds * 1000, _cancelToken.Token).GetAwaiter().GetResult(); + _cancelToken.Cancel(); + _cancelToken = null; + + currentRequest.Dispose(); + currentRequest = GetRequest(currentUri); + FillRequestStream(currentRequest); + } + + totalRequests--; + } + while (totalRequests > 0 && !response.IsSuccessStatusCode); + + return response; + } + + internal virtual void UpdateSession(HttpResponseMessage response) + { + ArgumentNullException.ThrowIfNull(response); + } + #endregion Virtual Methods + + #region Helper Methods +#nullable enable + internal static TimeSpan ConvertTimeoutSecondsToTimeSpan(int timeout) => timeout > 0 ? TimeSpan.FromSeconds(timeout) : Timeout.InfiniteTimeSpan; + + private void WriteWebRequestVerboseInfo(HttpRequestMessage request) + { + try + { + // Typical Basic Example: 'WebRequest: v1.1 POST https://httpstat.us/200 with query length 6' + StringBuilder verboseBuilder = new(128); + + // "Redact" the query string from verbose output, the details will be visible in Debug output + string uriWithoutQuery = request.RequestUri?.GetLeftPart(UriPartial.Path) ?? string.Empty; + verboseBuilder.Append($"WebRequest: v{request.Version} {request.Method} {uriWithoutQuery}"); + if (request.RequestUri?.Query is not null && request.RequestUri.Query.Length > 1) + { + verboseBuilder.Append($" with query length {request.RequestUri.Query.Length - 1}"); + } + + string? requestContentType = ContentHelper.GetContentType(request); + if (requestContentType is not null) + { + verboseBuilder.Append($" with {requestContentType} payload"); + } + + long? requestContentLength = request.Content?.Headers?.ContentLength; + if (requestContentLength is not null) + { + verboseBuilder.Append($" with body size {ContentHelper.GetFriendlyContentLength(requestContentLength)}"); + } + if (OutFile is not null) + { + verboseBuilder.Append($" output to {QualifyFilePath(OutFile)}"); + } + + WriteVerbose(verboseBuilder.ToString().Trim()); + } + catch (Exception ex) + { + // Just in case there are any edge cases we missed, we don't break workflows with an exception + WriteVerbose($"Failed to Write WebRequest Verbose Info: {ex} {ex.StackTrace}"); + } + } + + private void WriteWebRequestDebugInfo(HttpRequestMessage request) + { + try + { + // Typical basic example: + // WebRequest Detail + // ---QUERY + // test = 5 + // --- HEADERS + // User - Agent: Mozilla / 5.0, (Linux;Ubuntu 24.04.2 LTS;en - US), PowerShell / 7.6.0 + StringBuilder debugBuilder = new("WebRequest Detail" + Environment.NewLine, 512); + + if (!string.IsNullOrEmpty(request.RequestUri?.Query)) + { + debugBuilder.Append(DebugHeaderPrefix).AppendLine("QUERY"); + string[] queryParams = request.RequestUri.Query.TrimStart('?').Split('&'); + debugBuilder + .AppendJoin(Environment.NewLine, queryParams) + .AppendLine() + .AppendLine(); + } + + debugBuilder.Append(DebugHeaderPrefix).AppendLine("HEADERS"); + + foreach (var headerSet in new HttpHeaders?[] { request.Headers, request.Content?.Headers }) + { + if (headerSet is null) + { + continue; + } + + debugBuilder.AppendLine(headerSet.ToString()); + } + + if (request.Content is not null) + { + debugBuilder + .Append(DebugHeaderPrefix).AppendLine("BODY") + .AppendLine(request.Content switch + { + StringContent stringContent => stringContent + .ReadAsStringAsync(_cancelToken.Token) + .GetAwaiter().GetResult(), + MultipartFormDataContent multipartContent => "=> Multipart Form Content" + + Environment.NewLine + + multipartContent.ReadAsStringAsync(_cancelToken.Token) + .GetAwaiter().GetResult(), + ByteArrayContent byteContent => InFile is not null + ? "[Binary content: " + + ContentHelper.GetFriendlyContentLength(byteContent.Headers.ContentLength) + + "]" + : byteContent.ReadAsStringAsync(_cancelToken.Token).GetAwaiter().GetResult(), + StreamContent streamContent => + "[Stream content: " + ContentHelper.GetFriendlyContentLength(streamContent.Headers.ContentLength) + "]", + _ => "[Unknown content type]", + }) + .AppendLine(); + } + + WriteDebug(debugBuilder.ToString().Trim()); + } + catch (Exception ex) + { + // Just in case there are any edge cases we missed, we don't break workflows with an exception + WriteVerbose($"Failed to Write WebRequest Debug Info: {ex} {ex.StackTrace}"); + } + } + + private void WriteWebResponseVerboseInfo(HttpResponseMessage response) + { + try + { + // Typical basic example: WebResponse: 200 OK with text/plain payload body size 6 B (6 bytes) + StringBuilder verboseBuilder = new(128); + verboseBuilder.Append($"WebResponse: {(int)response.StatusCode} {response.ReasonPhrase ?? response.StatusCode.ToString()}"); + + string? responseContentType = ContentHelper.GetContentType(response); + if (responseContentType is not null) + { + verboseBuilder.Append($" with {responseContentType} payload"); + } + + long? responseContentLength = response.Content?.Headers?.ContentLength; + if (responseContentLength is not null) + { + verboseBuilder.Append($" with body size {ContentHelper.GetFriendlyContentLength(responseContentLength)}"); + } + + WriteVerbose(verboseBuilder.ToString().Trim()); + } + catch (Exception ex) + { + // Just in case there are any edge cases we missed, we don't break workflows with an exception + WriteVerbose($"Failed to Write WebResponse Verbose Info: {ex} {ex.StackTrace}"); + } + } + + private void WriteWebResponseDebugInfo(HttpResponseMessage response) + { + try + { + // Typical basic example + // WebResponse Detail + // --- HEADERS + // Date: Fri, 09 May 2025 18:06:44 GMT + // Server: Kestrel + // Set-Cookie: ARRAffinity=ee0b467f95b53d8dcfe48aeeb4173f93cf819be6e4721f434341647f4695039d;Path=/;HttpOnly;Secure;Domain=httpstat.us, ARRAffinitySameSite=ee0b467f95b53d8dcfe48aeeb4173f93cf819be6e4721f434341647f4695039d;Path=/;HttpOnly;SameSite=None;Secure;Domain=httpstat.us + // Strict-Transport-Security: max-age=2592000 + // Request-Context: appId=cid-v1:3548b0f5-7f75-492f-82bb-b6eb0e864e53 + // Content-Length: 6 + // Content-Type: text/plain + // --- BODY + // 200 OK + StringBuilder debugBuilder = new("WebResponse Detail" + Environment.NewLine, 512); + + debugBuilder.Append(DebugHeaderPrefix).AppendLine("HEADERS"); + + foreach (var headerSet in new HttpHeaders?[] { response.Headers, response.Content?.Headers }) + { + if (headerSet is null) + { + continue; + } + + debugBuilder.AppendLine(headerSet.ToString()); + } + + if (response.Content is not null) + { + debugBuilder.Append(DebugHeaderPrefix).AppendLine("BODY"); + + if (ContentHelper.IsTextBasedContentType(ContentHelper.GetContentType(response))) + { + debugBuilder.AppendLine( + response.Content.ReadAsStringAsync(_cancelToken.Token) + .GetAwaiter().GetResult()); + } + else + { + string friendlyContentLength = ContentHelper.GetFriendlyContentLength( + response.Content?.Headers?.ContentLength); + debugBuilder.AppendLine($"[Binary content: {friendlyContentLength}]"); + } + } + + WriteDebug(debugBuilder.ToString().Trim()); + } + catch (Exception ex) + { + // Just in case there are any edge cases we missed, we don't break workflows with an exception + WriteVerbose($"Failed to Write WebResponse Debug Info: {ex} {ex.StackTrace}"); + } + } + + private Uri PrepareUri(Uri uri) + { + uri = CheckProtocol(uri); + + // Before creating the web request, + // preprocess Body if content is a dictionary and method is GET (set as query) + LanguagePrimitives.TryConvertTo(Body, out IDictionary bodyAsDictionary); + if (bodyAsDictionary is not null && (Method == WebRequestMethod.Default || Method == WebRequestMethod.Get || CustomMethod == "GET")) + { + UriBuilder uriBuilder = new(uri); + if (uriBuilder.Query is not null && uriBuilder.Query.Length > 1) + { + uriBuilder.Query = string.Concat(uriBuilder.Query.AsSpan(1), "&", FormatDictionary(bodyAsDictionary)); + } + else + { + uriBuilder.Query = FormatDictionary(bodyAsDictionary); + } + + uri = uriBuilder.Uri; + + // Set body to null to prevent later FillRequestStream + Body = null; + } + + return uri; + } + + private static Uri CheckProtocol(Uri uri) + { + ArgumentNullException.ThrowIfNull(uri); + + return uri.IsAbsoluteUri ? uri : new Uri("http://" + uri.OriginalString); + } +#nullable restore + + private string QualifyFilePath(string path) => PathUtils.ResolveFilePath(filePath: path, command: this, isLiteralPath: true); + + private static string FormatDictionary(IDictionary content) + { + ArgumentNullException.ThrowIfNull(content); + + StringBuilder bodyBuilder = new(); + foreach (string key in content.Keys) + { + if (bodyBuilder.Length > 0) + { + bodyBuilder.Append('&'); + } + + object value = content[key]; + + // URLEncode the key and value + string encodedKey = WebUtility.UrlEncode(key); + string encodedValue = value is null ? string.Empty : WebUtility.UrlEncode(value.ToString()); + + bodyBuilder.Append($"{encodedKey}={encodedValue}"); + } + + return bodyBuilder.ToString(); + } + + private ErrorRecord GetValidationError(string msg, string errorId) + { + ValidationMetadataException ex = new(msg); + return new ErrorRecord(ex, errorId, ErrorCategory.InvalidArgument, this); + } + + private ErrorRecord GetValidationError(string msg, string errorId, params object[] args) + { + msg = string.Format(CultureInfo.InvariantCulture, msg, args); + ValidationMetadataException ex = new(msg); + return new ErrorRecord(ex, errorId, ErrorCategory.InvalidArgument, this); + } + + private string GetBasicAuthorizationHeader() + { + string password = new NetworkCredential(string.Empty, Credential.Password).Password; + string unencoded = string.Create(CultureInfo.InvariantCulture, $"{Credential.UserName}:{password}"); + byte[] bytes = Encoding.UTF8.GetBytes(unencoded); + return string.Create(CultureInfo.InvariantCulture, $"Basic {Convert.ToBase64String(bytes)}"); + } + + private string GetBearerAuthorizationHeader() + { + return string.Create(CultureInfo.InvariantCulture, $"Bearer {new NetworkCredential(string.Empty, Token).Password}"); + } + + private void ProcessAuthentication() + { + if (Authentication == WebAuthenticationType.Basic) + { + WebSession.Headers["Authorization"] = GetBasicAuthorizationHeader(); + } + else if (Authentication == WebAuthenticationType.Bearer || Authentication == WebAuthenticationType.OAuth) + { + WebSession.Headers["Authorization"] = GetBearerAuthorizationHeader(); + } + else + { + Diagnostics.Assert(false, string.Create(CultureInfo.InvariantCulture, $"Unrecognized Authentication value: {Authentication}")); + } + } + + private bool IsPersistentSession() => MyInvocation.BoundParameters.ContainsKey(nameof(WebSession)) || MyInvocation.BoundParameters.ContainsKey(nameof(SessionVariable)); + + /// + /// Sets the ContentLength property of the request and writes the specified content to the request's RequestStream. + /// + /// The WebRequest who's content is to be set. + /// A byte array containing the content data. + /// + /// Because this function sets the request's ContentLength property and writes content data into the request's stream, + /// it should be called one time maximum on a given request. + /// + internal void SetRequestContent(HttpRequestMessage request, byte[] content) + { + ArgumentNullException.ThrowIfNull(request); + ArgumentNullException.ThrowIfNull(content); + + request.Content = new ByteArrayContent(content); + } + + /// + /// Sets the ContentLength property of the request and writes the specified content to the request's RequestStream. + /// + /// The WebRequest who's content is to be set. + /// A String object containing the content data. + /// + /// Because this function sets the request's ContentLength property and writes content data into the request's stream, + /// it should be called one time maximum on a given request. + /// + internal void SetRequestContent(HttpRequestMessage request, string content) + { + ArgumentNullException.ThrowIfNull(request); + ArgumentNullException.ThrowIfNull(content); + + Encoding encoding = null; + + if (WebSession.ContentHeaders.TryGetValue(HttpKnownHeaderNames.ContentType, out string contentType) && contentType is not null) + { + // If Content-Type contains the encoding format (as CharSet), use this encoding format + // to encode the Body of the WebRequest sent to the server. Default Encoding format + // would be used if Charset is not supplied in the Content-Type property. + try + { + MediaTypeHeaderValue mediaTypeHeaderValue = MediaTypeHeaderValue.Parse(contentType); + if (!string.IsNullOrEmpty(mediaTypeHeaderValue.CharSet)) + { + encoding = Encoding.GetEncoding(mediaTypeHeaderValue.CharSet); + } + } + catch (Exception ex) when (ex is FormatException || ex is ArgumentException) + { + if (!SkipHeaderValidation) + { + ValidationMetadataException outerEx = new(WebCmdletStrings.ContentTypeException, ex); + ErrorRecord er = new(outerEx, "WebCmdletContentTypeException", ErrorCategory.InvalidArgument, contentType); + ThrowTerminatingError(er); + } + } + } + + byte[] bytes = StreamHelper.EncodeToBytes(content, encoding); + request.Content = new ByteArrayContent(bytes); + } + + internal void SetRequestContent(HttpRequestMessage request, XmlNode xmlNode) + { + ArgumentNullException.ThrowIfNull(request); + ArgumentNullException.ThrowIfNull(xmlNode); + + byte[] bytes = null; + XmlDocument doc = xmlNode as XmlDocument; + if (doc?.FirstChild is XmlDeclaration decl && !string.IsNullOrEmpty(decl.Encoding)) + { + Encoding encoding = Encoding.GetEncoding(decl.Encoding); + bytes = StreamHelper.EncodeToBytes(doc.OuterXml, encoding); + } + else + { + bytes = StreamHelper.EncodeToBytes(xmlNode.OuterXml, encoding: null); + } + + request.Content = new ByteArrayContent(bytes); + } + + /// + /// Sets the ContentLength property of the request and writes the specified content to the request's RequestStream. + /// + /// The WebRequest who's content is to be set. + /// A Stream object containing the content data. + /// + /// Because this function sets the request's ContentLength property and writes content data into the request's stream, + /// it should be called one time maximum on a given request. + /// + internal void SetRequestContent(HttpRequestMessage request, Stream contentStream) + { + ArgumentNullException.ThrowIfNull(request); + ArgumentNullException.ThrowIfNull(contentStream); + + request.Content = new StreamContent(contentStream); + } + + /// + /// Sets the ContentLength property of the request and writes the ContentLength property of the request and writes the specified content to the request's RequestStream. + /// + /// The WebRequest who's content is to be set. + /// A MultipartFormDataContent object containing multipart/form-data content. + /// + /// Because this function sets the request's ContentLength property and writes content data into the request's stream, + /// it should be called one time maximum on a given request. + /// + internal void SetRequestContent(HttpRequestMessage request, MultipartFormDataContent multipartContent) + { + ArgumentNullException.ThrowIfNull(request); + ArgumentNullException.ThrowIfNull(multipartContent); + + // Content headers will be set by MultipartFormDataContent which will throw unless we clear them first + WebSession.ContentHeaders.Clear(); + + request.Content = multipartContent; + } + + internal void SetRequestContent(HttpRequestMessage request, IDictionary content) + { + ArgumentNullException.ThrowIfNull(request); + ArgumentNullException.ThrowIfNull(content); + + string body = FormatDictionary(content); + SetRequestContent(request, body); + } + + internal void ParseLinkHeader(HttpResponseMessage response) + { + Uri requestUri = response.RequestMessage.RequestUri; + if (_relationLink is null) + { + // Must ignore the case of relation links. See RFC 8288 (https://tools.ietf.org/html/rfc8288) + _relationLink = new Dictionary(StringComparer.OrdinalIgnoreCase); + } + else + { + _relationLink.Clear(); + } + + // We only support the URL in angle brackets and `rel`, other attributes are ignored + // user can still parse it themselves via the Headers property + const string Pattern = "<(?.*?)>;\\s*rel=(?\")?(?(?(quoted).*?|[^,;]*))(?(quoted)\")"; + if (response.Headers.TryGetValues("Link", out IEnumerable links)) + { + foreach (string linkHeader in links) + { + MatchCollection matchCollection = Regex.Matches(linkHeader, Pattern); + foreach (Match match in matchCollection) + { + if (match.Success) + { + string url = match.Groups["url"].Value; + string rel = match.Groups["rel"].Value; + if (url != string.Empty && rel != string.Empty && !_relationLink.ContainsKey(rel)) + { + Uri absoluteUri = new(requestUri, url); + _relationLink.Add(rel, absoluteUri.AbsoluteUri); + } + } + } + } + } + } + + /// + /// Adds content to a . Object type detection is used to determine if the value is string, File, or Collection. + /// + /// The Field Name to use. + /// The Field Value to use. + /// The to update. + /// If true, collection types in will be enumerated. If false, collections will be treated as single value. + private static void AddMultipartContent(object fieldName, object fieldValue, MultipartFormDataContent formData, bool enumerate) + { + ArgumentNullException.ThrowIfNull(formData); + + // It is possible that the dictionary keys or values are PSObject wrapped depending on how the dictionary is defined and assigned. + // Before processing the field name and value we need to ensure we are working with the base objects and not the PSObject wrappers. + + // Unwrap fieldName PSObjects + if (fieldName is PSObject namePSObject) + { + fieldName = namePSObject.BaseObject; + } + + // Unwrap fieldValue PSObjects + if (fieldValue is PSObject valuePSObject) + { + fieldValue = valuePSObject.BaseObject; + } + + // Treat a single FileInfo as a FileContent + if (fieldValue is FileInfo file) + { + formData.Add(GetMultipartFileContent(fieldName: fieldName, file: file)); + return; + } + + // Treat Strings and other single values as a StringContent. + // If enumeration is false, also treat IEnumerables as StringContents. + // String implements IEnumerable so the explicit check is required. + if (!enumerate || fieldValue is string || fieldValue is not IEnumerable) + { + formData.Add(GetMultipartStringContent(fieldName: fieldName, fieldValue: fieldValue)); + return; + } + + // Treat the value as a collection and enumerate it if enumeration is true + if (enumerate && fieldValue is IEnumerable items) + { + foreach (object item in items) + { + // Recurse, but do not enumerate the next level. IEnumerables will be treated as single values. + AddMultipartContent(fieldName: fieldName, fieldValue: item, formData: formData, enumerate: false); + } + } + } + + /// + /// Gets a from the supplied field name and field value. Uses to convert the objects to strings. + /// + /// The Field Name to use for the + /// The Field Value to use for the + private static StringContent GetMultipartStringContent(object fieldName, object fieldValue) + { + ContentDispositionHeaderValue contentDisposition = new("form-data"); + contentDisposition.Name = LanguagePrimitives.ConvertTo(fieldName); + + // codeql[cs/information-exposure-through-exception] - PowerShell is an on-premise product, meaning local users would already have access to the binaries and stack traces. Therefore, the information would not be exposed in the same way it would be for an ASP .NET service. + StringContent result = new(LanguagePrimitives.ConvertTo(fieldValue)); + result.Headers.ContentDisposition = contentDisposition; + + return result; + } + + /// + /// Gets a from the supplied field name and . Uses to convert the fieldname to a string. + /// + /// The Field Name to use for the + /// The to use for the + private static StreamContent GetMultipartStreamContent(object fieldName, Stream stream) + { + ContentDispositionHeaderValue contentDisposition = new("form-data"); + contentDisposition.Name = LanguagePrimitives.ConvertTo(fieldName); + + StreamContent result = new(stream); + result.Headers.ContentDisposition = contentDisposition; + result.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream"); + + return result; + } + + /// + /// Gets a from the supplied field name and file. Calls to create the and then sets the file name. + /// + /// The Field Name to use for the + /// The file to use for the + private static StreamContent GetMultipartFileContent(object fieldName, FileInfo file) + { + StreamContent result = GetMultipartStreamContent(fieldName: fieldName, stream: new FileStream(file.FullName, FileMode.Open, FileAccess.Read, FileShare.Read)); + + result.Headers.ContentDisposition.FileName = file.Name; + result.Headers.ContentDisposition.FileNameStar = file.Name; + + return result; + } + + private static string FormatErrorMessage(string error, string contentType) + { + string formattedError = null; + + try + { + if (ContentHelper.IsXml(contentType)) + { + XmlDocument doc = new(); + doc.LoadXml(error); + + XmlWriterSettings settings = new XmlWriterSettings + { + Indent = true, + NewLineOnAttributes = true, + OmitXmlDeclaration = true + }; + + if (doc.FirstChild is XmlDeclaration decl) + { + settings.Encoding = Encoding.GetEncoding(decl.Encoding); + } + + StringBuilder stringBuilder = new(); + using XmlWriter xmlWriter = XmlWriter.Create(stringBuilder, settings); + doc.Save(xmlWriter); + string xmlString = stringBuilder.ToString(); + + formattedError = Environment.NewLine + xmlString; + } + else if (ContentHelper.IsJson(contentType)) + { + JsonNode jsonNode = JsonNode.Parse(error); + JsonSerializerOptions options = new JsonSerializerOptions { WriteIndented = true }; + string jsonString = jsonNode.ToJsonString(options); + + formattedError = Environment.NewLine + jsonString; + } + } + catch + { + // Ignore errors + } + + if (string.IsNullOrEmpty(formattedError)) + { + // Remove HTML tags making it easier to read + formattedError = Regex.Replace(error, "<[^>]*>", string.Empty); + } + + return formattedError; + } + + // Returns true if the status code is one of the supported redirection codes. + private static bool IsRedirectCode(HttpStatusCode statusCode) => statusCode switch + { + HttpStatusCode.Found + or HttpStatusCode.Moved + or HttpStatusCode.MultipleChoices + or HttpStatusCode.PermanentRedirect + or HttpStatusCode.SeeOther + or HttpStatusCode.TemporaryRedirect => true, + _ => false + }; + + // Returns true if the status code is a redirection code and the action requires switching to GET on redirection. + // See https://learn.microsoft.com/en-us/dotnet/api/system.net.httpstatuscode + private static bool RequestRequiresForceGet(HttpStatusCode statusCode, HttpMethod requestMethod) => statusCode switch + { + HttpStatusCode.Found + or HttpStatusCode.Moved + or HttpStatusCode.MultipleChoices => requestMethod == HttpMethod.Post, + HttpStatusCode.SeeOther => requestMethod != HttpMethod.Get && requestMethod != HttpMethod.Head, + _ => false + }; + + // Returns true if the status code shows a server or client error and MaximumRetryCount > 0 + private static bool ShouldRetry(HttpStatusCode statusCode) => (int)statusCode switch + { + 304 or (>= 400 and <= 599) => true, + _ => false + }; + + private static HttpMethod GetHttpMethod(WebRequestMethod method) => method switch + { + WebRequestMethod.Default or WebRequestMethod.Get => HttpMethod.Get, + WebRequestMethod.Delete => HttpMethod.Delete, + WebRequestMethod.Head => HttpMethod.Head, + WebRequestMethod.Patch => HttpMethod.Patch, + WebRequestMethod.Post => HttpMethod.Post, + WebRequestMethod.Put => HttpMethod.Put, + WebRequestMethod.Options => HttpMethod.Options, + WebRequestMethod.Trace => HttpMethod.Trace, + _ => new HttpMethod(method.ToString().ToUpperInvariant()) + }; + + #endregion Helper Methods + } + + /// + /// Exception class for webcmdlets to enable returning HTTP error response. + /// + public sealed class HttpResponseException : HttpRequestException + { + /// + /// Initializes a new instance of the class. + /// + /// Message for the exception. + /// Response from the HTTP server. + public HttpResponseException(string message, HttpResponseMessage response) : base(message, inner: null, response.StatusCode) + { + Response = response; + } + + /// + /// HTTP error response. + /// + public HttpResponseMessage Response { get; } } } diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/Common/WebResponseObject.Common.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/Common/WebResponseObject.Common.cs index d01d2bc08e7..ace84f480f9 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/Common/WebResponseObject.Common.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/Common/WebResponseObject.Common.cs @@ -1,66 +1,119 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#nullable enable using System; +using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; using System.IO; +using System.Net.Http; +using System.Text; +using System.Threading; namespace Microsoft.PowerShell.Commands { /// - /// WebResponseObject + /// WebResponseObject. /// - public partial class WebResponseObject + public class WebResponseObject { #region Properties /// - /// gets or protected sets the Content property + /// Gets or sets the BaseResponse property. /// - [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] - public byte[] Content { get; protected set; } + public HttpResponseMessage BaseResponse { get; set; } /// - /// gets the StatusCode property + /// Gets or protected sets the response body content. /// - public int StatusCode - { - get { return (WebResponseHelper.GetStatusCode(BaseResponse)); } - } + public byte[]? Content { get; protected set; } /// - /// gets the StatusDescription property + /// Gets the Headers property. /// - public string StatusDescription - { - get { return (WebResponseHelper.GetStatusDescription(BaseResponse)); } - } + public Dictionary> Headers => _headers ??= WebResponseHelper.GetHeadersDictionary(BaseResponse); + + private Dictionary>? _headers; - private MemoryStream _rawContentStream; /// - /// gets the RawContentStream property + /// Gets or protected sets the full response content. /// - public MemoryStream RawContentStream - { - get { return (_rawContentStream); } - } + /// + /// Full response content, including the HTTP status line, headers, and body. + /// + public string? RawContent { get; protected set; } /// - /// gets the RawContentLength property + /// Gets the length (in bytes) of . /// - public long RawContentLength - { - get { return (null == RawContentStream ? -1 : RawContentStream.Length); } - } + public long RawContentLength => RawContentStream is null ? -1 : RawContentStream.Length; + + /// + /// Gets or protected sets the response body content as a . + /// + public MemoryStream RawContentStream { get; protected set; } + + /// + /// Gets the RelationLink property. + /// + public Dictionary? RelationLink { get; internal set; } + + /// + /// Gets the response status code. + /// + public int StatusCode => WebResponseHelper.GetStatusCode(BaseResponse); /// - /// gets or protected sets the RawContent property + /// Gets the response status description. /// - public string RawContent { get; protected set; } + public string StatusDescription => WebResponseHelper.GetStatusDescription(BaseResponse); + + /// + /// Gets or sets the output file path. + /// + public string? OutFile { get; internal set; } #endregion Properties + #region Protected Fields + + /// + /// Time permitted between reads or Timeout.InfiniteTimeSpan for no timeout. + /// + protected TimeSpan perReadTimeout; + + #endregion Protected Fields + + #region Constructors + + /// + /// Initializes a new instance of the class. + /// + /// The Http response. + /// Time permitted between reads or Timeout.InfiniteTimeSpan for no timeout. + /// The cancellation token. + public WebResponseObject(HttpResponseMessage response, TimeSpan perReadTimeout, CancellationToken cancellationToken) : this(response, null, perReadTimeout, cancellationToken) { } + + /// + /// Initializes a new instance of the class + /// with the specified . + /// + /// Http response. + /// The http content stream. + /// Time permitted between reads or Timeout.InfiniteTimeSpan for no timeout. + /// The cancellation token. + public WebResponseObject(HttpResponseMessage response, Stream? contentStream, TimeSpan perReadTimeout, CancellationToken cancellationToken) + { + this.perReadTimeout = perReadTimeout; + SetResponse(response, contentStream, cancellationToken); + InitializeContent(); + InitializeRawContent(response); + } + + #endregion Constructors + #region Methods /// @@ -68,12 +121,56 @@ public long RawContentLength /// private void InitializeContent() { - this.Content = this.RawContentStream.ToArray(); + Content = RawContentStream.ToArray(); } - private bool IsPrintable(char c) + private void InitializeRawContent(HttpResponseMessage baseResponse) { - return (Char.IsLetterOrDigit(c) || Char.IsPunctuation(c) || Char.IsSeparator(c) || Char.IsSymbol(c) || Char.IsWhiteSpace(c)); + StringBuilder raw = ContentHelper.GetRawContentHeader(baseResponse); + + // Use ASCII encoding for the RawContent visual view of the content. + if (Content?.Length > 0) + { + raw.Append(ToString()); + } + + RawContent = raw.ToString(); + } + + private static bool IsPrintable(char c) => char.IsLetterOrDigit(c) + || char.IsPunctuation(c) + || char.IsSeparator(c) + || char.IsSymbol(c) + || char.IsWhiteSpace(c); + + [MemberNotNull(nameof(RawContentStream))] + [MemberNotNull(nameof(BaseResponse))] + private void SetResponse(HttpResponseMessage response, Stream? contentStream, CancellationToken cancellationToken) + { + ArgumentNullException.ThrowIfNull(response); + + BaseResponse = response; + + if (contentStream is MemoryStream ms) + { + RawContentStream = ms; + } + else + { + Stream st = contentStream ?? StreamHelper.GetResponseStream(response, cancellationToken); + + long contentLength = response.Content.Headers.ContentLength.GetValueOrDefault(); + if (contentLength <= 0) + { + contentLength = StreamHelper.DefaultReadBuffer; + } + + int initialCapacity = (int)Math.Min(contentLength, StreamHelper.DefaultReadBuffer); + RawContentStream = new WebResponseContentMemoryStream(st, initialCapacity, cmdlet: null, response.Content.Headers.ContentLength.GetValueOrDefault(), perReadTimeout, cancellationToken); + } + + // Set the position of the content stream to the beginning + RawContentStream.Position = 0; } /// @@ -82,7 +179,12 @@ private bool IsPrintable(char c) /// The string representation of this web response. public sealed override string ToString() { - char[] stringContent = System.Text.Encoding.ASCII.GetChars(Content); + if (Content is null) + { + return string.Empty; + } + + char[] stringContent = Encoding.ASCII.GetChars(Content); for (int counter = 0; counter < stringContent.Length; counter++) { if (!IsPrintable(stringContent[counter])) diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/ConvertFromJsonCommand.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/ConvertFromJsonCommand.cs index 9ec8ef961c5..82e1277e00c 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/ConvertFromJsonCommand.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/ConvertFromJsonCommand.cs @@ -1,65 +1,66 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. using System; using System.Collections.Generic; using System.Management.Automation; -using System.Reflection; namespace Microsoft.PowerShell.Commands { /// - /// The ConvertFrom-Json command - /// This command convert a Json string representation to a JsonObject + /// The ConvertFrom-Json command. + /// This command converts a Json string representation to a JsonObject. /// - [Cmdlet(VerbsData.ConvertFrom, "Json", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=217031", RemotingCapability = RemotingCapability.None)] + [Cmdlet(VerbsData.ConvertFrom, "Json", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2096606", RemotingCapability = RemotingCapability.None)] [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] public class ConvertFromJsonCommand : Cmdlet { #region parameters /// - /// gets or sets the InputString property + /// Gets or sets the InputString property. /// [Parameter(Mandatory = true, Position = 0, ValueFromPipeline = true)] [AllowEmptyString] public string InputObject { get; set; } /// - /// inputObjectBuffer buffers all InputObjet contents available in the pipeline. + /// InputObjectBuffer buffers all InputObject contents available in the pipeline. /// - private List _inputObjectBuffer = new List(); + private readonly List _inputObjectBuffer = new(); - #endregion parameters + /// + /// Returned data structure is a Hashtable instead a CustomPSObject. + /// + [Parameter] + public SwitchParameter AsHashtable { get; set; } - #region overrides + /// + /// Gets or sets the maximum depth the JSON input is allowed to have. By default, it is 1024. + /// + [Parameter] + [ValidateRange(ValidateRangeKind.Positive)] + public int Depth { get; set; } = 1024; /// - /// Prerequisite checks + /// Gets or sets the switch to prevent ConvertFrom-Json from unravelling collections during deserialization, instead passing them as a single + /// object through the pipeline. /// - protected override void BeginProcessing() - { -#if CORECLR - JsonObject.ImportJsonDotNetModule(this); -#else - try - { - System.Reflection.Assembly.Load(new AssemblyName("System.Web.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35")); - } - catch (System.IO.FileNotFoundException) - { - ThrowTerminatingError(new ErrorRecord( - new NotSupportedException(WebCmdletStrings.ExtendedProfileRequired), - "ExtendedProfileRequired", - ErrorCategory.NotInstalled, - null)); - } -#endif - } + [Parameter] + public SwitchParameter NoEnumerate { get; set; } + + /// + /// Gets or sets the switch to control how DateTime values are to be parsed as a dotnet object. + /// + [Parameter] + public JsonDateKind DateKind { get; set; } = JsonDateKind.Default; + + #endregion parameters + + #region overrides /// - /// Buffers InputObjet contents available in the pipeline. + /// Buffers InputObjet contents available in the pipeline. /// protected override void ProcessRecord() { @@ -67,7 +68,7 @@ protected override void ProcessRecord() } /// - /// the main execution method for the convertfrom-json command + /// The main execution method for the ConvertFrom-Json command. /// protected override void EndProcessing() { @@ -91,7 +92,7 @@ protected override void EndProcessing() catch (ArgumentException) { // The first input string does not represent a complete Json Syntax. - // Hence consider the the entire input as a single Json content. + // Hence consider the entire input as a single Json content. } if (successfullyConverted) @@ -113,19 +114,19 @@ protected override void EndProcessing() /// /// ConvertFromJsonHelper is a helper method to convert to Json input to .Net Type. /// - /// Input String. + /// Input string. /// True if successfully converted, else returns false. private bool ConvertFromJsonHelper(string input) { ErrorRecord error = null; - object result = JsonObject.ConvertFromJson(input, out error); + object result = JsonObject.ConvertFromJson(input, AsHashtable.IsPresent, Depth, DateKind, out error); if (error != null) { ThrowTerminatingError(error); } - WriteObject(result); + WriteObject(result, !NoEnumerate.IsPresent); return (result != null); } diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/ConvertToJsonCommand.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/ConvertToJsonCommand.cs index 7feb75ce842..173d999b06d 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/ConvertToJsonCommand.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/ConvertToJsonCommand.cs @@ -1,59 +1,48 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. using System; -using System.Diagnostics.CodeAnalysis; using System.Collections.Generic; using System.Management.Automation; -using System.Collections; -using System.Reflection; -using System.Text; -using System.Globalization; -using Dbg = System.Management.Automation; using System.Management.Automation.Internal; -#if CORECLR -using Newtonsoft.Json; -using Newtonsoft.Json.Converters; -#else -using System.Collections.Specialized; -using System.Web.Script.Serialization; -#endif +using System.Threading; -// FxCop suppressions for resource strings: -[module: SuppressMessage("Microsoft.Naming", "CA1703:ResourceStringsShouldBeSpelledCorrectly", Scope = "resource", Target = "WebCmdletStrings.resources", MessageId = "json")] -[module: SuppressMessage("Microsoft.Naming", "CA1703:ResourceStringsShouldBeSpelledCorrectly", Scope = "resource", Target = "WebCmdletStrings.resources", MessageId = "Json")] +using Newtonsoft.Json; namespace Microsoft.PowerShell.Commands { /// - /// The ConvertTo-Json command - /// This command convert an object to a Json string representation + /// The ConvertTo-Json command. + /// This command converts an object to a Json string representation. /// - [Cmdlet(VerbsData.ConvertTo, "Json", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=217032", RemotingCapability = RemotingCapability.None)] - [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] - public class ConvertToJsonCommand : PSCmdlet + [Cmdlet(VerbsData.ConvertTo, "Json", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2096925", RemotingCapability = RemotingCapability.None)] + [OutputType(typeof(string))] + public class ConvertToJsonCommand : PSCmdlet, IDisposable { - #region parameters /// - /// gets or sets the InputObject property + /// Gets or sets the InputObject property. /// [Parameter(Position = 0, Mandatory = true, ValueFromPipeline = true)] [AllowNull] public object InputObject { get; set; } private int _depth = 2; - private const int maxDepthAllowed = 100; + + private readonly CancellationTokenSource _cancellationSource = new(); /// - /// gets or sets the Depth property + /// Gets or sets the Depth property. /// [Parameter] - [ValidateRange(1, int.MaxValue)] - public int Depth { get { return _depth; } set { _depth = value; } } + [ValidateRange(0, 100)] + public int Depth + { + get { return _depth; } + set { _depth = value; } + } /// - /// gets or sets the Compress property. + /// Gets or sets the Compress property. /// If the Compress property is set to be true, the Json string will /// be output in the compressed way. Otherwise, the Json string will /// be output with indentations. @@ -62,662 +51,97 @@ public class ConvertToJsonCommand : PSCmdlet public SwitchParameter Compress { get; set; } /// - /// gets or sets the EnumsAsStrings property. + /// Gets or sets the EnumsAsStrings property. /// If the EnumsAsStrings property is set to true, enum values will /// be converted to their string equivalent. Otherwise, enum values /// will be converted to their numeric equivalent. /// - [Parameter()] + [Parameter] public SwitchParameter EnumsAsStrings { get; set; } - #endregion parameters - - #region overrides - /// - /// Prerequisite checks + /// Gets or sets the AsArray property. + /// If the AsArray property is set to be true, the result JSON string will + /// be returned with surrounding '[', ']' chars. Otherwise, + /// the array symbols will occur only if there is more than one input object. /// - protected override void BeginProcessing() - { - if (_depth > maxDepthAllowed) - { - string errorMessage = StringUtil.Format(WebCmdletStrings.ReachedMaximumDepthAllowed, maxDepthAllowed); - ThrowTerminatingError(new ErrorRecord( - new InvalidOperationException(errorMessage), - "ReachedMaximumDepthAllowed", - ErrorCategory.InvalidOperation, - null)); - } -#if CORECLR - JsonObject.ImportJsonDotNetModule(this); -#else - try - { - System.Reflection.Assembly.Load(new AssemblyName("System.Web.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35")); - } - catch (System.IO.FileNotFoundException) - { - ThrowTerminatingError(new ErrorRecord( - new NotSupportedException(WebCmdletStrings.ExtendedProfileRequired), - "ExtendedProfileRequired", - ErrorCategory.NotInstalled, - null)); - } -#endif - } - - private List _inputObjects = new List(); - - /// - /// Caching the input objects for the convertto-json command - /// - protected override void ProcessRecord() - { - if (InputObject != null) - { - _inputObjects.Add(InputObject); - } - } - - /// - /// Do the conversion to json and write output - /// - protected override void EndProcessing() - { - if (_inputObjects.Count > 0) - { - object objectToProcess = (_inputObjects.Count > 1) ? (_inputObjects.ToArray() as object) : (_inputObjects[0]); - // Pre-process the object so that it serializes the same, except that properties whose - // values cannot be evaluated are treated as having the value null. - object preprocessedObject = ProcessValue(objectToProcess, 0); -#if CORECLR - JsonSerializerSettings jsonSettings = new JsonSerializerSettings { TypeNameHandling = TypeNameHandling.None, MaxDepth = 1024 }; - if (EnumsAsStrings) - { - jsonSettings.Converters.Add(new StringEnumConverter()); - } - if (!Compress) - { - jsonSettings.Formatting = Formatting.Indented; - } - string output = JsonConvert.SerializeObject(preprocessedObject, jsonSettings); - WriteObject(output); -#else - // In Full CLR, we use the JavaScriptSerializer for which RecursionLimit was set to the default value of 100 (the actual recursion limit is 99 since - // at 100 the exception is thrown). See https://msdn.microsoft.com/en-us/library/system.web.script.serialization.javascriptserializer.recursionlimit(v=vs.110).aspx - // ProcessValue creates an object to be serialized from 1 to depth. However, the properties of the object at 'depth' should also be serialized, - // and from the perspective of the serializer, this means it needs to support serializing depth + 1. For the JavaScriptSerializer to support this, - // RecursionLimit needs to be set to depth + 2. - JavaScriptSerializer helper = new JavaScriptSerializer() { RecursionLimit = (maxDepthAllowed + 2) }; - helper.MaxJsonLength = Int32.MaxValue; - string output = helper.Serialize(preprocessedObject); - WriteObject(Compress ? output : ConvertToPrettyJsonString(output)); -#endif - } - } - - #endregion overrides - - #region convertOutputToPrettierFormat - - /// - /// Convert the Json string to a more readable format - /// - /// - /// - private string ConvertToPrettyJsonString(string json) - { - if (!json.StartsWith("{", StringComparison.OrdinalIgnoreCase) && !json.StartsWith("[", StringComparison.OrdinalIgnoreCase)) - { - return json; - } - - StringBuilder retStr = new StringBuilder(); - if (json.StartsWith("{", StringComparison.OrdinalIgnoreCase)) - { - retStr.Append('{'); - ConvertDictionary(json, 1, retStr, "", 0); - } - else if (json.StartsWith("[", StringComparison.OrdinalIgnoreCase)) - { - retStr.Append('['); - ConvertList(json, 1, retStr, "", 0); - } - - return retStr.ToString(); - } - - /// - /// Convert a Json List, which starts with '['. - /// - /// - /// - /// - /// - /// - /// - private int ConvertList(string json, int index, StringBuilder result, string padString, int numberOfSpaces) - { - result.Append("\r\n"); - StringBuilder newPadString = new StringBuilder(); - newPadString.Append(padString); - AddSpaces(numberOfSpaces, newPadString); - AddIndentations(1, newPadString); - - bool headChar = true; - - for (int i = index; i < json.Length; i++) - { - switch (json[i]) - { - case '{': - result.Append(newPadString.ToString()); - result.Append(json[i]); - i = ConvertDictionary(json, i + 1, result, newPadString.ToString(), 0); - headChar = false; - break; - case '[': - result.Append(newPadString.ToString()); - result.Append(json[i]); - i = ConvertList(json, i + 1, result, newPadString.ToString(), 0); - headChar = false; - break; - case ']': - result.Append("\r\n"); - result.Append(padString); - AddSpaces(numberOfSpaces, result); - result.Append(json[i]); - return i; - case '"': - if (headChar) - { - result.Append(newPadString.ToString()); - } - result.Append(json[i]); - i = ConvertQuotedString(json, i + 1, result); - headChar = false; - break; - case ',': - result.Append(json[i]); - result.Append("\r\n"); - headChar = true; - break; - default: - if (headChar) - { - result.Append(newPadString.ToString()); - } - result.Append(json[i]); - headChar = false; - break; - } - } - - Dbg.Diagnostics.Assert(false, "ConvertDictionary should return when encounter '}'"); - ThrowTerminatingError(NewError()); - return -1; - } - - /// - /// Convert the quoted string. - /// - /// - /// - /// - /// - private int ConvertQuotedString(string json, int index, StringBuilder result) - { - for (int i = index; i < json.Length; i++) - { - result.Append(json[i]); - if (json[i] == '"') - { - // Ensure that the quote is not escaped by iteratively searching backwards for the backslash. - // Examples: - // "a \" b" --> here second quote is escaped - // "c:\\" --> here second quote is not escaped - // - var j = i; - var escaped = false; - while (j > 0 && json[--j] == '\\') - { - escaped = !escaped; - } - - if (!escaped) - { - return i; - } - } - } - - Dbg.Diagnostics.Assert(false, "ConvertDictionary should return when encounter '}'"); - ThrowTerminatingError(NewError()); - return -1; - } + [Parameter] + public SwitchParameter AsArray { get; set; } /// - /// Convert a Json dictionary, which starts with '{'. + /// Specifies how strings are escaped when writing JSON text. + /// If the EscapeHandling property is set to EscapeHtml, the result JSON string will + /// be returned with HTML (<, >, &, ', ") and control characters (e.g. newline) are escaped. /// - /// - /// - /// - /// - /// - /// - private int ConvertDictionary(string json, int index, StringBuilder result, string padString, int numberOfSpaces) - { - result.Append("\r\n"); - StringBuilder newPadString = new StringBuilder(); - newPadString.Append(padString); - AddSpaces(numberOfSpaces, newPadString); - AddIndentations(1, newPadString); - - bool headChar = true; - bool beforeQuote = true; - int newSpaceCount = 0; - const int spaceCountAfterQuoteMark = 1; - - for (int i = index; i < json.Length; i++) - { - switch (json[i]) - { - case '{': - result.Append(json[i]); - i = ConvertDictionary(json, i + 1, result, newPadString.ToString(), newSpaceCount); - headChar = false; - break; - case '[': - result.Append(json[i]); - i = ConvertList(json, i + 1, result, newPadString.ToString(), newSpaceCount); - headChar = false; - break; - case '}': - result.Append("\r\n"); - result.Append(padString); - AddSpaces(numberOfSpaces, result); - result.Append(json[i]); - return i; - case '"': - if (headChar) - { - result.Append(newPadString.ToString()); - } - result.Append(json[i]); - int end = ConvertQuotedString(json, i + 1, result); - if (beforeQuote) - { - newSpaceCount = 0; - } - i = end; - headChar = false; - break; - case ':': - result.Append(json[i]); - AddSpaces(spaceCountAfterQuoteMark, result); - headChar = false; - beforeQuote = false; - break; - case ',': - result.Append(json[i]); - result.Append("\r\n"); - headChar = true; - beforeQuote = true; - newSpaceCount = 0; - break; - default: - if (headChar) - { - result.Append(newPadString.ToString()); - } - result.Append(json[i]); - if (beforeQuote) - { - newSpaceCount += 1; - } - headChar = false; - break; - } - } - - Dbg.Diagnostics.Assert(false, "ConvertDictionary should return when encounter '}'"); - ThrowTerminatingError(NewError()); - return -1; - } + [Parameter] + public StringEscapeHandling EscapeHandling { get; set; } = StringEscapeHandling.Default; /// - /// Add tabs to result + /// IDisposable implementation, dispose of any disposable resources created by the cmdlet. /// - /// - /// - private void AddIndentations(int numberOfTabsToReturn, StringBuilder result) + public void Dispose() { - int realNumber = numberOfTabsToReturn * 2; - for (int i = 0; i < realNumber; i++) - { - result.Append(' '); - } + Dispose(disposing: true); + GC.SuppressFinalize(this); } /// - /// Add spaces to result + /// Implementation of IDisposable for both manual Dispose() and finalizer-called disposal of resources. /// - /// - /// - private void AddSpaces(int numberOfSpacesToReturn, StringBuilder result) + /// + /// Specified as true when Dispose() was called, false if this is called from the finalizer. + /// + protected virtual void Dispose(bool disposing) { - for (int i = 0; i < numberOfSpacesToReturn; i++) + if (disposing) { - result.Append(' '); + _cancellationSource.Dispose(); } } - private ErrorRecord NewError() - { - ErrorDetails details = new ErrorDetails(this.GetType().GetTypeInfo().Assembly, - "WebCmdletStrings", "JsonStringInBadFormat"); - ErrorRecord errorRecord = new ErrorRecord( - new InvalidOperationException(details.Message), - "JsonStringInBadFormat", - ErrorCategory.InvalidOperation, - InputObject); - return errorRecord; - } - - #endregion convertOutputToPrettierFormat + private readonly List _inputObjects = new(); /// - /// Return an alternate representation of the specified object that serializes the same JSON, except - /// that properties that cannot be evaluated are treated as having the value null. - /// - /// Primitive types are returned verbatim. Aggregate types are processed recursively. + /// Caching the input objects for the command. /// - /// The object to be processed - /// The current depth into the object graph - /// An object suitable for serializing to JSON - private object ProcessValue(object obj, int depth) + protected override void ProcessRecord() { - PSObject pso = obj as PSObject; - - if (pso != null) - obj = pso.BaseObject; - - Object rv = obj; - bool isPurePSObj = false; - bool isCustomObj = false; - - if (obj == null - || DBNull.Value.Equals(obj) - || obj is string - || obj is char - || obj is bool - || obj is DateTime - || obj is DateTimeOffset - || obj is Guid - || obj is Uri - || obj is double - || obj is float - || obj is decimal) - { - rv = obj; - } - else - { - TypeInfo t = obj.GetType().GetTypeInfo(); - - if (t.IsPrimitive) - { - rv = obj; - } - else if (t.IsEnum) - { - // Win8:378368 Enums based on System.Int64 or System.UInt64 are not JSON-serializable - // because JavaScript does not support the necessary precision. - Type enumUnderlyingType = Enum.GetUnderlyingType(obj.GetType()); - if (enumUnderlyingType.Equals(typeof(Int64)) || enumUnderlyingType.Equals(typeof(UInt64))) - { - rv = obj.ToString(); - } - else - { - rv = obj; - } - } - else - { - if (depth > Depth) - { - if (pso != null && pso.immediateBaseObjectIsEmpty) - { - // The obj is a pure PSObject, we convert the original PSObject to a string, - // instead of its base object in this case - rv = LanguagePrimitives.ConvertTo(pso, typeof(string), - CultureInfo.InvariantCulture); - isPurePSObj = true; - } - else - { - rv = LanguagePrimitives.ConvertTo(obj, typeof(String), - CultureInfo.InvariantCulture); - } - } - else - { - IDictionary dict = obj as IDictionary; - if (dict != null) - { - rv = ProcessDictionary(dict, depth); - } - else - { - IEnumerable enumerable = obj as IEnumerable; - if (enumerable != null) - { - rv = ProcessEnumerable(enumerable, depth); - } - else - { -#if CORECLR - rv = ProcessCustomObject(obj, depth); -#else - rv = ProcessCustomObject(obj, depth); -#endif - isCustomObj = true; - } - } - } - } - } - - rv = AddPsProperties(pso, rv, depth, isPurePSObj, isCustomObj); - - return rv; + _inputObjects.Add(InputObject); } /// - /// Add to a base object any properties that might have been added to an object (via PSObject) through the Add-Member cmdlet. + /// Do the conversion to json and write output. /// - /// The containing PSObject, or null if the base object was not contained in a PSObject - /// The base object that might have been decorated with additional properties - /// The current depth into the object graph - /// the processed object is a pure PSObject - /// the processed object is a custom object - /// - /// The original base object if no additional properties had been added, - /// otherwise a dictionary containing the value of the original base object in the "value" key - /// as well as the names and values of an additional properties. - /// - private object AddPsProperties(object psobj, object obj, int depth, bool isPurePSObj, bool isCustomObj) - { - PSObject pso = psobj as PSObject; - - if (pso == null) - return obj; - - // when isPurePSObj is true, the obj is guaranteed to be a string converted by LanguagePrimitives - if (isPurePSObj) - return obj; - - bool wasDictionary = true; - IDictionary dict = obj as IDictionary; - - if (dict == null) - { - wasDictionary = false; - dict = new Dictionary(); - dict.Add("value", obj); - } - - AppendPsProperties(pso, dict, depth, isCustomObj); - - if (wasDictionary == false && dict.Count == 1) - return obj; - - return dict; - } - - /// - /// Append to a dictionary any properties that might have been added to an object (via PSObject) through the Add-Member cmdlet. - /// If the passed in object is a custom object (not a simple object, not a dictionary, not a list, get processed in ProcessCustomObject method), - /// we also take Adapted properties into account. Otherwise, we only consider the Extended properties. - /// When the object is a pure PSObject, it also gets processed in "ProcessCustomObject" before reaching this method, so we will - /// iterate both extended and adapted properties for it. Since it's a pure PSObject, there will be no adapted properties. - /// - /// The containing PSObject, or null if the base object was not contained in a PSObject - /// The dictionary to which any additional properties will be appended - /// The current depth into the object graph - /// The processed object is a custom object - private void AppendPsProperties(PSObject psobj, IDictionary receiver, int depth, bool isCustomObject) + protected override void EndProcessing() { - // serialize only Extended and Adapted properties.. - PSMemberInfoCollection srcPropertiesToSearch = - new PSMemberInfoIntegratingCollection(psobj, - isCustomObject ? PSObject.GetPropertyCollection(PSMemberViewTypes.Extended | PSMemberViewTypes.Adapted) : - PSObject.GetPropertyCollection(PSMemberViewTypes.Extended)); - - foreach (PSPropertyInfo prop in srcPropertiesToSearch) + if (_inputObjects.Count > 0) { - object value = null; - try - { - value = prop.Value; - } - catch (Exception) - { - } - - if (!receiver.Contains(prop.Name)) - { - receiver[prop.Name] = ProcessValue(value, depth + 1); - } - } - } + object objectToProcess = (_inputObjects.Count > 1 || AsArray) ? (_inputObjects.ToArray() as object) : _inputObjects[0]; - /// - /// Return an alternate representation of the specified dictionary that serializes the same JSON, except - /// that any contained properties that cannot be evaluated are treated as having the value null. - /// - /// - /// - /// - private object ProcessDictionary(IDictionary dict, int depth) - { - Dictionary result = new Dictionary(dict.Count); + var context = new JsonObject.ConvertToJsonContext( + Depth, + EnumsAsStrings.IsPresent, + Compress.IsPresent, + EscapeHandling, + targetCmdlet: this, + _cancellationSource.Token); - foreach (DictionaryEntry entry in dict) - { - string name = entry.Key as string; - if (name == null) + // null is returned only if the pipeline is stopping (e.g. ctrl+c is signaled). + // in that case, we shouldn't write the null to the output pipe. + string output = JsonObject.ConvertToJson(objectToProcess, in context); + if (output != null) { - // use the error string that matches the message from JavaScriptSerializer - var exception = - new InvalidOperationException(string.Format(CultureInfo.InvariantCulture, - WebCmdletStrings.NonStringKeyInDictionary, - dict.GetType().FullName)); - ThrowTerminatingError(new ErrorRecord(exception, "NonStringKeyInDictionary", ErrorCategory.InvalidOperation, dict)); + WriteObject(output); } - - result.Add(name, ProcessValue(entry.Value, depth + 1)); - } - - return result; - } - - /// - /// Return an alternate representation of the specified collection that serializes the same JSON, except - /// that any contained properties that cannot be evaluated are treated as having the value null. - /// - /// - /// - /// - private object ProcessEnumerable(IEnumerable enumerable, int depth) - { - List result = new List(); - - foreach (object o in enumerable) - { - result.Add(ProcessValue(o, depth + 1)); } - - return result; } /// - /// Return an alternate representation of the specified aggregate object that serializes the same JSON, except - /// that any contained properties that cannot be evaluated are treated as having the value null. - /// - /// The result is a dictionary in which all public fields and public gettable properties of the original object - /// are represented. If any exception occurs while retrieving the value of a field or property, that entity - /// is included in the output dictionary with a value of null. + /// Process the Ctrl+C signal. /// - /// - /// - /// - private object ProcessCustomObject(object o, int depth) + protected override void StopProcessing() { - Dictionary result = new Dictionary(); - Type t = o.GetType(); - - foreach (FieldInfo info in t.GetFields(BindingFlags.Public | BindingFlags.Instance)) - { - if (!info.IsDefined(typeof(T), true)) - { - object value; - try - { - value = info.GetValue(o); - } - catch (Exception) - { - value = null; - } - - result.Add(info.Name, ProcessValue(value, depth + 1)); - } - } - - foreach (PropertyInfo info2 in t.GetProperties(BindingFlags.Public | BindingFlags.Instance)) - { - if (!info2.IsDefined(typeof(T), true)) - { - MethodInfo getMethod = info2.GetGetMethod(); - if ((getMethod != null) && (getMethod.GetParameters().Length <= 0)) - { - object value; - try - { - value = getMethod.Invoke(o, new object[0]); - } - catch (Exception) - { - value = null; - } - - result.Add(info2.Name, ProcessValue(value, depth + 1)); - } - } - } - return result; + _cancellationSource.Cancel(); } } } diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/CoreCLR/BasicHtmlWebResponseObject.CoreClr.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/CoreCLR/BasicHtmlWebResponseObject.CoreClr.cs deleted file mode 100644 index f4b060430f5..00000000000 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/CoreCLR/BasicHtmlWebResponseObject.CoreClr.cs +++ /dev/null @@ -1,55 +0,0 @@ -#if CORECLR - -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -using System.Net.Http; -using System.IO; -using System.Text; - -namespace Microsoft.PowerShell.Commands -{ - /// - /// Response object for html content without DOM parsing - /// - public partial class BasicHtmlWebResponseObject : WebResponseObject - { - #region Constructors - - /// - /// Constructor for HtmlWebResponseObject - /// - /// - public BasicHtmlWebResponseObject(HttpResponseMessage response) - : this(response, null) - { } - - /// - /// Constructor for HtmlWebResponseObject with memory stream - /// - /// - /// - public BasicHtmlWebResponseObject(HttpResponseMessage response, Stream contentStream) - : base(response, contentStream) - { - EnsureHtmlParser(); - InitializeContent(); - InitializeRawContent(response); - } - - #endregion Constructors - - #region Methods - - private void InitializeRawContent(HttpResponseMessage baseResponse) - { - StringBuilder raw = ContentHelper.GetRawContentHeader(baseResponse); - raw.Append(Content); - this.RawContent = raw.ToString(); - } - - #endregion Methods - } -} -#endif \ No newline at end of file diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/CoreCLR/ContentHelper.CoreClr.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/CoreCLR/ContentHelper.CoreClr.cs deleted file mode 100644 index 4268bc63a98..00000000000 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/CoreCLR/ContentHelper.CoreClr.cs +++ /dev/null @@ -1,72 +0,0 @@ -#if CORECLR - -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -using System.Text; -using System.Linq; -using System.Net.Http; -using System.Net.Http.Headers; - -namespace Microsoft.PowerShell.Commands -{ - internal static partial class ContentHelper - { - internal static Encoding GetEncoding(HttpResponseMessage response) - { - // ContentType may not exist in response header. - string charSet = response.Content.Headers.ContentType?.CharSet; - return GetEncodingOrDefault(charSet); - } - - internal static string GetContentType(HttpResponseMessage response) - { - // ContentType may not exist in response header. Return null if not. - return response.Content.Headers.ContentType?.MediaType; - } - - internal static StringBuilder GetRawContentHeader(HttpResponseMessage response) - { - StringBuilder raw = new StringBuilder(); - - string protocol = WebResponseHelper.GetProtocol(response); - if (!string.IsNullOrEmpty(protocol)) - { - int statusCode = WebResponseHelper.GetStatusCode(response); - string statusDescription = WebResponseHelper.GetStatusDescription(response); - raw.AppendFormat("{0} {1} {2}", protocol, statusCode, statusDescription); - raw.AppendLine(); - } - - HttpHeaders[] headerCollections = - { - response.Headers, - response.Content == null ? null : response.Content.Headers - }; - - foreach (var headerCollection in headerCollections) - { - if (headerCollection == null) - { - continue; - } - foreach (var header in headerCollection) - { - // Headers may have multiple entries with different values - foreach (var headerValue in header.Value) - { - raw.Append(header.Key); - raw.Append(": "); - raw.Append(headerValue); - raw.AppendLine(); - } - } - } - - raw.AppendLine(); - return raw; - } - } -} -#endif \ No newline at end of file diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/CoreCLR/HtmlWebResponseObject.CoreClr.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/CoreCLR/HtmlWebResponseObject.CoreClr.cs deleted file mode 100644 index c87d24a0d45..00000000000 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/CoreCLR/HtmlWebResponseObject.CoreClr.cs +++ /dev/null @@ -1,107 +0,0 @@ -#if CORECLR - -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -using System; -using System.Management.Automation; -using System.Net.Http; -using System.IO; -using System.Text; -using ExecutionContext = System.Management.Automation.ExecutionContext; - -namespace Microsoft.PowerShell.Commands -{ - /// - /// Response object for html content - /// - public partial class HtmlWebResponseObject : WebResponseObject, IDisposable - { - #region Constructors - - /// - /// Constructor for HtmlWebResponseObject - /// - /// - /// - internal HtmlWebResponseObject(HttpResponseMessage response, ExecutionContext executionContext) - : this(response, null, executionContext) - { } - - /// - /// Constructor for HtmlWebResponseObject with memory stream - /// - /// - /// /// - internal HtmlWebResponseObject(HttpResponseMessage response, Stream contentStream, ExecutionContext executionContext) - : base(response, contentStream) - { - if (executionContext == null) - { - throw PSTraceSource.NewArgumentNullException("executionContext"); - } - - _executionContext = executionContext; - InitializeContent(); - InitializeRawContent(response); - } - - #endregion Constructors - - #region Properties - - /// - /// Gets the Encoding that was used to decode the Content - /// - /// - /// The Encoding used to decode the Content; otherwise, a null reference if the content is not text. - /// - public Encoding Encoding { get; private set; } - - #endregion Properties - - #region Methods - - // NOTE: Currently this code path is not enabled. - // See FillRequestStream in WebRequestPSCmdlet.CoreClr.cs and - // GetResponseObject in WebResponseObjectFactory.CoreClr.cs for details. - private void InitializeContent() - { - string contentType = ContentHelper.GetContentType(BaseResponse); - string content = null; - if (ContentHelper.IsText(contentType)) - { - Encoding encoding = null; - // fill the Content buffer - string characterSet = WebResponseHelper.GetCharacterSet(BaseResponse); - this.Content = StreamHelper.DecodeStream(RawContentStream, characterSet, out encoding); - this.Encoding = encoding; - } - else - { - this.Content = string.Empty; - } - } - - private void InitializeRawContent(HttpResponseMessage baseResponse) - { - StringBuilder raw = ContentHelper.GetRawContentHeader(baseResponse); - if (null != Content) - { - raw.Append(Content); - } - this.RawContent = raw.ToString(); - } - - /// - /// Dispose the the instance of the class. - /// - public void Dispose() - { - GC.SuppressFinalize(this); - } - #endregion - } -} -#endif \ No newline at end of file diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/CoreCLR/HttpKnownHeaderNames.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/CoreCLR/HttpKnownHeaderNames.cs index 975e89a19c5..6571696e5bf 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/CoreCLR/HttpKnownHeaderNames.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/CoreCLR/HttpKnownHeaderNames.cs @@ -1,27 +1,34 @@ -#if CORECLR +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +#nullable enable using System; using System.Collections.Generic; namespace Microsoft.PowerShell.Commands { - internal static partial class HttpKnownHeaderNames + internal static class HttpKnownHeaderNames { #region Known_HTTP_Header_Names // Known HTTP Header Names. - // List comes from corefx/System/Net/HttpKnownHeaderNames.cs + // List comes from https://github.com/dotnet/runtime/blob/51a8dd5323721b363e61069575511f783e7ea6d3/src/libraries/Common/src/System/Net/HttpKnownHeaderNames.cs public const string Accept = "Accept"; public const string AcceptCharset = "Accept-Charset"; public const string AcceptEncoding = "Accept-Encoding"; public const string AcceptLanguage = "Accept-Language"; + public const string AcceptPatch = "Accept-Patch"; public const string AcceptRanges = "Accept-Ranges"; + public const string AccessControlAllowCredentials = "Access-Control-Allow-Credentials"; + public const string AccessControlAllowHeaders = "Access-Control-Allow-Headers"; + public const string AccessControlAllowMethods = "Access-Control-Allow-Methods"; + public const string AccessControlAllowOrigin = "Access-Control-Allow-Origin"; + public const string AccessControlExposeHeaders = "Access-Control-Expose-Headers"; + public const string AccessControlMaxAge = "Access-Control-Max-Age"; public const string Age = "Age"; public const string Allow = "Allow"; + public const string AltSvc = "Alt-Svc"; public const string Authorization = "Authorization"; public const string CacheControl = "Cache-Control"; public const string Connection = "Connection"; @@ -32,6 +39,7 @@ internal static partial class HttpKnownHeaderNames public const string ContentLocation = "Content-Location"; public const string ContentMD5 = "Content-MD5"; public const string ContentRange = "Content-Range"; + public const string ContentSecurityPolicy = "Content-Security-Policy"; public const string ContentType = "Content-Type"; public const string Cookie = "Cookie"; public const string Cookie2 = "Cookie2"; @@ -48,6 +56,7 @@ internal static partial class HttpKnownHeaderNames public const string IfUnmodifiedSince = "If-Unmodified-Since"; public const string KeepAlive = "Keep-Alive"; public const string LastModified = "Last-Modified"; + public const string Link = "Link"; public const string Location = "Location"; public const string MaxForwards = "Max-Forwards"; public const string Origin = "Origin"; @@ -56,6 +65,7 @@ internal static partial class HttpKnownHeaderNames public const string ProxyAuthenticate = "Proxy-Authenticate"; public const string ProxyAuthorization = "Proxy-Authorization"; public const string ProxyConnection = "Proxy-Connection"; + public const string PublicKeyPins = "Public-Key-Pins"; public const string Range = "Range"; public const string Referer = "Referer"; // NB: The spelling-mistake "Referer" for "Referrer" must be matched. public const string RetryAfter = "Retry-After"; @@ -67,45 +77,49 @@ internal static partial class HttpKnownHeaderNames public const string Server = "Server"; public const string SetCookie = "Set-Cookie"; public const string SetCookie2 = "Set-Cookie2"; + public const string StrictTransportSecurity = "Strict-Transport-Security"; public const string TE = "TE"; + public const string TSV = "TSV"; public const string Trailer = "Trailer"; public const string TransferEncoding = "Transfer-Encoding"; public const string Upgrade = "Upgrade"; + public const string UpgradeInsecureRequests = "Upgrade-Insecure-Requests"; public const string UserAgent = "User-Agent"; public const string Vary = "Vary"; public const string Via = "Via"; public const string WWWAuthenticate = "WWW-Authenticate"; public const string Warning = "Warning"; public const string XAspNetVersion = "X-AspNet-Version"; + public const string XContentDuration = "X-Content-Duration"; + public const string XContentTypeOptions = "X-Content-Type-Options"; + public const string XFrameOptions = "X-Frame-Options"; + public const string XMSEdgeRef = "X-MSEdge-Ref"; public const string XPoweredBy = "X-Powered-By"; + public const string XRequestID = "X-Request-ID"; + public const string XUACompatible = "X-UA-Compatible"; #endregion Known_HTTP_Header_Names - private static HashSet s_contentHeaderSet = null; - internal static HashSet ContentHeaders - { - get - { - if (s_contentHeaderSet == null) - { - s_contentHeaderSet = new HashSet(StringComparer.OrdinalIgnoreCase); + private static readonly HashSet s_contentHeaderSet; - s_contentHeaderSet.Add(HttpKnownHeaderNames.Allow); - s_contentHeaderSet.Add(HttpKnownHeaderNames.ContentDisposition); - s_contentHeaderSet.Add(HttpKnownHeaderNames.ContentEncoding); - s_contentHeaderSet.Add(HttpKnownHeaderNames.ContentLanguage); - s_contentHeaderSet.Add(HttpKnownHeaderNames.ContentLength); - s_contentHeaderSet.Add(HttpKnownHeaderNames.ContentLocation); - s_contentHeaderSet.Add(HttpKnownHeaderNames.ContentMD5); - s_contentHeaderSet.Add(HttpKnownHeaderNames.ContentRange); - s_contentHeaderSet.Add(HttpKnownHeaderNames.ContentType); - s_contentHeaderSet.Add(HttpKnownHeaderNames.Expires); - s_contentHeaderSet.Add(HttpKnownHeaderNames.LastModified); - } + static HttpKnownHeaderNames() + { + // Thread-safe initialization. + s_contentHeaderSet = new HashSet(StringComparer.OrdinalIgnoreCase); - return s_contentHeaderSet; - } + s_contentHeaderSet.Add(HttpKnownHeaderNames.Allow); + s_contentHeaderSet.Add(HttpKnownHeaderNames.ContentDisposition); + s_contentHeaderSet.Add(HttpKnownHeaderNames.ContentEncoding); + s_contentHeaderSet.Add(HttpKnownHeaderNames.ContentLanguage); + s_contentHeaderSet.Add(HttpKnownHeaderNames.ContentLength); + s_contentHeaderSet.Add(HttpKnownHeaderNames.ContentLocation); + s_contentHeaderSet.Add(HttpKnownHeaderNames.ContentMD5); + s_contentHeaderSet.Add(HttpKnownHeaderNames.ContentRange); + s_contentHeaderSet.Add(HttpKnownHeaderNames.ContentType); + s_contentHeaderSet.Add(HttpKnownHeaderNames.Expires); + s_contentHeaderSet.Add(HttpKnownHeaderNames.LastModified); } + + internal static HashSet ContentHeaders => s_contentHeaderSet; } } -#endif diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/CoreCLR/InvokeRestMethodCommand.CoreClr.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/CoreCLR/InvokeRestMethodCommand.CoreClr.cs deleted file mode 100644 index 8be7c98342a..00000000000 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/CoreCLR/InvokeRestMethodCommand.CoreClr.cs +++ /dev/null @@ -1,115 +0,0 @@ -#if CORECLR - -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -using System; -using System.Management.Automation; -using System.Net.Http; -using System.Text; - -namespace Microsoft.PowerShell.Commands -{ - /// - /// The Invoke-RestMethod command - /// This command makes an HTTP or HTTPS request to a web service, - /// and returns the response in an appropriate way. - /// Intended to work against the wide spectrum of "RESTful" web services - /// currently deployed across the web. - /// - [Cmdlet(VerbsLifecycle.Invoke, "RestMethod", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=217034", DefaultParameterSetName = "StandardMethod")] - public partial class InvokeRestMethodCommand : WebRequestPSCmdlet - { - #region Virtual Method Overrides - - /// - /// Process the web response and output corresponding objects. - /// - /// - internal override void ProcessResponse(HttpResponseMessage response) - { - if (null == response) { throw new ArgumentNullException("response"); } - - using (BufferingStreamReader responseStream = new BufferingStreamReader(StreamHelper.GetResponseStream(response))) - { - if (ShouldWriteToPipeline) - { - // First see if it is an RSS / ATOM feed, in which case we can - // stream it - unless the user has overridden it with a return type of "XML" - if (TryProcessFeedStream(responseStream)) - { - // Do nothing, content has been processed. - } - else - { - // determine the response type - RestReturnType returnType = CheckReturnType(response); - // get the response encoding - Encoding encoding = ContentHelper.GetEncoding(response); - - object obj = null; - Exception ex = null; - - string str = StreamHelper.DecodeStream(responseStream, ref encoding); - bool convertSuccess = false; - - // On CoreCLR, we need to explicitly load Json.NET - JsonObject.ImportJsonDotNetModule(this); - if (returnType == RestReturnType.Json) - { - convertSuccess = TryConvertToJson(str, out obj, ref ex) || TryConvertToXml(str, out obj, ref ex); - } - // default to try xml first since it's more common - else - { - convertSuccess = TryConvertToXml(str, out obj, ref ex) || TryConvertToJson(str, out obj, ref ex); - } - - if (!convertSuccess) - { - // fallback to string - obj = str; - } - - WriteObject(obj); - } - } - - if (ShouldSaveToOutFile) - { - StreamHelper.SaveStreamToFile(responseStream, QualifiedOutFile, this); - } - } - } - - #endregion Virtual Method Overrides - - #region Helper Methods - - private RestReturnType CheckReturnType(HttpResponseMessage response) - { - if (null == response) { throw new ArgumentNullException("response"); } - - RestReturnType rt = RestReturnType.Detect; - string contentType = ContentHelper.GetContentType(response); - if (string.IsNullOrEmpty(contentType)) - { - rt = RestReturnType.Detect; - } - else if (ContentHelper.IsJson(contentType)) - { - rt = RestReturnType.Json; - } - else if (ContentHelper.IsXml(contentType)) - { - rt = RestReturnType.Xml; - } - - return (rt); - } - - #endregion Helper Methods - } -} -#endif \ No newline at end of file diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/CoreCLR/InvokeWebRequestCommand.CoreClr.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/CoreCLR/InvokeWebRequestCommand.CoreClr.cs index ebd8a659c3d..0bcafcf2964 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/CoreCLR/InvokeWebRequestCommand.CoreClr.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/CoreCLR/InvokeWebRequestCommand.CoreClr.cs @@ -1,31 +1,32 @@ -#if CORECLR +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +#nullable enable using System; +using System.IO; using System.Management.Automation; using System.Net.Http; -using System.IO; +using System.Threading; namespace Microsoft.PowerShell.Commands { /// - /// The Invoke-RestMethod command + /// The Invoke-WebRequest command. /// This command makes an HTTP or HTTPS request to a web server and returns the results. /// - [Cmdlet(VerbsLifecycle.Invoke, "WebRequest", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=217035", DefaultParameterSetName = "StandardMethod")] + [Cmdlet(VerbsLifecycle.Invoke, "WebRequest", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2097126", DefaultParameterSetName = "StandardMethod")] + [OutputType(typeof(BasicHtmlWebResponseObject))] public class InvokeWebRequestCommand : WebRequestPSCmdlet { #region Virtual Method Overrides /// - /// Default constructor for InvokeWebRequestCommand + /// Initializes a new instance of the class. /// public InvokeWebRequestCommand() : base() { - this._parseRelLink = true; + _parseRelLink = true; } /// @@ -34,30 +35,27 @@ public InvokeWebRequestCommand() : base() /// internal override void ProcessResponse(HttpResponseMessage response) { - if (null == response) { throw new ArgumentNullException("response"); } - - // check for Server Core, throws exception if -UseBasicParsing is not used - if (ShouldWriteToPipeline && !UseBasicParsing) - { - // IE is not available in PS Linux, and may not available in PS Core depending on - // where it's running (desktop/nano/iot). - // For PS Linux and PS Core, if IE is not available, we always use basic parsing. - if (!VerifyInternetExplorerAvailable(true)) - { - UseBasicParsing = true; - } - } + ArgumentNullException.ThrowIfNull(response); + TimeSpan perReadTimeout = ConvertTimeoutSecondsToTimeSpan(OperationTimeoutSeconds); + Stream responseStream = StreamHelper.GetResponseStream(response, _cancelToken.Token); + string outFilePath = WebResponseHelper.GetOutFilePath(response, _qualifiedOutFile); - Stream responseStream = StreamHelper.GetResponseStream(response); if (ShouldWriteToPipeline) { - // creating a MemoryStream wrapper to response stream here to support IsStopping. - responseStream = new WebResponseContentMemoryStream(responseStream, StreamHelper.ChunkSize, this); - WebResponseObject ro = WebResponseObjectFactory.GetResponseObject(response, responseStream, this.Context, UseBasicParsing); + // Creating a MemoryStream wrapper to response stream here to support IsStopping. + responseStream = new WebResponseContentMemoryStream( + responseStream, + StreamHelper.ChunkSize, + this, + response.Content.Headers.ContentLength.GetValueOrDefault(), + perReadTimeout, + _cancelToken.Token); + WebResponseObject ro = WebResponseHelper.IsText(response) ? new BasicHtmlWebResponseObject(response, responseStream, perReadTimeout, _cancelToken.Token) : new WebResponseObject(response, responseStream, perReadTimeout, _cancelToken.Token); ro.RelationLink = _relationLink; + ro.OutFile = outFilePath; WriteObject(ro); - // use the rawcontent stream from WebResponseObject for further + // Use the rawcontent stream from WebResponseObject for further // processing of the stream. This is need because WebResponse's // stream can be used only once. responseStream = ro.RawContentStream; @@ -66,11 +64,14 @@ internal override void ProcessResponse(HttpResponseMessage response) if (ShouldSaveToOutFile) { - StreamHelper.SaveStreamToFile(responseStream, QualifiedOutFile, this); + WriteVerbose($"File Name: {Path.GetFileName(outFilePath)}"); + + // ContentLength is always the partial length, while ContentRange is the full length + // Without Request.Range set, ContentRange is null and partial length (ContentLength) equals to full length + StreamHelper.SaveStreamToFile(responseStream, outFilePath, this, response.Content.Headers.ContentRange?.Length.GetValueOrDefault() ?? response.Content.Headers.ContentLength.GetValueOrDefault(), perReadTimeout, _cancelToken.Token); } } #endregion Virtual Method Overrides } } -#endif \ No newline at end of file diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/CoreCLR/WebProxy.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/CoreCLR/WebProxy.cs index 2ceb4dc3b33..c8fed859771 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/CoreCLR/WebProxy.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/CoreCLR/WebProxy.cs @@ -1,72 +1,65 @@ -#if CORECLR +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +#nullable enable using System; using System.Net; namespace Microsoft.PowerShell.Commands { - internal class WebProxy : IWebProxy + internal sealed class WebProxy : IWebProxy, IEquatable { - private ICredentials _credentials; - private Uri _proxyAddress; + private ICredentials? _credentials; + private readonly Uri _proxyAddress; internal WebProxy(Uri address) { - if (address == null) - { - throw new ArgumentNullException("address"); - } + ArgumentNullException.ThrowIfNull(address); _proxyAddress = address; } - public ICredentials Credentials - { - get { return _credentials; } - set { _credentials = value; } - } + public override bool Equals(object? obj) => Equals(obj as WebProxy); - internal bool BypassProxyOnLocal - { - get; set; - } + public override int GetHashCode() => HashCode.Combine(_proxyAddress, _credentials, BypassProxyOnLocal); - internal bool UseDefaultCredentials + public bool Equals(WebProxy? other) { - get - { - return _credentials == CredentialCache.DefaultCredentials; - } - set + if (other is null) { - _credentials = value ? CredentialCache.DefaultCredentials : null; + return false; } + + // _proxyAddress cannot be null as it is set in the constructor + return other._credentials == _credentials + && _proxyAddress.Equals(other._proxyAddress) + && BypassProxyOnLocal == other.BypassProxyOnLocal; } - public Uri GetProxy(Uri destination) + public ICredentials? Credentials { - if (destination == null) - { - throw new ArgumentNullException("destination"); - } + get => _credentials; - if (destination.IsLoopback) - { - return destination; - } + set => _credentials = value; + } - return _proxyAddress; + internal bool BypassProxyOnLocal { get; set; } + internal bool UseDefaultCredentials + { + get => _credentials == CredentialCache.DefaultCredentials; + + set => _credentials = value ? CredentialCache.DefaultCredentials : null; } - public bool IsBypassed(Uri host) + public Uri GetProxy(Uri destination) { - return host.IsLoopback; + ArgumentNullException.ThrowIfNull(destination); + + return destination.IsLoopback ? destination : _proxyAddress; } + + public bool IsBypassed(Uri host) => host.IsLoopback; } } -#endif \ No newline at end of file diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/CoreCLR/WebRequestPSCmdlet.CoreClr.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/CoreCLR/WebRequestPSCmdlet.CoreClr.cs deleted file mode 100644 index 750afa12c82..00000000000 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/CoreCLR/WebRequestPSCmdlet.CoreClr.cs +++ /dev/null @@ -1,839 +0,0 @@ -#if CORECLR - -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -using System; -using System.Management.Automation; -using System.Net; -using System.Net.Http; -using System.Net.Http.Headers; -using System.IO; -using System.Text; -using System.Collections; -using System.Globalization; -using System.Security.Cryptography; -using System.Threading; -using System.Xml; -using System.Collections.Generic; -using System.Text.RegularExpressions; -using System.Linq; - -namespace Microsoft.PowerShell.Commands -{ - /// - /// Exception class for webcmdlets to enable returning HTTP error response - /// - public sealed class HttpResponseException : HttpRequestException - { - /// - /// Constructor for HttpResponseException - /// - /// Message for the exception - /// Response from the HTTP server - public HttpResponseException (string message, HttpResponseMessage response) : base(message) - { - Response = response; - } - - /// - /// HTTP error response - /// - public HttpResponseMessage Response { get; private set; } - } - - /// - /// Base class for Invoke-RestMethod and Invoke-WebRequest commands. - /// - public abstract partial class WebRequestPSCmdlet : PSCmdlet - { - - /// - /// gets or sets the PreserveAuthorizationOnRedirect property - /// - /// - /// This property overrides compatibility with web requests on Windows. - /// On FullCLR (WebRequest), authorization headers are stripped during redirect. - /// CoreCLR (HTTPClient) does not have this behavior so web requests that work on - /// PowerShell/FullCLR can fail with PowerShell/CoreCLR. To provide compatibility, - /// we'll detect requests with an Authorization header and automatically strip - /// the header when the first redirect occurs. This switch turns off this logic for - /// edge cases where the authorization header needs to be preserved across redirects. - /// - [Parameter] - public virtual SwitchParameter PreserveAuthorizationOnRedirect { get; set; } - - /// - /// gets or sets the SkipHeaderValidation property - /// - /// - /// This property adds headers to the request's header collection without validation. - /// - [Parameter] - public virtual SwitchParameter SkipHeaderValidation { get; set; } - - #region Abstract Methods - - /// - /// Read the supplied WebResponse object and push the - /// resulting output into the pipeline. - /// - /// Instance of a WebResponse object to be processed - internal abstract void ProcessResponse(HttpResponseMessage response); - - #endregion Abstract Methods - - /// - /// Cancellation token source - /// - private CancellationTokenSource _cancelToken = null; - - /// - /// Parse Rel Links - /// - internal bool _parseRelLink = false; - - /// - /// Automatically follow Rel Links - /// - internal bool _followRelLink = false; - - /// - /// Automatically follow Rel Links - /// - internal Dictionary _relationLink = null; - - /// - /// Maximum number of Rel Links to follow - /// - internal int _maximumFollowRelLink = Int32.MaxValue; - - private HttpMethod GetHttpMethod(WebRequestMethod method) - { - switch (Method) - { - case WebRequestMethod.Default: - case WebRequestMethod.Get: - return HttpMethod.Get; - case WebRequestMethod.Head: - return HttpMethod.Head; - case WebRequestMethod.Post: - return HttpMethod.Post; - case WebRequestMethod.Put: - return HttpMethod.Put; - case WebRequestMethod.Delete: - return HttpMethod.Delete; - case WebRequestMethod.Trace: - return HttpMethod.Trace; - case WebRequestMethod.Options: - return HttpMethod.Options; - default: - // Merge and Patch - return new HttpMethod(Method.ToString().ToUpperInvariant()); - } - } - - #region Virtual Methods - - // NOTE: Only pass true for handleRedirect if the original request has an authorization header - // and PreserveAuthorizationOnRedirect is NOT set. - internal virtual HttpClient GetHttpClient(bool handleRedirect) - { - // By default the HttpClientHandler will automatically decompress GZip and Deflate content - HttpClientHandler handler = new HttpClientHandler(); - handler.CookieContainer = WebSession.Cookies; - - // set the credentials used by this request - if (WebSession.UseDefaultCredentials) - { - // the UseDefaultCredentials flag overrides other supplied credentials - handler.UseDefaultCredentials = true; - } - else if (WebSession.Credentials != null) - { - handler.Credentials = WebSession.Credentials; - } - - if (NoProxy) - { - handler.UseProxy = false; - } - else if (WebSession.Proxy != null) - { - handler.Proxy = WebSession.Proxy; - } - - if (null != WebSession.Certificates) - { - handler.ClientCertificates.AddRange(WebSession.Certificates); - } - - if (SkipCertificateCheck) - { - handler.ServerCertificateCustomValidationCallback = HttpClientHandler.DangerousAcceptAnyServerCertificateValidator; - handler.ClientCertificateOptions = ClientCertificateOption.Manual; - } - - // This indicates GetResponse will handle redirects. - if (handleRedirect) - { - handler.AllowAutoRedirect = false; - } - else if (WebSession.MaximumRedirection > -1) - { - if (WebSession.MaximumRedirection == 0) - { - handler.AllowAutoRedirect = false; - } - else - { - handler.MaxAutomaticRedirections = WebSession.MaximumRedirection; - } - } - - HttpClient httpClient = new HttpClient(handler); - - // check timeout setting (in seconds instead of milliseconds as in HttpWebRequest) - if (TimeoutSec == 0) - { - // A zero timeout means infinite - httpClient.Timeout = TimeSpan.FromMilliseconds(Timeout.Infinite); - } - else if (TimeoutSec > 0) - { - httpClient.Timeout = new TimeSpan(0, 0, TimeoutSec); - } - - return httpClient; - } - - internal virtual HttpRequestMessage GetRequest(Uri uri, bool stripAuthorization) - { - Uri requestUri = PrepareUri(uri); - HttpMethod httpMethod = null; - - switch (ParameterSetName) - { - case "StandardMethodNoProxy": - goto case "StandardMethod"; - case "StandardMethod": - // set the method if the parameter was provided - httpMethod = GetHttpMethod(Method); - break; - case "CustomMethodNoProxy": - goto case "CustomMethod"; - case "CustomMethod": - if (!string.IsNullOrEmpty(CustomMethod)) - { - // set the method if the parameter was provided - httpMethod = new HttpMethod(CustomMethod.ToString().ToUpperInvariant()); - } - break; - } - - // create the base WebRequest object - var request = new HttpRequestMessage(httpMethod, requestUri); - - // pull in session data - if (WebSession.Headers.Count > 0) - { - WebSession.ContentHeaders.Clear(); - foreach (var entry in WebSession.Headers) - { - if (HttpKnownHeaderNames.ContentHeaders.Contains(entry.Key)) - { - WebSession.ContentHeaders.Add(entry.Key, entry.Value); - } - else - { - if (stripAuthorization - && - String.Equals(entry.Key, HttpKnownHeaderNames.Authorization.ToString(), StringComparison.OrdinalIgnoreCase) - ) - { - continue; - } - - if (SkipHeaderValidation) - { - request.Headers.TryAddWithoutValidation(entry.Key, entry.Value); - } - else - { - request.Headers.Add(entry.Key, entry.Value); - } - } - } - } - - // Set 'Transfer-Encoding: chunked' if 'Transfer-Encoding' is specified - if (WebSession.Headers.ContainsKey(HttpKnownHeaderNames.TransferEncoding)) - { - request.Headers.TransferEncodingChunked = true; - } - - // Set 'User-Agent' if WebSession.Headers doesn't already contain it - string userAgent = null; - if (WebSession.Headers.TryGetValue(HttpKnownHeaderNames.UserAgent, out userAgent)) - { - WebSession.UserAgent = userAgent; - } - else - { - if (SkipHeaderValidation) - { - request.Headers.TryAddWithoutValidation(HttpKnownHeaderNames.UserAgent, WebSession.UserAgent); - } - else - { - request.Headers.Add(HttpKnownHeaderNames.UserAgent, WebSession.UserAgent); - } - - } - - // Set 'Keep-Alive' to false. This means set the Connection to 'Close'. - if (DisableKeepAlive) - { - request.Headers.Add(HttpKnownHeaderNames.Connection, "Close"); - } - - // Set 'Transfer-Encoding' - if (TransferEncoding != null) - { - request.Headers.TransferEncodingChunked = true; - var headerValue = new TransferCodingHeaderValue(TransferEncoding); - if (!request.Headers.TransferEncoding.Contains(headerValue)) - { - request.Headers.TransferEncoding.Add(headerValue); - } - } - - // Some web sites (e.g. Twitter) will return exception on POST when Expect100 is sent - // Default behavior is continue to send body content anyway after a short period - // Here it send the two part as a whole. - request.Headers.ExpectContinue = false; - - return (request); - } - - internal virtual void FillRequestStream(HttpRequestMessage request) - { - if (null == request) { throw new ArgumentNullException("request"); } - - // set the content type - if (ContentType != null) - { - WebSession.ContentHeaders[HttpKnownHeaderNames.ContentType] = ContentType; - //request - } - // ContentType == null - else if (Method == WebRequestMethod.Post || (IsCustomMethodSet() && CustomMethod.ToUpperInvariant() == "POST")) - { - // Win8:545310 Invoke-WebRequest does not properly set MIME type for POST - string contentType = null; - WebSession.ContentHeaders.TryGetValue(HttpKnownHeaderNames.ContentType, out contentType); - if (string.IsNullOrEmpty(contentType)) - { - WebSession.ContentHeaders[HttpKnownHeaderNames.ContentType] = "application/x-www-form-urlencoded"; - } - } - - // coerce body into a usable form - if (Body != null) - { - object content = Body; - - // make sure we're using the base object of the body, not the PSObject wrapper - PSObject psBody = Body as PSObject; - if (psBody != null) - { - content = psBody.BaseObject; - } - - /* TODO: This needs to be enable after the dependency on mshtml is resolved. - var html = content as HtmlWebResponseObject; - if (html != null) - { - // use the form if it's the only one present - if (html.Forms.Count == 1) - { - SetRequestContent(request, html.Forms[0].Fields); - } - } - else if (content is FormObject) - */ - - if (content is FormObject) - { - FormObject form = content as FormObject; - SetRequestContent(request, form.Fields); - } - else if (content is IDictionary && request.Method != HttpMethod.Get) - { - IDictionary dictionary = content as IDictionary; - SetRequestContent(request, dictionary); - } - else if (content is XmlNode) - { - XmlNode xmlNode = content as XmlNode; - SetRequestContent(request, xmlNode); - } - else if (content is Stream) - { - Stream stream = content as Stream; - SetRequestContent(request, stream); - } - else if (content is byte[]) - { - byte[] bytes = content as byte[]; - SetRequestContent(request, bytes); - } - else - { - SetRequestContent(request, - (string)LanguagePrimitives.ConvertTo(content, typeof(string), CultureInfo.InvariantCulture)); - } - } - else if (InFile != null) // copy InFile data - { - try - { - // open the input file - SetRequestContent(request, new FileStream(InFile, FileMode.Open)); - } - catch (UnauthorizedAccessException) - { - string msg = string.Format(CultureInfo.InvariantCulture, WebCmdletStrings.AccessDenied, - _originalFilePath); - throw new UnauthorizedAccessException(msg); - } - } - - // Add the content headers - if (request.Content != null) - { - foreach (var entry in WebSession.ContentHeaders) - { - request.Content.Headers.Add(entry.Key, entry.Value); - } - } - } - - // Returns true if the status code is one of the supported redirection codes. - static bool IsRedirectCode(HttpStatusCode code) - { - int intCode = (int) code; - return - ( - (intCode >= 300 && intCode < 304) - || - intCode == 307 - ); - } - - // Returns true if the status code is a redirection code and the action requires switching from POST to GET on redirection. - // NOTE: Some of these status codes map to the same underlying value but spelling them out for completeness. - static bool IsRedirectToGet(HttpStatusCode code) - { - return - ( - code == HttpStatusCode.Found - || - code == HttpStatusCode.Moved - || - code == HttpStatusCode.Redirect - || - code == HttpStatusCode.RedirectMethod - || - code == HttpStatusCode.TemporaryRedirect - || - code == HttpStatusCode.RedirectKeepVerb - || - code == HttpStatusCode.SeeOther - ); - } - - internal virtual HttpResponseMessage GetResponse(HttpClient client, HttpRequestMessage request, bool stripAuthorization) - { - if (client == null) { throw new ArgumentNullException("client"); } - if (request == null) { throw new ArgumentNullException("request"); } - - _cancelToken = new CancellationTokenSource(); - HttpResponseMessage response = client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, _cancelToken.Token).GetAwaiter().GetResult(); - - if (stripAuthorization && IsRedirectCode(response.StatusCode)) - { - _cancelToken.Cancel(); - _cancelToken = null; - - // if explicit count was provided, reduce it for this redirection. - if (WebSession.MaximumRedirection > 0) - { - WebSession.MaximumRedirection--; - } - // For selected redirects that used POST, GET must be used with the - // redirected Location. - // Since GET is the default; POST only occurs when -Method POST is used. - if (Method == WebRequestMethod.Post && IsRedirectToGet(response.StatusCode)) - { - // See https://msdn.microsoft.com/en-us/library/system.net.httpstatuscode(v=vs.110).aspx - Method = WebRequestMethod.Get; - } - - // recreate the HttpClient with redirection enabled since the first call suppressed redirection - using (client = GetHttpClient(false)) - using (HttpRequestMessage redirectRequest = GetRequest(response.Headers.Location, stripAuthorization:true)) - { - FillRequestStream(redirectRequest); - _cancelToken = new CancellationTokenSource(); - response = client.SendAsync(redirectRequest, HttpCompletionOption.ResponseHeadersRead, _cancelToken.Token).GetAwaiter().GetResult(); - } - } - return response; - } - - internal virtual void UpdateSession(HttpResponseMessage response) - { - if (response == null) { throw new ArgumentNullException("response"); } - } - - #endregion Virtual Methods - - #region Overrides - - /// - /// the main execution method for cmdlets derived from WebRequestPSCmdlet. - /// - protected override void ProcessRecord() - { - try - { - // Set cmdlet context for write progress - ValidateParameters(); - PrepareSession(); - - // if the request contains an authorization header and PreserveAuthorizationOnRedirect is not set, - // it needs to be stripped on the first redirect. - bool stripAuthorization = null != WebSession - && - null != WebSession.Headers - && - !PreserveAuthorizationOnRedirect.IsPresent - && - WebSession.Headers.ContainsKey(HttpKnownHeaderNames.Authorization.ToString()); - - using (HttpClient client = GetHttpClient(stripAuthorization)) - { - int followedRelLink = 0; - Uri uri = Uri; - do - { - if (followedRelLink > 0) - { - string linkVerboseMsg = string.Format(CultureInfo.CurrentCulture, - WebCmdletStrings.FollowingRelLinkVerboseMsg, - uri.AbsoluteUri); - WriteVerbose(linkVerboseMsg); - } - - using (HttpRequestMessage request = GetRequest(uri, stripAuthorization:false)) - { - FillRequestStream(request); - try - { - long requestContentLength = 0; - if (request.Content != null) - requestContentLength = request.Content.Headers.ContentLength.Value; - - string reqVerboseMsg = String.Format(CultureInfo.CurrentCulture, - WebCmdletStrings.WebMethodInvocationVerboseMsg, - request.Method, - request.RequestUri, - requestContentLength); - WriteVerbose(reqVerboseMsg); - - HttpResponseMessage response = GetResponse(client, request, stripAuthorization); - - string contentType = ContentHelper.GetContentType(response); - string respVerboseMsg = string.Format(CultureInfo.CurrentCulture, - WebCmdletStrings.WebResponseVerboseMsg, - response.Content.Headers.ContentLength, - contentType); - WriteVerbose(respVerboseMsg); - - if (!response.IsSuccessStatusCode) - { - string message = String.Format(CultureInfo.CurrentCulture, WebCmdletStrings.ResponseStatusCodeFailure, - (int)response.StatusCode, response.ReasonPhrase); - HttpResponseException httpEx = new HttpResponseException(message, response); - ErrorRecord er = new ErrorRecord(httpEx, "WebCmdletWebResponseException", ErrorCategory.InvalidOperation, request); - string detailMsg = ""; - StreamReader reader = null; - try - { - reader = new StreamReader(StreamHelper.GetResponseStream(response)); - // remove HTML tags making it easier to read - detailMsg = System.Text.RegularExpressions.Regex.Replace(reader.ReadToEnd(), "<[^>]*>",""); - } - catch (Exception) - { - // catch all - } - finally - { - if (reader != null) - { - reader.Dispose(); - } - } - if (!String.IsNullOrEmpty(detailMsg)) - { - er.ErrorDetails = new ErrorDetails(detailMsg); - } - ThrowTerminatingError(er); - } - - if (_parseRelLink || _followRelLink) - { - ParseLinkHeader(response, uri); - } - ProcessResponse(response); - UpdateSession(response); - - // If we hit our maximum redirection count, generate an error. - // Errors with redirection counts of greater than 0 are handled automatically by .NET, but are - // impossible to detect programmatically when we hit this limit. By handling this ourselves - // (and still writing out the result), users can debug actual HTTP redirect problems. - if (WebSession.MaximumRedirection == 0) // Indicate "HttpClientHandler.AllowAutoRedirect == false" - { - if (response.StatusCode == HttpStatusCode.Found || - response.StatusCode == HttpStatusCode.Moved || - response.StatusCode == HttpStatusCode.MovedPermanently) - { - ErrorRecord er = new ErrorRecord(new InvalidOperationException(), "MaximumRedirectExceeded", ErrorCategory.InvalidOperation, request); - er.ErrorDetails = new ErrorDetails(WebCmdletStrings.MaximumRedirectionCountExceeded); - WriteError(er); - } - } - } - catch (HttpRequestException ex) - { - ErrorRecord er = new ErrorRecord(ex, "WebCmdletWebResponseException", ErrorCategory.InvalidOperation, request); - if (ex.InnerException != null) - { - er.ErrorDetails = new ErrorDetails(ex.InnerException.Message); - } - ThrowTerminatingError(er); - } - - if (_followRelLink) - { - if (!_relationLink.ContainsKey("next")) - { - return; - } - uri = new Uri(_relationLink["next"]); - followedRelLink++; - } - } - } - while (_followRelLink && (followedRelLink < _maximumFollowRelLink)); - } - } - catch (CryptographicException ex) - { - ErrorRecord er = new ErrorRecord(ex, "WebCmdletCertificateException", ErrorCategory.SecurityError, null); - ThrowTerminatingError(er); - } - catch (NotSupportedException ex) - { - ErrorRecord er = new ErrorRecord(ex, "WebCmdletIEDomNotSupportedException", ErrorCategory.NotImplemented, null); - ThrowTerminatingError(er); - } - } - - /// - /// Implementing ^C, after start the BeginGetResponse - /// - protected override void StopProcessing() - { - if (_cancelToken != null) - { - _cancelToken.Cancel(); - } - } - - #endregion Overrides - - #region Helper Methods - - /// - /// Sets the ContentLength property of the request and writes the specified content to the request's RequestStream. - /// - /// The WebRequest who's content is to be set - /// A byte array containing the content data. - /// The number of bytes written to the requests RequestStream (and the new value of the request's ContentLength property - /// - /// Because this function sets the request's ContentLength property and writes content data into the requests's stream, - /// it should be called one time maximum on a given request. - /// - internal long SetRequestContent(HttpRequestMessage request, Byte[] content) - { - if (request == null) - throw new ArgumentNullException("request"); - if (content == null) - return 0; - - var byteArrayContent = new ByteArrayContent(content); - request.Content = byteArrayContent; - - return byteArrayContent.Headers.ContentLength.Value; - } - - /// - /// Sets the ContentLength property of the request and writes the specified content to the request's RequestStream. - /// - /// The WebRequest who's content is to be set - /// A String object containing the content data. - /// The number of bytes written to the requests RequestStream (and the new value of the request's ContentLength property - /// - /// Because this function sets the request's ContentLength property and writes content data into the requests's stream, - /// it should be called one time maximum on a given request. - /// - internal long SetRequestContent(HttpRequestMessage request, string content) - { - if (request == null) - throw new ArgumentNullException("request"); - - if (content == null) - return 0; - - Encoding encoding = null; - if (ContentType != null) - { - // If Content-Type contains the encoding format (as CharSet), use this encoding format - // to encode the Body of the WebRequest sent to the server. Default Encoding format - // would be used if Charset is not supplied in the Content-Type property. - var mediaTypeHeaderValue = new MediaTypeHeaderValue(ContentType); - if (!string.IsNullOrEmpty(mediaTypeHeaderValue.CharSet)) - { - try - { - encoding = Encoding.GetEncoding(mediaTypeHeaderValue.CharSet); - } - catch (ArgumentException ex) - { - ErrorRecord er = new ErrorRecord(ex, "WebCmdletEncodingException", ErrorCategory.InvalidArgument, ContentType); - ThrowTerminatingError(er); - } - } - } - - Byte[] bytes = StreamHelper.EncodeToBytes(content, encoding); - var byteArrayContent = new ByteArrayContent(bytes); - request.Content = byteArrayContent; - - return byteArrayContent.Headers.ContentLength.Value; - } - - internal long SetRequestContent(HttpRequestMessage request, XmlNode xmlNode) - { - if (request == null) - throw new ArgumentNullException("request"); - - if (xmlNode == null) - return 0; - - Byte[] bytes = null; - XmlDocument doc = xmlNode as XmlDocument; - if (doc != null && (doc.FirstChild as XmlDeclaration) != null) - { - XmlDeclaration decl = doc.FirstChild as XmlDeclaration; - Encoding encoding = Encoding.GetEncoding(decl.Encoding); - bytes = StreamHelper.EncodeToBytes(doc.OuterXml, encoding); - } - else - { - bytes = StreamHelper.EncodeToBytes(xmlNode.OuterXml); - } - - var byteArrayContent = new ByteArrayContent(bytes); - request.Content = byteArrayContent; - - return byteArrayContent.Headers.ContentLength.Value; - } - - /// - /// Sets the ContentLength property of the request and writes the specified content to the request's RequestStream. - /// - /// The WebRequest who's content is to be set - /// A Stream object containing the content data. - /// The number of bytes written to the requests RequestStream (and the new value of the request's ContentLength property - /// - /// Because this function sets the request's ContentLength property and writes content data into the requests's stream, - /// it should be called one time maximum on a given request. - /// - internal long SetRequestContent(HttpRequestMessage request, Stream contentStream) - { - if (request == null) - throw new ArgumentNullException("request"); - if (contentStream == null) - throw new ArgumentNullException("contentStream"); - - var streamContent = new StreamContent(contentStream); - request.Content = streamContent; - - return streamContent.Headers.ContentLength.Value; - } - - internal long SetRequestContent(HttpRequestMessage request, IDictionary content) - { - if (request == null) - throw new ArgumentNullException("request"); - if (content == null) - throw new ArgumentNullException("content"); - - string body = FormatDictionary(content); - return (SetRequestContent(request, body)); - - } - - internal void ParseLinkHeader(HttpResponseMessage response, System.Uri requestUri) - { - if (_relationLink == null) - { - _relationLink = new Dictionary(); - } - else - { - _relationLink.Clear(); - } - - // we only support the URL in angle brackets and `rel`, other attributes are ignored - // user can still parse it themselves via the Headers property - string pattern = "<(?.*?)>;\\srel=\"(?.*?)\""; - IEnumerable links; - if (response.Headers.TryGetValues("Link", out links)) - { - foreach(string link in links.FirstOrDefault().Split(",")) - { - Match match = Regex.Match(link, pattern); - if (match.Success) - { - string url = match.Groups["url"].Value; - string rel = match.Groups["rel"].Value; - if (url != String.Empty && rel != String.Empty && !_relationLink.ContainsKey(rel)) - { - Uri absoluteUri = new Uri(requestUri, url); - _relationLink.Add(rel, absoluteUri.AbsoluteUri.ToString()); - } - } - } - } - } - - #endregion Helper Methods - } -} -#endif diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/CoreCLR/WebResponseHelper.CoreClr.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/CoreCLR/WebResponseHelper.CoreClr.cs index ec570876343..377a7e56265 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/CoreCLR/WebResponseHelper.CoreClr.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/CoreCLR/WebResponseHelper.CoreClr.cs @@ -1,47 +1,61 @@ -#if CORECLR +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +#nullable enable -using System.Net.Http; +using System; +using System.Collections.Generic; using System.Globalization; +using System.IO; +using System.Net.Http; namespace Microsoft.PowerShell.Commands { - internal static partial class WebResponseHelper + internal static class WebResponseHelper { - internal static string GetCharacterSet(HttpResponseMessage response) - { - string characterSet = response.Content.Headers.ContentType.CharSet; - return characterSet; - } + internal static string? GetCharacterSet(HttpResponseMessage response) => response.Content.Headers.ContentType?.CharSet; - internal static string GetProtocol(HttpResponseMessage response) + internal static Dictionary> GetHeadersDictionary(HttpResponseMessage response) { - string protocol = string.Format(CultureInfo.InvariantCulture, - "HTTP/{0}", response.Version); - return protocol; - } + var headers = new Dictionary>(StringComparer.OrdinalIgnoreCase); + foreach (var entry in response.Headers) + { + headers[entry.Key] = entry.Value; + } + // In CoreFX, HttpResponseMessage separates content related headers, such as Content-Type to + // HttpResponseMessage.Content.Headers. The remaining headers are in HttpResponseMessage.Headers. + // The keys in both should be unique with no duplicates between them. + // Added for backwards compatibility with PowerShell 5.1 and earlier. + if (response.Content is not null) + { + foreach (var entry in response.Content.Headers) + { + headers[entry.Key] = entry.Value; + } + } - internal static int GetStatusCode(HttpResponseMessage response) - { - int statusCode = (int)response.StatusCode; - return statusCode; + return headers; } - internal static string GetStatusDescription(HttpResponseMessage response) + internal static string GetOutFilePath(HttpResponseMessage response, string qualifiedOutFile) { - string statusDescription = response.StatusCode.ToString(); - return statusDescription; + // Get file name from last segment of Uri + string? lastUriSegment = System.Net.WebUtility.UrlDecode(response.RequestMessage?.RequestUri?.Segments[^1]); + + return Directory.Exists(qualifiedOutFile) ? Path.Join(qualifiedOutFile, lastUriSegment) : qualifiedOutFile; } + internal static string GetProtocol(HttpResponseMessage response) => string.Create(CultureInfo.InvariantCulture, $"HTTP/{response.Version}"); + + internal static int GetStatusCode(HttpResponseMessage response) => (int)response.StatusCode; + + internal static string GetStatusDescription(HttpResponseMessage response) => response.StatusCode.ToString(); + internal static bool IsText(HttpResponseMessage response) { // ContentType may not exist in response header. - string contentType = response.Content.Headers.ContentType?.MediaType; + string? contentType = ContentHelper.GetContentType(response); return ContentHelper.IsText(contentType); } } } -#endif \ No newline at end of file diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/CoreCLR/WebResponseObject.CoreClr.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/CoreCLR/WebResponseObject.CoreClr.cs deleted file mode 100644 index 9e4b52bfd0f..00000000000 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/CoreCLR/WebResponseObject.CoreClr.cs +++ /dev/null @@ -1,131 +0,0 @@ -#if CORECLR - -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -using System; -using System.Text; -using System.Net.Http; -using System.Collections.Generic; -using System.IO; - -namespace Microsoft.PowerShell.Commands -{ - /// - /// WebResponseObject - /// - public partial class WebResponseObject - { - #region Properties - - /// - /// gets or sets the BaseResponse property - /// - public HttpResponseMessage BaseResponse { get; set; } - - /// - /// gets the Headers property - /// - public Dictionary> Headers - { - get - { - var headers = new Dictionary>(StringComparer.OrdinalIgnoreCase); - foreach (var entry in BaseResponse.Headers) - { - headers[entry.Key] = entry.Value; - } - if (BaseResponse.Content != null) - { - foreach (var entry in BaseResponse.Content.Headers) - { - headers[entry.Key] = entry.Value; - } - } - - return headers; - } - } - - /// - /// gets the RelationLink property - /// - public Dictionary RelationLink { get; internal set; } - - #endregion - - #region Constructors - - /// - /// Constructor for WebResponseObject - /// - /// - public WebResponseObject(HttpResponseMessage response) - : this(response, null) - { } - - /// - /// Constructor for WebResponseObject with contentStream - /// - /// - /// - public WebResponseObject(HttpResponseMessage response, Stream contentStream) - { - SetResponse(response, contentStream); - InitializeContent(); - InitializeRawContent(response); - } - - #endregion Constructors - - #region Methods - - private void InitializeRawContent(HttpResponseMessage baseResponse) - { - StringBuilder raw = ContentHelper.GetRawContentHeader(baseResponse); - - // Use ASCII encoding for the RawContent visual view of the content. - if (Content.Length > 0) - { - raw.Append(this.ToString()); - } - - this.RawContent = raw.ToString(); - } - - private void SetResponse(HttpResponseMessage response, Stream contentStream) - { - if (null == response) { throw new ArgumentNullException("response"); } - - BaseResponse = response; - - MemoryStream ms = contentStream as MemoryStream; - if (null != ms) - { - _rawContentStream = ms; - } - else - { - Stream st = contentStream; - if (contentStream == null) - { - st = StreamHelper.GetResponseStream(response); - } - - long contentLength = response.Content.Headers.ContentLength.Value; - if (0 >= contentLength) - { - contentLength = StreamHelper.DefaultReadBuffer; - } - int initialCapacity = (int)Math.Min(contentLength, StreamHelper.DefaultReadBuffer); - _rawContentStream = new WebResponseContentMemoryStream(st, initialCapacity, null); - } - // set the position of the content stream to the beginning - _rawContentStream.Position = 0; - } - - #endregion - } -} -#endif \ No newline at end of file diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/CoreCLR/WebResponseObjectFactory.CoreClr.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/CoreCLR/WebResponseObjectFactory.CoreClr.cs deleted file mode 100644 index f706ae5a142..00000000000 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/CoreCLR/WebResponseObjectFactory.CoreClr.cs +++ /dev/null @@ -1,40 +0,0 @@ -#if CORECLR - -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -using System.Net.Http; -using System.IO; -using System.Management.Automation; - -namespace Microsoft.PowerShell.Commands -{ - internal static class WebResponseObjectFactory - { - internal static WebResponseObject GetResponseObject(HttpResponseMessage response, Stream responseStream, ExecutionContext executionContext, bool useBasicParsing = false) - { - WebResponseObject output; - if (WebResponseHelper.IsText(response)) - { - output = new BasicHtmlWebResponseObject(response, responseStream); - - // TODO: This code needs to be enable after the dependency on mshtml is resolved. - //if (useBasicParsing) - //{ - // output = new BasicHtmlWebResponseObject(response, responseStream); - //} - //else - //{ - // output = new HtmlWebResponseObject(response, responseStream, executionContext); - //} - } - else - { - output = new WebResponseObject(response, responseStream); - } - return (output); - } - } -} -#endif \ No newline at end of file diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/FormObject.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/FormObject.cs index 1ae2083a5d0..5ac4adfbb64 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/FormObject.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/FormObject.cs @@ -1,38 +1,39 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#nullable enable using System.Collections.Generic; namespace Microsoft.PowerShell.Commands { /// - /// FormObject used in HtmlWebResponseObject + /// FormObject used in HtmlWebResponseObject. /// public class FormObject { /// - /// gets or private sets the Id property + /// Gets the Id property. /// - public string Id { get; private set; } + public string Id { get; } /// - /// gets or private sets the Method property + /// Gets the Method property. /// - public string Method { get; private set; } + public string Method { get; } /// - /// gets or private sets the Action property + /// Gets the Action property. /// - public string Action { get; private set; } + public string Action { get; } /// - /// gets or private sets the Fields property + /// Gets the Fields property. /// - public Dictionary Fields { get; private set; } + public Dictionary Fields { get; } /// - /// constructor for FormObject + /// Initializes a new instance of the class. /// /// /// @@ -47,8 +48,7 @@ public FormObject(string id, string method, string action) internal void AddField(string key, string value) { - string test; - if (null != key && !Fields.TryGetValue(key, out test)) + if (key is not null && !Fields.TryGetValue(key, out string? _)) { Fields[key] = value; } diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/FormObjectCollection.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/FormObjectCollection.cs index 3fdfa1c6f55..5f923558185 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/FormObjectCollection.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/FormObjectCollection.cs @@ -1,6 +1,7 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#nullable enable using System; using System.Collections.ObjectModel; @@ -8,20 +9,20 @@ namespace Microsoft.PowerShell.Commands { /// - /// FormObjectCollection used in HtmlWebResponseObject + /// FormObjectCollection used in HtmlWebResponseObject. /// public class FormObjectCollection : Collection { /// - /// Gets the FormObject from the key + /// Gets the FormObject from the key. /// /// /// - public FormObject this[string key] + public FormObject? this[string key] { get { - FormObject form = null; + FormObject? form = null; foreach (FormObject f in this) { if (string.Equals(key, f.Id, StringComparison.OrdinalIgnoreCase)) @@ -30,7 +31,8 @@ public FormObject this[string key] break; } } - return (form); + + return form; } } } diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/FullClr/BasicHtmlWebResponseObject.FullClr.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/FullClr/BasicHtmlWebResponseObject.FullClr.cs deleted file mode 100644 index 80ee6bb1d22..00000000000 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/FullClr/BasicHtmlWebResponseObject.FullClr.cs +++ /dev/null @@ -1,52 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -using System.Net; -using System.IO; -using System.Text; - -namespace Microsoft.PowerShell.Commands -{ - /// - /// Response object for html content without DOM parsing - /// - public partial class BasicHtmlWebResponseObject : WebResponseObject - { - #region Constructors - - /// - /// Constructor for HtmlWebResponseObject - /// - /// - public BasicHtmlWebResponseObject(WebResponse response) - : this(response, null) - { } - - /// - /// Constructor for HtmlWebResponseObject with memory stream - /// - /// - /// - public BasicHtmlWebResponseObject(WebResponse response, Stream contentStream) - : base(response, contentStream) - { - EnsureHtmlParser(); - InitializeContent(); - InitializeRawContent(response); - } - - #endregion Constructors - - #region Methods - - private void InitializeRawContent(WebResponse baseResponse) - { - StringBuilder raw = ContentHelper.GetRawContentHeader(baseResponse); - raw.Append(Content); - this.RawContent = raw.ToString(); - } - - #endregion Methods - } -} diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/FullClr/ContentHelper.FullClr.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/FullClr/ContentHelper.FullClr.cs deleted file mode 100644 index 12d7d14d946..00000000000 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/FullClr/ContentHelper.FullClr.cs +++ /dev/null @@ -1,65 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -using System; -using System.Text; -using System.Net; - -namespace Microsoft.PowerShell.Commands -{ - internal static partial class ContentHelper - { - internal static Encoding GetEncoding(WebResponse response) - { - string characterSet = null; - HttpWebResponse httpResponse = response as HttpWebResponse; - if (null != httpResponse) - { - characterSet = httpResponse.CharacterSet; - } - - return GetEncodingOrDefault(characterSet); - } - - // Gets the content type with safe fallback - in the situation - // of FTPWebResponse that returns NotImplemented. - internal static string GetContentType(WebResponse response) - { - string contentType = null; - try - { - contentType = response.ContentType; - } - catch (NotImplementedException) { } - - return contentType; - } - - internal static StringBuilder GetRawContentHeader(WebResponse baseResponse) - { - StringBuilder raw = new StringBuilder(); - - // add protocol and status line - string protocol = WebResponseHelper.GetProtocol(baseResponse); - if (!String.IsNullOrEmpty(protocol)) - { - int statusCode = WebResponseHelper.GetStatusCode(baseResponse); - string statusDescription = WebResponseHelper.GetStatusDescription(baseResponse); - raw.AppendFormat("{0} {1} {2}", protocol, statusCode, statusDescription); - raw.AppendLine(); - } - - // add headers - foreach (string key in baseResponse.Headers.AllKeys) - { - string value = baseResponse.Headers[key]; - raw.AppendFormat("{0}: {1}", key, value); - raw.AppendLine(); - } - - raw.AppendLine(); - return raw; - } - } -} \ No newline at end of file diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/FullClr/HtmlWebResponseObject.FullClr.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/FullClr/HtmlWebResponseObject.FullClr.cs deleted file mode 100644 index 807811ef75d..00000000000 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/FullClr/HtmlWebResponseObject.FullClr.cs +++ /dev/null @@ -1,65 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -using System; -using System.Management.Automation; -using System.Net; -using System.IO; -using System.Text; -using ExecutionContext = System.Management.Automation.ExecutionContext; - -namespace Microsoft.PowerShell.Commands -{ - /// - /// Response object for html content - /// - public partial class HtmlWebResponseObject : WebResponseObject, IDisposable - { - #region Constructors - - /// - /// Constructor for HtmlWebResponseObject - /// - /// - /// - internal HtmlWebResponseObject(WebResponse response, ExecutionContext executionContext) - : this(response, null, executionContext) - { } - - /// - /// Constructor for HtmlWebResponseObject with memory stream - /// - /// - /// - /// - internal HtmlWebResponseObject(WebResponse response, Stream contentStream, ExecutionContext executionContext) - : base(response, contentStream) - { - if (executionContext == null) - { - throw PSTraceSource.NewArgumentNullException("executionContext"); - } - - _executionContext = executionContext; - InitializeContent(); - InitializeRawContent(response); - } - - #endregion Constructors - - #region Methods - - private void InitializeRawContent(WebResponse baseResponse) - { - StringBuilder raw = ContentHelper.GetRawContentHeader(baseResponse); - if (null != Content) - { - raw.Append(Content); - } - this.RawContent = raw.ToString(); - } - - #endregion - } -} diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/FullClr/InvokeRestMethodCommand.FullClr.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/FullClr/InvokeRestMethodCommand.FullClr.cs deleted file mode 100644 index c07f151ab61..00000000000 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/FullClr/InvokeRestMethodCommand.FullClr.cs +++ /dev/null @@ -1,112 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -using System; -using System.Management.Automation; -using System.Net; -using System.Collections; -using System.IO; -using System.Xml; -using System.Text; - -namespace Microsoft.PowerShell.Commands -{ - /// - /// The Invoke-RestMethod command - /// This command makes an HTTP or HTTPS request to a web service, - /// and returns the response in an appropriate way. - /// Intended to work against the wide spectrum of "RESTful" web services - /// currently deployed across the web. - /// - [Cmdlet(VerbsLifecycle.Invoke, "RestMethod", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=217034", DefaultParameterSetName = "StandardMethod")] - public partial class InvokeRestMethodCommand : WebRequestPSCmdlet - { - #region Virtual Method Overrides - - /// - /// Process the web response and output corresponding objects. - /// - /// - internal override void ProcessResponse(WebResponse response) - { - if (null == response) { throw new ArgumentNullException("response"); } - - using (BufferingStreamReader responseStream = new BufferingStreamReader(StreamHelper.GetResponseStream(response))) - { - if (ShouldWriteToPipeline) - { - // First see if it is an RSS / ATOM feed, in which case we can - // stream it - unless the user has overridden it with a return type of "XML" - if (TryProcessFeedStream(responseStream)) - { - // Do nothing, content has been processed. - } - else - { - // determine the response type - RestReturnType returnType = CheckReturnType(response); - // get the response encoding - Encoding encoding = ContentHelper.GetEncoding(response); - - object obj = null; - Exception ex = null; - - string str = StreamHelper.DecodeStream(responseStream, encoding); - bool convertSuccess = false; - if (returnType == RestReturnType.Json) - { - convertSuccess = TryConvertToJson(str, out obj, ref ex) || TryConvertToXml(str, out obj, ref ex); - } - // default to try xml first since it's more common - else - { - convertSuccess = TryConvertToXml(str, out obj, ref ex) || TryConvertToJson(str, out obj, ref ex); - } - - if (!convertSuccess) - { - // fallback to string - obj = str; - } - - WriteObject(obj); - } - } - - if (ShouldSaveToOutFile) - { - StreamHelper.SaveStreamToFile(responseStream, QualifiedOutFile, this); - } - } - } - - #endregion Virtual Method Overrides - - #region Helper Methods - - private RestReturnType CheckReturnType(WebResponse response) - { - if (null == response) { throw new ArgumentNullException("response"); } - - RestReturnType rt = RestReturnType.Detect; - string contentType = ContentHelper.GetContentType(response); - if (string.IsNullOrEmpty(contentType)) - { - rt = RestReturnType.Detect; - } - else if (ContentHelper.IsJson(contentType)) - { - rt = RestReturnType.Json; - } - else if (ContentHelper.IsXml(contentType)) - { - rt = RestReturnType.Xml; - } - - return (rt); - } - - #endregion Helper Methods - } -} \ No newline at end of file diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/FullClr/InvokeWebRequestCommand.FullClr.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/FullClr/InvokeWebRequestCommand.FullClr.cs deleted file mode 100644 index cb5a5710783..00000000000 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/FullClr/InvokeWebRequestCommand.FullClr.cs +++ /dev/null @@ -1,58 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -using System; -using System.Management.Automation; -using System.Net; -using System.IO; - -namespace Microsoft.PowerShell.Commands -{ - /// - /// The Invoke-RestMethod command - /// This command makes an HTTP or HTTPS request to a web server and returns the results. - /// - [Cmdlet(VerbsLifecycle.Invoke, "WebRequest", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=217035", DefaultParameterSetName = "StandardMethod")] - public class InvokeWebRequestCommand : WebRequestPSCmdlet - { - #region Virtual Method Overrides - - /// - /// Process the web response and output corresponding objects. - /// - /// - internal override void ProcessResponse(WebResponse response) - { - if (null == response) { throw new ArgumentNullException("response"); } - - // check for Server Core, throws exception if -UseBasicParsing is not used - if (ShouldWriteToPipeline && (!UseBasicParsing)) - { - VerifyInternetExplorerAvailable(true); - } - - Stream responseStream = StreamHelper.GetResponseStream(response); - if (ShouldWriteToPipeline) - { - // creating a MemoryStream wrapper to response stream here to support IsStopping. - responseStream = new WebResponseContentMemoryStream(responseStream, StreamHelper.ChunkSize, this); - WebResponseObject ro = WebResponseObjectFactory.GetResponseObject(response, responseStream, this.Context, UseBasicParsing); - WriteObject(ro); - - // use the rawcontent stream from WebResponseObject for further - // processing of the stream. This is need because WebResponse's - // stream can be used only once. - responseStream = ro.RawContentStream; - responseStream.Seek(0, SeekOrigin.Begin); - } - - if (ShouldSaveToOutFile) - { - StreamHelper.SaveStreamToFile(responseStream, QualifiedOutFile, this); - } - } - - #endregion Virtual Method Overrides - } -} \ No newline at end of file diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/FullClr/JsonObjectTypeResolver.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/FullClr/JsonObjectTypeResolver.cs deleted file mode 100644 index 142b64c1d73..00000000000 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/FullClr/JsonObjectTypeResolver.cs +++ /dev/null @@ -1,29 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -using System; -using System.Collections.Generic; -using System.Web.Script.Serialization; - -namespace Microsoft.PowerShell.Commands -{ - internal class JsonObjectTypeResolver : JavaScriptTypeResolver - { - public override Type ResolveType(string id) - { - // TODO: this seems to work, but it's still a little suspect - // and probably worth gaining a little deeper understanding. - Type type = typeof(Dictionary); - return (type); - } - - /// - /// Override abstract methods - /// - public override string ResolveTypeId(Type type) - { - return (string.Empty); - } - } -} \ No newline at end of file diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/FullClr/WebRequestPSCmdlet.FullClr.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/FullClr/WebRequestPSCmdlet.FullClr.cs deleted file mode 100644 index c1b65ba3ee3..00000000000 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/FullClr/WebRequestPSCmdlet.FullClr.cs +++ /dev/null @@ -1,797 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -using System; -using System.Management.Automation; -using System.Net; -using System.IO; -using System.Text; -using System.Collections; -using System.Globalization; -using System.Security.Cryptography; -using System.Threading; -using System.Web; -using System.Xml; -using mshtml; - -namespace Microsoft.PowerShell.Commands -{ - /// - /// Base class for Invoke-RestMethod and Invoke-WebRequest commands. - /// - public abstract partial class WebRequestPSCmdlet : PSCmdlet - { - #region Abstract Methods - - /// - /// Read the supplied WebResponse object and push the - /// resulting output into the pipeline. - /// - /// Instance of a WebResponse object to be processed - internal abstract void ProcessResponse(WebResponse response); - - #endregion Abstract Methods - - /// - /// the WebRequest we will GetResponse for. - /// - private WebRequest _webRequest; - - #region Virtual Methods - - internal virtual WebRequest GetRequest(Uri uri) - { - uri = PrepareUri(uri); - // create the base WebRequest object - WebRequest request = WebRequest.Create(uri); - - // pull in session data - if (0 < WebSession.Headers.Count) - { - try - { - HttpWebRequest webRequest = request as HttpWebRequest; - request.Headers.Clear(); - foreach (string key in WebSession.Headers.Keys) - { - bool setHeaderViaProperty = TryMapHeaderToProperty(webRequest, key); - - if (!setHeaderViaProperty) - { - request.Headers[key] = WebSession.Headers[key]; - } - } - } - catch (NotImplementedException) - { - } - } - - // set the credentials used by this request - if (WebSession.UseDefaultCredentials) - { - // the UseDefaultCredentials flag overrides other supplied credentials - request.UseDefaultCredentials = true; - } - else if (null != WebSession.Credentials) - { - request.Credentials = WebSession.Credentials; - } - - if (NoProxy) - { - handler.UseProxy = false; - } - else if (WebSession.Proxy != null) - { - request.Proxy = WebSession.Proxy; - } - - switch (ParameterSetName) - { - case "StandardMethodNoProxy": - goto case "StandardMethod"; - case "StandardMethod": - if (WebRequestMethod.Default != Method) - { - // set the method if the parameter was provided - request.Method = Method.ToString().ToUpperInvariant(); - } - break; - case "CustomMethodNoProxy": - goto case "CustomMethod"; - case "CustomMethod": - // set the method if the parameter was provided - request.Method = CustomMethod.ToUpperInvariant(); - break; - } - - // pull in http specific properties - HttpWebRequest httpRequest = request as HttpWebRequest; - if (null != httpRequest) - { - httpRequest.CookieContainer = WebSession.Cookies; - httpRequest.UserAgent = WebSession.UserAgent; - - if (null != WebSession.Certificates) - { - httpRequest.ClientCertificates = WebSession.Certificates; - } - - if (-1 < WebSession.MaximumRedirection) - { - if (WebSession.MaximumRedirection == 0) - { - httpRequest.AllowAutoRedirect = false; - } - else - { - httpRequest.MaximumAutomaticRedirections = WebSession.MaximumRedirection; - } - } - - // check timeout setting (in seconds instead of milliseconds as in HttpWebRequest) - if (TimeoutSec == 0) - { - // A zero timeout means infinite - httpRequest.Timeout = Timeout.Infinite; - } - else if (TimeoutSec > 0) - { - // just to make sure - if (TimeoutSec > Int32.MaxValue / 1000) - { - httpRequest.Timeout = Int32.MaxValue; - } - else - { - httpRequest.Timeout = TimeoutSec * 1000; - } - } - - // check keep-alive setting - if (DisableKeepAlive) - { - // default value is true, so only need to set if false. - httpRequest.KeepAlive = false; - } - - if (null != TransferEncoding) - { - httpRequest.SendChunked = true; - httpRequest.TransferEncoding = TransferEncoding; - } - } - - return (request); - } - - private bool TryMapHeaderToProperty(HttpWebRequest webRequest, string key) - { - bool setHeaderViaProperty = false; - - // Perform header-to-property overrides - if (webRequest != null) - { - if (String.Equals("Accept", key, StringComparison.OrdinalIgnoreCase)) - { - webRequest.Accept = WebSession.Headers[key]; - setHeaderViaProperty = true; - } - - if (String.Equals("Connection", key, StringComparison.OrdinalIgnoreCase)) - { - webRequest.Connection = WebSession.Headers[key]; - setHeaderViaProperty = true; - } - - if (String.Equals("Content-Length", key, StringComparison.OrdinalIgnoreCase)) - { - webRequest.ContentLength = Convert.ToInt64(WebSession.Headers[key], CultureInfo.InvariantCulture); - setHeaderViaProperty = true; - } - - if (String.Equals("Content-Type", key, StringComparison.OrdinalIgnoreCase)) - { - webRequest.ContentType = WebSession.Headers[key]; - setHeaderViaProperty = true; - } - - if (String.Equals("Date", key, StringComparison.OrdinalIgnoreCase)) - { - webRequest.Date = DateTime.Parse(WebSession.Headers[key], CultureInfo.InvariantCulture); - setHeaderViaProperty = true; - } - - if (String.Equals("Expect", key, StringComparison.OrdinalIgnoreCase)) - { - webRequest.Expect = WebSession.Headers[key]; - setHeaderViaProperty = true; - } - - if (String.Equals("Host", key, StringComparison.OrdinalIgnoreCase)) - { - webRequest.Host = WebSession.Headers[key]; - setHeaderViaProperty = true; - } - - if (String.Equals("If-Modified-Since", key, StringComparison.OrdinalIgnoreCase)) - { - webRequest.IfModifiedSince = DateTime.Parse(WebSession.Headers[key], CultureInfo.InvariantCulture); - setHeaderViaProperty = true; - } - - if (String.Equals("Referer", key, StringComparison.OrdinalIgnoreCase)) - { - webRequest.Referer = WebSession.Headers[key]; - setHeaderViaProperty = true; - } - - if (String.Equals("Transfer-Encoding", key, StringComparison.OrdinalIgnoreCase)) - { - webRequest.SendChunked = true; - - if (String.Equals("Chunked", WebSession.Headers[key], StringComparison.OrdinalIgnoreCase)) - { - // .NET Doesn't support setting both the header and the property - webRequest.SendChunked = true; - } - else - { - webRequest.TransferEncoding = WebSession.Headers[key]; - } - - setHeaderViaProperty = true; - } - - if (String.Equals("User-Agent", key, StringComparison.OrdinalIgnoreCase)) - { - WebSession.UserAgent = WebSession.Headers[key]; - webRequest.UserAgent = WebSession.Headers[key]; - setHeaderViaProperty = true; - } - } - return setHeaderViaProperty; - } - - internal virtual void FillRequestStream(WebRequest request) - { - if (null == request) { throw new ArgumentNullException("request"); } - - // set the content type - if (null != ContentType) - { - request.ContentType = ContentType; - } - // ContentType == null - else if ((IsStandardMethodSet() && Method == WebRequestMethod.Post) - || (IsCustomMethodSet() && CustomMethod.ToUpperInvariant() == "POST")) - { - // Win8:545310 Invoke-WebRequest does not properly set MIME type for POST - if (String.IsNullOrEmpty(request.ContentType)) - { - request.ContentType = "application/x-www-form-urlencoded"; - } - } - - // coerce body into a usable form - if (null != Body) - { - object content = Body; - - // make sure we're using the base object of the body, not the PSObject wrapper - PSObject psBody = Body as PSObject; - if (null != psBody) - { - content = psBody.BaseObject; - } - - if (null != content as HtmlWebResponseObject) - { - HtmlWebResponseObject html = content as HtmlWebResponseObject; - // use the form it's the only one present - if (html.Forms.Count == 1) - { - SetRequestContent(request, html.Forms[0].Fields); - } - } - else if (null != content as FormObject) - { - FormObject form = content as FormObject; - SetRequestContent(request, form.Fields); - } - else if (null != content as IDictionary && request.Method != WebRequestMethods.Http.Get) - { - IDictionary dictionary = content as IDictionary; - SetRequestContent(request, dictionary); - } - else if (null != content as XmlNode) - { - XmlNode xmlNode = content as XmlNode; - SetRequestContent(request, xmlNode); - } - else if (null != content as Stream) - { - Stream stream = content as Stream; - SetRequestContent(request, stream); - } - else if (null != content as byte[]) - { - byte[] bytes = content as byte[]; - SetRequestContent(request, bytes); - } - else - { - SetRequestContent(request, - (string)LanguagePrimitives.ConvertTo(content, typeof(string), CultureInfo.InvariantCulture)); - } - } - else if (null != InFile) // copy InFile data - { - try - { - // open the input file - using (FileStream fs = new FileStream(InFile, FileMode.Open)) - { - SetRequestContent(request, fs); - } - } - catch (UnauthorizedAccessException) - { - string msg = string.Format(CultureInfo.InvariantCulture, WebCmdletStrings.AccessDenied, - _originalFilePath); - throw new UnauthorizedAccessException(msg); - } - } - else - { - request.ContentLength = 0; - } - } - - internal virtual WebResponse GetResponse(WebRequest request) - { - if (null == request) { throw new ArgumentNullException("request"); } - - // Construct TimeoutState - HttpWebRequest httpRequest = request as HttpWebRequest; - TimeoutState timeoutState = null; - if (httpRequest != null && httpRequest.Timeout > 0) - { - timeoutState = new TimeoutState(httpRequest); - } - - // Construct WebRequestState - _webRequest = request; - WebRequestState requestState = new WebRequestState(request); - - // Call asynchronous GetResponse - IAsyncResult asyncResult = (IAsyncResult)request.BeginGetResponse(new AsyncCallback(ResponseCallback), requestState); - // Set timeout if necessary - if (timeoutState != null) - { - ThreadPool.RegisterWaitForSingleObject(asyncResult.AsyncWaitHandle, new WaitOrTimerCallback(TimeoutCallback), timeoutState, timeoutState.httpRequest.Timeout, true); - } - - // Wait on signal - requestState.waithandle.WaitOne(-1, false); - requestState.waithandle.Close(); - _webRequest = null; - - // The current thread will be waked up in three cases: - // 1. the EngGetResponse is done. In this case, we EITHER get the response (requestState.response != null), - // OR a WebException is raised (requestState.webException != null). - // 2. the ^C is typed, a PipelineStoppedException is raised. StopProcessing will abort the request. In this - // case, there will be a WebException with status 'RequestCancelled'. - // 3. the time is up. The TimeoutCallback method will abort the request. In this case, there will also be a - // WebException with status 'RequestCancelled' and timeoutState.abort will be true. - if (requestState.webException != null) - { - // Case 3. We wrap the exception to be 'Timeout' WebException - if (timeoutState != null && timeoutState.abort && requestState.webException.Status.Equals(WebExceptionStatus.RequestCanceled)) - { - throw new WebException(WebCmdletStrings.RequestTimeout, WebExceptionStatus.Timeout); - } - - // Case 1 or 2 - throw requestState.webException; - } - - return (requestState.response); - } - - internal virtual void UpdateSession(WebResponse response) - { - if (null == response) { throw new ArgumentNullException("response"); } - - // process HTTP specific session data - HttpWebResponse httpResponse = response as HttpWebResponse; - if ((null != WebSession) && (null != httpResponse)) - { - // save response cookies into the session - WebSession.Cookies.Add(httpResponse.Cookies); - } - } - - #endregion Virtual Methods - - #region Overrides - - /// - /// the main execution method for cmdlets derived from WebRequestPSCmdlet. - /// - protected override void ProcessRecord() - { - try - { - // Set cmdlet context for write progress - ValidateParameters(); - PrepareSession(); - WebRequest request = GetRequest(Uri); - FillRequestStream(request); - - // Some web sites (e.g. Twitter) will return exception on POST when Expect100 is sent - // Default behaviour is continue to send body content anyway after a short period - // Here it send the two part as a whole. - ServicePointManager.Expect100Continue = false; - - try - { - string reqVerboseMsg = String.Format(CultureInfo.CurrentCulture, - "{0} {1} with {2}-byte payload", - request.Method, - request.RequestUri, - request.ContentLength); - WriteVerbose(reqVerboseMsg); - WebResponse response = GetResponse(request); - try - { - string contentType = ContentHelper.GetContentType(response); - string respVerboseMsg = String.Format(CultureInfo.CurrentCulture, - "received {0}-byte response of content type {1}", - response.ContentLength, - contentType); - WriteVerbose(respVerboseMsg); - ProcessResponse(response); - UpdateSession(response); - - // If we hit our maximum redirection count, generate an error. - // Errors with redirection counts of greater than 0 are handled automatically by .NET, but are - // impossible to detect programmatically when we hit this limit. By handling this ourselves - // (and still writing out the result), users can debug actual HTTP redirect problems. - HttpWebRequest httpRequest = request as HttpWebRequest; - if ((httpRequest != null) && (httpRequest.AllowAutoRedirect == false)) - { - HttpWebResponse webResponse = response as HttpWebResponse; - if ((webResponse.StatusCode == HttpStatusCode.Found) || - (webResponse.StatusCode == HttpStatusCode.Moved) || - webResponse.StatusCode == HttpStatusCode.MovedPermanently) - { - ErrorRecord er = new ErrorRecord(new InvalidOperationException(), "MaximumRedirectExceeded", ErrorCategory.InvalidOperation, httpRequest); - er.ErrorDetails = new ErrorDetails(WebCmdletStrings.MaximumRedirectionCountExceeded); - WriteError(er); - } - } - } - finally - { - // Choosing to close the stream instead of Dispose as the - // response object is being written to the output pipe. - if (response != null) - { - response.Close(); - } - } - } - catch (WebException ex) - { - WebException exThrown = ex; - string detailMsg = String.Empty; - try - { - if (ex.Response != null && ex.Response.ContentLength > 0) - { - Stream input = StreamHelper.GetResponseStream(ex.Response); - StreamReader reader = new StreamReader(input); - detailMsg = reader.ReadToEnd(); - - // If we were asked to not use the IE engine (or this is Invoke-RestMethod), use a simple - // regex replace to remove tags. - if (UseBasicParsing || (this is InvokeRestMethodCommand)) - { - detailMsg = System.Text.RegularExpressions.Regex.Replace(detailMsg, "<[^>]*>", ""); - } - else - { - // Otherwise, use IE to clean it up, as errors often come back as HTML - VerifyInternetExplorerAvailable(false); - - try - { - IHTMLDocument2 _parsedHtml = (IHTMLDocument2)new HTMLDocument(); - _parsedHtml.write(detailMsg); - detailMsg = _parsedHtml.body.outerText; - } - catch (System.Runtime.InteropServices.COMException) { } - } - } - } - // catch all - catch (Exception) - { - } - ErrorRecord er = new ErrorRecord(exThrown, "WebCmdletWebResponseException", ErrorCategory.InvalidOperation, request); - if (!String.IsNullOrEmpty(detailMsg)) - { - er.ErrorDetails = new ErrorDetails(detailMsg); - } - ThrowTerminatingError(er); - } - } - catch (CryptographicException ex) - { - ErrorRecord er = new ErrorRecord(ex, "WebCmdletCertificateException", ErrorCategory.SecurityError, null); - ThrowTerminatingError(er); - } - catch (NotSupportedException ex) - { - ErrorRecord er = new ErrorRecord(ex, "WebCmdletIEDomNotSupportedException", ErrorCategory.NotImplemented, null); - ThrowTerminatingError(er); - } - } - - /// - /// Implementing ^C, after start the BeginGetResponse - /// - protected override void StopProcessing() - { - if (_webRequest != null) - { - _webRequest.Abort(); - } - } - - #endregion Overrides - - #region Helper Methods - - /// - /// Call back method for BeginGetResponse - /// - /// - private static void ResponseCallback(IAsyncResult asyncResult) - { - WebRequestState myRequestState = (WebRequestState)asyncResult.AsyncState; - - try - { - myRequestState.response = myRequestState.request.EndGetResponse(asyncResult); - } - catch (WebException ex) - { - myRequestState.response = null; - myRequestState.webException = ex; - } - finally - { - myRequestState.waithandle.Set(); - } - } - - /// - /// Call back method for timeout - /// - /// - /// - private static void TimeoutCallback(object state, bool timeout) - { - if (timeout) - { - TimeoutState timeoutState = state as TimeoutState; - if (timeoutState != null) - { - timeoutState.abort = true; - timeoutState.httpRequest.Abort(); - } - } - } - - /// - /// Sets the ContentLength property of the request and writes the specified content to the request's RequestStream. - /// - /// The WebRequest who's content is to be set - /// A byte array containing the content data. - /// The number of bytes written to the requests RequestStream (and the new value of the request's ContentLength property - /// - /// Because this function sets the request's ContentLength property and writes content data into the requests's stream, - /// it should be called one time maximum on a given request. - /// - internal long SetRequestContent(WebRequest request, Byte[] content) - { - if (request == null) - throw new ArgumentNullException("request"); - - if (content != null) - { - if (request.ContentLength == 0) - { - request.ContentLength = content.Length; - } - StreamHelper.WriteToStream(content, request.GetRequestStream()); - } - else - { - request.ContentLength = 0; - } - - return request.ContentLength; - } - - /// - /// Sets the ContentLength property of the request and writes the specified content to the request's RequestStream. - /// - /// The WebRequest who's content is to be set - /// A String object containing the content data. - /// The number of bytes written to the requests RequestStream (and the new value of the request's ContentLength property - /// - /// Because this function sets the request's ContentLength property and writes content data into the requests's stream, - /// it should be called one time maximum on a given request. - /// - internal long SetRequestContent(WebRequest request, String content) - { - if (request == null) - throw new ArgumentNullException("request"); - - if (content != null) - { - Encoding encoding = null; - if (null != ContentType) - { - // If Content-Type contains the encoding format (as CharSet), use this encoding format - // to encode the Body of the WebRequest sent to the server. Default Encoding format - // would be used if Charset is not supplied in the Content-Type property. - System.Net.Mime.ContentType mimeContentType = new System.Net.Mime.ContentType(ContentType); - if (!String.IsNullOrEmpty(mimeContentType.CharSet)) - { - try - { - encoding = Encoding.GetEncoding(mimeContentType.CharSet); - } - catch (ArgumentException ex) - { - ErrorRecord er = new ErrorRecord(ex, "WebCmdletEncodingException", ErrorCategory.InvalidArgument, ContentType); - ThrowTerminatingError(er); - } - } - } - - Byte[] bytes = StreamHelper.EncodeToBytes(content, encoding); - - if (request.ContentLength == 0) - { - request.ContentLength = bytes.Length; - } - StreamHelper.WriteToStream(bytes, request.GetRequestStream()); - } - else - { - request.ContentLength = 0; - } - - return request.ContentLength; - } - - internal long SetRequestContent(WebRequest request, XmlNode xmlNode) - { - if (request == null) - throw new ArgumentNullException("request"); - - if (xmlNode != null) - { - Byte[] bytes = null; - XmlDocument doc = xmlNode as XmlDocument; - if (doc != null && (doc.FirstChild as XmlDeclaration) != null) - { - XmlDeclaration decl = doc.FirstChild as XmlDeclaration; - Encoding encoding = Encoding.GetEncoding(decl.Encoding); - bytes = StreamHelper.EncodeToBytes(doc.OuterXml, encoding); - } - else - { - bytes = StreamHelper.EncodeToBytes(xmlNode.OuterXml); - } - - if (request.ContentLength == 0) - { - request.ContentLength = bytes.Length; - } - StreamHelper.WriteToStream(bytes, request.GetRequestStream()); - } - else - { - request.ContentLength = 0; - } - - return request.ContentLength; - } - - /// - /// Sets the ContentLength property of the request and writes the specified content to the request's RequestStream. - /// - /// The WebRequest who's content is to be set - /// A Stream object containing the content data. - /// The number of bytes written to the requests RequestStream (and the new value of the request's ContentLength property - /// - /// Because this function sets the request's ContentLength property and writes content data into the requests's stream, - /// it should be called one time maximum on a given request. - /// - internal long SetRequestContent(WebRequest request, Stream contentStream) - { - if (request == null) - throw new ArgumentNullException("request"); - if (contentStream == null) - throw new ArgumentNullException("contentStream"); - - if (request.ContentLength == 0) - { - request.ContentLength = contentStream.Length; - } - - StreamHelper.WriteToStream(contentStream, request.GetRequestStream(), this); - - return request.ContentLength; - } - - internal long SetRequestContent(WebRequest request, IDictionary content) - { - if (request == null) - throw new ArgumentNullException("request"); - if (content == null) - throw new ArgumentNullException("content"); - - string body = FormatDictionary(content); - return (SetRequestContent(request, body)); - } - - #endregion Helper Methods - - #region private State class - - /// - /// The web request state is used when place asynchronous call to get response of a web request. - /// - private class WebRequestState - { - public WebRequest request; - public WebResponse response; - public ManualResetEvent waithandle; - public WebException webException; - - public WebRequestState(WebRequest webRequest) - { - request = webRequest; - response = null; - webException = null; - waithandle = new ManualResetEvent(false); - } - } - - /// - /// The timeout state is used when the request is a http request and need to timeout - /// - private class TimeoutState - { - public HttpWebRequest httpRequest; - public bool abort; - - public TimeoutState(HttpWebRequest request) - { - httpRequest = request; - abort = false; - } - } - - #endregion private State class - } -} \ No newline at end of file diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/FullClr/WebResponseHelper.FullClr.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/FullClr/WebResponseHelper.FullClr.cs deleted file mode 100644 index 8e654276015..00000000000 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/FullClr/WebResponseHelper.FullClr.cs +++ /dev/null @@ -1,72 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -using System; -using System.Net; -using System.Globalization; - -namespace Microsoft.PowerShell.Commands -{ - internal static partial class WebResponseHelper - { - internal static string GetCharacterSet(WebResponse response) - { - string characterSet = null; - - HttpWebResponse httpResponse = response as HttpWebResponse; - if (null != httpResponse) - { - characterSet = httpResponse.CharacterSet; - } - - return (characterSet); - } - - internal static string GetProtocol(WebResponse response) - { - string protocol = string.Empty; - - HttpWebResponse httpResponse = response as HttpWebResponse; - if (null != httpResponse) - { - protocol = string.Format(CultureInfo.InvariantCulture, - "HTTP/{0}", httpResponse.ProtocolVersion); - } - - return (protocol); - } - - internal static int GetStatusCode(WebResponse response) - { - int statusCode = 0; - - HttpWebResponse httpResponse = response as HttpWebResponse; - if (null != httpResponse) - { - statusCode = (int)httpResponse.StatusCode; - } - - return (statusCode); - } - - internal static string GetStatusDescription(WebResponse response) - { - string statusDescription = string.Empty; - - HttpWebResponse httpResponse = response as HttpWebResponse; - if (null != httpResponse) - { - statusDescription = httpResponse.StatusDescription; - } - - return (statusDescription); - } - - internal static bool IsText(WebResponse response) - { - string contentType = ContentHelper.GetContentType(response); - return (ContentHelper.IsText(contentType)); - } - } -} \ No newline at end of file diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/FullClr/WebResponseObject.FullClr.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/FullClr/WebResponseObject.FullClr.cs deleted file mode 100644 index ee9087c4623..00000000000 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/FullClr/WebResponseObject.FullClr.cs +++ /dev/null @@ -1,116 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -using System; -using System.Text; -using System.Net; -using System.Collections.Generic; -using System.IO; - -namespace Microsoft.PowerShell.Commands -{ - /// - /// WebResponseObject - /// - public partial class WebResponseObject - { - #region Properties - - /// - /// gets or sets the BaseResponse property - /// - public WebResponse BaseResponse { get; set; } - - /// - /// gets the Headers property - /// - public Dictionary Headers - { - get - { - Dictionary headers = new Dictionary(StringComparer.OrdinalIgnoreCase); - foreach (string key in BaseResponse.Headers.Keys) - { - headers[key] = BaseResponse.Headers[key]; - } - - return headers; - } - } - - #endregion - - #region Constructors - - /// - /// Constructor for WebResponseObject - /// - /// - public WebResponseObject(WebResponse response) - : this(response, null) - { } - - /// - /// Constructor for WebResponseObject with contentStream - /// - /// - /// - public WebResponseObject(WebResponse response, Stream contentStream) - { - SetResponse(response, contentStream); - InitializeContent(); - InitializeRawContent(response); - } - - #endregion Constructors - - #region Methods - - private void InitializeRawContent(WebResponse baseResponse) - { - StringBuilder raw = ContentHelper.GetRawContentHeader(baseResponse); - - // Use ASCII encoding for the RawContent visual view of the content. - if (Content.Length > 0) - { - raw.Append(this.ToString()); - } - - this.RawContent = raw.ToString(); - } - - private void SetResponse(WebResponse response, Stream contentStream) - { - if (null == response) { throw new ArgumentNullException("response"); } - - BaseResponse = response; - - MemoryStream ms = contentStream as MemoryStream; - if (null != ms) - { - _rawContentStream = ms; - } - else - { - Stream st = contentStream; - if (contentStream == null) - { - st = StreamHelper.GetResponseStream(response); - } - - long contentLength = response.ContentLength; - if (0 >= contentLength) - { - contentLength = StreamHelper.DefaultReadBuffer; - } - int initialCapacity = (int)Math.Min(contentLength, StreamHelper.DefaultReadBuffer); - _rawContentStream = new WebResponseContentMemoryStream(st, initialCapacity, null); - } - // set the position of the content stream to the beginning - _rawContentStream.Position = 0; - } - - #endregion - } -} diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/FullClr/WebResponseObjectFactory.FullClr.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/FullClr/WebResponseObjectFactory.FullClr.cs deleted file mode 100644 index 1d42838eedb..00000000000 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/FullClr/WebResponseObjectFactory.FullClr.cs +++ /dev/null @@ -1,35 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -using System; -using System.Net; -using System.IO; -using System.Management.Automation; - -namespace Microsoft.PowerShell.Commands -{ - internal static class WebResponseObjectFactory - { - internal static WebResponseObject GetResponseObject(WebResponse response, Stream responseStream, ExecutionContext executionContext, bool useBasicParsing = false) - { - WebResponseObject output; - if (WebResponseHelper.IsText(response)) - { - if (!useBasicParsing) - { - output = new HtmlWebResponseObject(response, responseStream, executionContext); - } - else - { - output = new BasicHtmlWebResponseObject(response, responseStream); - } - } - else - { - output = new WebResponseObject(response, responseStream); - } - return (output); - } - } -} \ No newline at end of file diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/JsonDateKind.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/JsonDateKind.cs new file mode 100644 index 00000000000..2fed27128a6 --- /dev/null +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/JsonDateKind.cs @@ -0,0 +1,38 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#nullable enable + +namespace Microsoft.PowerShell.Commands +{ + /// + /// Enums for ConvertFrom-Json -DateKind parameter. + /// + public enum JsonDateKind + { + /// + /// DateTime values are returned as a DateTime with the Kind representing the time zone in the raw string. + /// + Default, + + /// + /// DateTime values are returned as the Local kind representation of the value. + /// + Local, + + /// + /// DateTime values are returned as the UTC kind representation of the value. + /// + Utc, + + /// + /// DateTime values are returned as a DateTimeOffset value preserving the timezone information. + /// + Offset, + + /// + /// DateTime values are returned as raw strings. + /// + String, + } +} diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/JsonObject.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/JsonObject.cs index 28c443d4790..6506f2bd2ce 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/JsonObject.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/JsonObject.cs @@ -1,167 +1,329 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. using System; +using System.Collections; using System.Collections.Generic; -using System.Globalization; using System.Diagnostics.CodeAnalysis; +using System.Globalization; using System.Management.Automation; -using System.Text.RegularExpressions; -#if CORECLR +using System.Management.Automation.Language; +using System.Numerics; +using System.Reflection; +using System.Threading; + using Newtonsoft.Json; +using Newtonsoft.Json.Converters; using Newtonsoft.Json.Linq; -using System.Collections.ObjectModel; -using System.IO; -using System.Management.Automation.Internal; -using System.Reflection; -#else -using System.Web.Script.Serialization; -using System.Collections.Specialized; -#endif namespace Microsoft.PowerShell.Commands { /// - /// JsonObject class + /// JsonObject class. /// - [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", Justification = "Preferring Json over JSON")] public static class JsonObject { - private const int maxDepthAllowed = 100; + #region HelperTypes /// - /// Convert a Json string back to an object + /// Context for convert-to-json operation. /// - /// - /// - /// - [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] + public readonly struct ConvertToJsonContext + { + /// + /// Gets the maximum depth for walking the object graph. + /// + public readonly int MaxDepth; + + /// + /// Gets the cancellation token. + /// + public readonly CancellationToken CancellationToken; + + /// + /// Gets the StringEscapeHandling setting. + /// + public readonly StringEscapeHandling StringEscapeHandling; + + /// + /// Gets the EnumsAsStrings setting. + /// + public readonly bool EnumsAsStrings; + + /// + /// Gets the CompressOutput setting. + /// + public readonly bool CompressOutput; + + /// + /// Gets the target cmdlet that is doing the convert-to-json operation. + /// + public readonly PSCmdlet Cmdlet; + + /// + /// Initializes a new instance of the struct. + /// + /// The maximum depth to visit the object. + /// Indicates whether to use enum names for the JSON conversion. + /// Indicates whether to get the compressed output. + public ConvertToJsonContext(int maxDepth, bool enumsAsStrings, bool compressOutput) + : this(maxDepth, enumsAsStrings, compressOutput, StringEscapeHandling.Default, targetCmdlet: null, CancellationToken.None) + { + } + + /// + /// Initializes a new instance of the struct. + /// + /// The maximum depth to visit the object. + /// Indicates whether to use enum names for the JSON conversion. + /// Indicates whether to get the compressed output. + /// Specifies how strings are escaped when writing JSON text. + /// Specifies the cmdlet that is calling this method. + /// Specifies the cancellation token for cancelling the operation. + public ConvertToJsonContext( + int maxDepth, + bool enumsAsStrings, + bool compressOutput, + StringEscapeHandling stringEscapeHandling, + PSCmdlet targetCmdlet, + CancellationToken cancellationToken) + { + this.MaxDepth = maxDepth; + this.CancellationToken = cancellationToken; + this.StringEscapeHandling = stringEscapeHandling; + this.EnumsAsStrings = enumsAsStrings; + this.CompressOutput = compressOutput; + this.Cmdlet = targetCmdlet; + } + } + + private sealed class DuplicateMemberHashSet : HashSet + { + public DuplicateMemberHashSet(int capacity) + : base(capacity, StringComparer.OrdinalIgnoreCase) + { + } + } + + #endregion HelperTypes + + #region ConvertFromJson + + /// + /// Convert a Json string back to an object of type PSObject. + /// + /// The json text to convert. + /// An error record if the conversion failed. + /// A PSObject. + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", Justification = "Preferring Json over JSON")] public static object ConvertFromJson(string input, out ErrorRecord error) { - if (input == null) + return ConvertFromJson(input, returnHashtable: false, out error); + } + + /// + /// Convert a Json string back to an object of type or + /// depending on parameter . + /// + /// The json text to convert. + /// True if the result should be returned as a + /// instead of a + /// An error record if the conversion failed. + /// A or a + /// if the parameter is true. + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", Justification = "Preferring Json over JSON")] + public static object ConvertFromJson(string input, bool returnHashtable, out ErrorRecord error) + { + return ConvertFromJson(input, returnHashtable, maxDepth: 1024, out error); + } + + /// + /// Convert a JSON string back to an object of type or + /// depending on parameter . + /// + /// The JSON text to convert. + /// True if the result should be returned as a + /// instead of a . + /// The max depth allowed when deserializing the json input. Set to null for no maximum. + /// An error record if the conversion failed. + /// A or a + /// if the parameter is true. + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", Justification = "Preferring Json over JSON")] + public static object ConvertFromJson(string input, bool returnHashtable, int? maxDepth, out ErrorRecord error) + => ConvertFromJson(input, returnHashtable, maxDepth, jsonDateKind: JsonDateKind.Default, out error); + + /// + /// Convert a JSON string back to an object of type or + /// depending on parameter . + /// + /// The JSON text to convert. + /// True if the result should be returned as a + /// instead of a . + /// The max depth allowed when deserializing the json input. Set to null for no maximum. + /// Controls how DateTime values are to be converted. + /// An error record if the conversion failed. + /// A or a + /// if the parameter is true. + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", Justification = "Preferring Json over JSON")] + internal static object ConvertFromJson(string input, bool returnHashtable, int? maxDepth, JsonDateKind jsonDateKind, out ErrorRecord error) + { + ArgumentNullException.ThrowIfNull(input); + + DateParseHandling dateParseHandling; + DateTimeZoneHandling dateTimeZoneHandling; + switch (jsonDateKind) { - throw new ArgumentNullException("input"); + case JsonDateKind.Default: + dateParseHandling = DateParseHandling.DateTime; + dateTimeZoneHandling = DateTimeZoneHandling.RoundtripKind; + break; + + case JsonDateKind.Local: + dateParseHandling = DateParseHandling.DateTime; + dateTimeZoneHandling = DateTimeZoneHandling.Local; + break; + + case JsonDateKind.Utc: + dateParseHandling = DateParseHandling.DateTime; + dateTimeZoneHandling = DateTimeZoneHandling.Utc; + break; + + case JsonDateKind.Offset: + dateParseHandling = DateParseHandling.DateTimeOffset; + dateTimeZoneHandling = DateTimeZoneHandling.Unspecified; + break; + + case JsonDateKind.String: + dateParseHandling = DateParseHandling.None; + dateTimeZoneHandling = DateTimeZoneHandling.Unspecified; + break; + + default: + throw new ArgumentException($"Unknown JsonDateKind value requested '{jsonDateKind}'"); } error = null; -#if CORECLR - object obj = null; try { - // JsonConvert.DeserializeObject does not throw an exception when an invalid Json array is passed. - // This issue is being tracked by https://github.com/JamesNK/Newtonsoft.Json/issues/1321. - // To work around this, we need to identify when input is a Json array, and then try to parse it via JArray.Parse(). - - // If input starts with '[' (ignoring white spaces). - if ((Regex.Match(input, @"^\s*\[")).Success) - { - // JArray.Parse() will throw a JsonException if the array is invalid. - // This will be caught by the catch block below, and then throw an - // ArgumentException - this is done to have same behavior as the JavaScriptSerializer. - JArray.Parse(input); - - // Please note that if the Json array is valid, we don't do anything, - // we just continue the deserialization. - } + var obj = JsonConvert.DeserializeObject( + input, + new JsonSerializerSettings + { + DateParseHandling = dateParseHandling, + DateTimeZoneHandling = dateTimeZoneHandling, - obj = JsonConvert.DeserializeObject(input, new JsonSerializerSettings() { TypeNameHandling = TypeNameHandling.None, MaxDepth = 1024 }); + // This TypeNameHandling setting is required to be secure. + TypeNameHandling = TypeNameHandling.None, + MetadataPropertyHandling = MetadataPropertyHandling.Ignore, + MaxDepth = maxDepth + }); - // JObject is a IDictionary - var dictionary = obj as JObject; - if (dictionary != null) + switch (obj) { - obj = PopulateFromJDictionary(dictionary, out error); - } - else - { - // JArray is a collection - var list = obj as JArray; - if (list != null) - { - obj = PopulateFromJArray(list, out error); - } + case JObject dictionary: + // JObject is a IDictionary + return returnHashtable + ? PopulateHashTableFromJDictionary(dictionary, out error) + : PopulateFromJDictionary(dictionary, new DuplicateMemberHashSet(dictionary.Count), out error); + case JArray list: + return returnHashtable + ? PopulateHashTableFromJArray(list, out error) + : PopulateFromJArray(list, out error); + default: + return obj; } } catch (JsonException je) { var msg = string.Format(CultureInfo.CurrentCulture, WebCmdletStrings.JsonDeserializationFailed, je.Message); + // the same as JavaScriptSerializer does throw new ArgumentException(msg, je); } -#else - //In ConvertTo-Json, to serialize an object with a given depth, we set the RecursionLimit to depth + 2, see JavaScriptSerializer constructor in ConvertToJsonCommand.cs. - // Setting RecursionLimit to depth + 2 in order to support '$object | ConvertTo-Json –depth | ConvertFrom-Json'. - JavaScriptSerializer serializer = new JavaScriptSerializer(new JsonObjectTypeResolver()) { RecursionLimit = (maxDepthAllowed + 2) }; - serializer.MaxJsonLength = Int32.MaxValue; - object obj = serializer.DeserializeObject(input); - - if (obj is IDictionary) - { - var dictionary = obj as IDictionary; - obj = PopulateFromDictionary(dictionary, out error); - } - else if (obj is ICollection) - { - var list = obj as ICollection; - obj = PopulateFromList(list, out error); - } -#endif - return obj; } -#if CORECLR // This function is a clone of PopulateFromDictionary using JObject as an input. - private static PSObject PopulateFromJDictionary(JObject entries, out ErrorRecord error) + private static PSObject PopulateFromJDictionary(JObject entries, DuplicateMemberHashSet memberHashTracker, out ErrorRecord error) { error = null; - PSObject result = new PSObject(); + var result = new PSObject(entries.Count); foreach (var entry in entries) { - PSPropertyInfo property = result.Properties[entry.Key]; - if (property != null) + if (string.IsNullOrEmpty(entry.Key)) { - string errorMsg = string.Format(CultureInfo.InvariantCulture, - WebCmdletStrings.DuplicateKeysInJsonString, property.Name, entry.Key); + var errorMsg = string.Format(CultureInfo.CurrentCulture, WebCmdletStrings.EmptyKeyInJsonString); error = new ErrorRecord( new InvalidOperationException(errorMsg), - "DuplicateKeysInJsonString", + "EmptyKeyInJsonString", ErrorCategory.InvalidOperation, null); return null; } - // Array - else if (entry.Value is JArray) + // Case sensitive duplicates should normally not occur since JsonConvert.DeserializeObject + // does not throw when encountering duplicates and just uses the last entry. + if (memberHashTracker.TryGetValue(entry.Key, out var maybePropertyName) + && string.Equals(entry.Key, maybePropertyName, StringComparison.Ordinal)) { - JArray list = entry.Value as JArray; - ICollection listResult = PopulateFromJArray(list, out error); - if (error != null) - { - return null; - } - result.Properties.Add(new PSNoteProperty(entry.Key, listResult)); + var errorMsg = string.Format(CultureInfo.CurrentCulture, WebCmdletStrings.DuplicateKeysInJsonString, entry.Key); + error = new ErrorRecord( + new InvalidOperationException(errorMsg), + "DuplicateKeysInJsonString", + ErrorCategory.InvalidOperation, + null); + return null; } - // Dictionary - else if (entry.Value is JObject) + // Compare case insensitive to tell the user to use the -AsHashTable option instead. + // This is because PSObject cannot have keys with different casing. + if (memberHashTracker.TryGetValue(entry.Key, out var propertyName)) { - JObject dic = entry.Value as JObject; - PSObject dicResult = PopulateFromJDictionary(dic, out error); - if (error != null) - { - return null; - } - result.Properties.Add(new PSNoteProperty(entry.Key, dicResult)); + var errorMsg = string.Format(CultureInfo.CurrentCulture, WebCmdletStrings.KeysWithDifferentCasingInJsonString, propertyName, entry.Key); + error = new ErrorRecord( + new InvalidOperationException(errorMsg), + "KeysWithDifferentCasingInJsonString", + ErrorCategory.InvalidOperation, + null); + return null; } - // Value - else // (entry.Value is JValue) + switch (entry.Value) { - JValue theValue = entry.Value as JValue; - result.Properties.Add(new PSNoteProperty(entry.Key, theValue.Value)); + case JArray list: + { + // Array + var listResult = PopulateFromJArray(list, out error); + if (error != null) + { + return null; + } + + result.Properties.Add(new PSNoteProperty(entry.Key, listResult)); + break; + } + case JObject dic: + { + // Dictionary + var dicResult = PopulateFromJDictionary(dic, new DuplicateMemberHashSet(dic.Count), out error); + if (error != null) + { + return null; + } + + result.Properties.Add(new PSNoteProperty(entry.Key, dicResult)); + break; + } + case JValue value: + { + result.Properties.Add(new PSNoteProperty(entry.Key, value.Value)); + break; + } } + + memberHashTracker.Add(entry.Key); } + return result; } @@ -169,193 +331,520 @@ private static PSObject PopulateFromJDictionary(JObject entries, out ErrorRecord private static ICollection PopulateFromJArray(JArray list, out ErrorRecord error) { error = null; - List result = new List(); + var result = new object[list.Count]; + var i = 0; foreach (var element in list) { - // Array - if (element is JArray) + switch (element) { - JArray subList = element as JArray; - ICollection listResult = PopulateFromJArray(subList, out error); - if (error != null) - { - return null; - } - result.Add(listResult); + case JArray subList: + // Array + result[i++] = PopulateFromJArray(subList, out error); + if (error != null) + { + return null; + } + + break; + + case JObject dic: + // Dictionary + result[i++] = PopulateFromJDictionary(dic, new DuplicateMemberHashSet(dic.Count), out error); + if (error != null) + { + return null; + } + + break; + + case JValue value: + if (value.Type != JTokenType.Comment) + { + result[i++] = value.Value; + } + + break; } + } - // Dictionary - else if (element is JObject) + // In the common case of not having any comments, return the original array, otherwise create a sliced copy. + return i == list.Count ? result : result[..i]; + } + + // This function is a clone of PopulateFromDictionary using JObject as an input. + private static Hashtable PopulateHashTableFromJDictionary(JObject entries, out ErrorRecord error) + { + error = null; + OrderedHashtable result = new(entries.Count); + foreach (var entry in entries) + { + // Case sensitive duplicates should normally not occur since JsonConvert.DeserializeObject + // does not throw when encountering duplicates and just uses the last entry. + if (result.ContainsKey(entry.Key)) { - JObject dic = element as JObject; - PSObject dicResult = PopulateFromJDictionary(dic, out error); - if (error != null) - { - return null; - } - result.Add(dicResult); + string errorMsg = string.Format(CultureInfo.CurrentCulture, WebCmdletStrings.DuplicateKeysInJsonString, entry.Key); + error = new ErrorRecord( + new InvalidOperationException(errorMsg), + "DuplicateKeysInJsonString", + ErrorCategory.InvalidOperation, + null); + return null; } - // Value - else // (element is JValue) + switch (entry.Value) { - result.Add(((JValue)element).Value); + case JArray list: + { + // Array + var listResult = PopulateHashTableFromJArray(list, out error); + if (error != null) + { + return null; + } + + result.Add(entry.Key, listResult); + break; + } + case JObject dic: + { + // Dictionary + var dicResult = PopulateHashTableFromJDictionary(dic, out error); + if (error != null) + { + return null; + } + + result.Add(entry.Key, dicResult); + break; + } + case JValue value: + { + result.Add(entry.Key, value.Value); + break; + } } } - return result.ToArray(); + + return result; } - /// - /// Loads the Json.Net module to the given cmdlet execution context. - /// - /// - /// - [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly")] - public static void ImportJsonDotNetModule(Cmdlet cmdlet) + // This function is a clone of PopulateFromList using JArray as input. + private static ICollection PopulateHashTableFromJArray(JArray list, out ErrorRecord error) { - const string jsonDotNetAssemblyName = "Newtonsoft.Json, Version=7.0.0.0"; + error = null; + var result = new object[list.Count]; + var i = 0; - // Check if the Newtonsoft.Json.dll assembly is loaded. - try - { - System.Reflection.Assembly.Load(new AssemblyName(jsonDotNetAssemblyName)); - } - catch (System.IO.FileNotFoundException) + foreach (var element in list) { - // It is not, try to load it. - // Make sure that PSModuleAutoLoadingPreference is not set to 'None'. - PSModuleAutoLoadingPreference moduleAutoLoadingPreference = - CommandDiscovery.GetCommandDiscoveryPreference(cmdlet.Context, SpecialVariables.PSModuleAutoLoadingPreferenceVarPath, "PSModuleAutoLoadingPreference"); - if (moduleAutoLoadingPreference == PSModuleAutoLoadingPreference.None) + switch (element) { - cmdlet.ThrowTerminatingError(new ErrorRecord( - new NotSupportedException(WebCmdletStrings.PSModuleAutoloadingPreferenceNotEnable), - "PSModuleAutoloadingPreferenceNotEnable", - ErrorCategory.NotEnabled, - null)); - } + case JArray subList: + // Array + result[i++] = PopulateHashTableFromJArray(subList, out error); + if (error != null) + { + return null; + } - // Use module auto-loading to import Json.Net. - var jsonNetModulePath = Path.Combine(System.Environment.GetEnvironmentVariable("ProgramFiles"), @"WindowsPowerShell\Modules\Json.Net"); - CmdletInfo cmdletInfo = cmdlet.Context.SessionState.InvokeCommand.GetCmdlet("Microsoft.PowerShell.Core\\Import-Module"); - Exception exception; - Collection importedModule = CommandDiscovery.AutoloadSpecifiedModule(jsonNetModulePath, cmdlet.Context, cmdletInfo.Visibility, out exception); + break; - if ((importedModule == null) || (importedModule.Count == 0)) - { - string errorMessage = StringUtil.Format(WebCmdletStrings.JsonNetModuleRequired, WebCmdletStrings.CouldNotAutoImportJsonNetModule); + case JObject dic: + // Dictionary + result[i++] = PopulateHashTableFromJDictionary(dic, out error); + if (error != null) + { + return null; + } + + break; + + case JValue value: + if (value.Type != JTokenType.Comment) + { + result[i++] = value.Value; + } - cmdlet.ThrowTerminatingError(new ErrorRecord( - new NotSupportedException(errorMessage, exception), - "CouldNotAutoImportJsonNetModule", - ErrorCategory.InvalidOperation, - null)); + break; } + } - // Finally, ensure that the Newtonsoft.Json.dll assembly was loaded. - try + // In the common case of not having any comments, return the original array, otherwise create a sliced copy. + return i == list.Count ? result : result[..i]; + } + + #endregion ConvertFromJson + + #region ConvertToJson + + /// + /// Convert an object to JSON string. + /// + public static string ConvertToJson(object objectToProcess, in ConvertToJsonContext context) + { + try + { + // Pre-process the object so that it serializes the same, except that properties whose + // values cannot be evaluated are treated as having the value null. + _maxDepthWarningWritten = false; + object preprocessedObject = ProcessValue(objectToProcess, currentDepth: 0, in context); + var jsonSettings = new JsonSerializerSettings { - System.Reflection.Assembly.Load(new AssemblyName(jsonDotNetAssemblyName)); + // This TypeNameHandling setting is required to be secure. + TypeNameHandling = TypeNameHandling.None, + MaxDepth = 1024, + StringEscapeHandling = context.StringEscapeHandling + }; + + if (context.EnumsAsStrings) + { + jsonSettings.Converters.Add(new StringEnumConverter()); } - catch (System.IO.FileNotFoundException) + + if (!context.CompressOutput) { - string errorMessage = StringUtil.Format( - WebCmdletStrings.JsonNetModuleRequired, - StringUtil.Format(WebCmdletStrings.JsonNetModuleFilesRequired, jsonNetModulePath)); - - cmdlet.ThrowTerminatingError(new ErrorRecord( - new NotSupportedException(errorMessage), - "JsonNetModuleRequired", - ErrorCategory.NotInstalled, - null)); + jsonSettings.Formatting = Formatting.Indented; } + + return JsonConvert.SerializeObject(preprocessedObject, jsonSettings); + } + catch (OperationCanceledException) + { + return null; } } -#else - private static ICollection PopulateFromList(ICollection list, out ErrorRecord error) + + private static bool _maxDepthWarningWritten; + + /// + /// Return an alternate representation of the specified object that serializes the same JSON, except + /// that properties that cannot be evaluated are treated as having the value null. + /// Primitive types are returned verbatim. Aggregate types are processed recursively. + /// + /// The object to be processed. + /// The current depth into the object graph. + /// The context to use for the convert-to-json operation. + /// An object suitable for serializing to JSON. + private static object ProcessValue(object obj, int currentDepth, in ConvertToJsonContext context) { - error = null; - List result = new List(); + context.CancellationToken.ThrowIfCancellationRequested(); + + if (LanguagePrimitives.IsNull(obj)) + { + return null; + } - foreach (object element in list) + PSObject pso = obj as PSObject; + + if (pso != null) + { + obj = pso.BaseObject; + } + + object rv = obj; + bool isPurePSObj = false; + bool isCustomObj = false; + + if (obj == NullString.Value + || obj == DBNull.Value) + { + rv = null; + } + else if (obj is string + || obj is char + || obj is bool + || obj is DateTime + || obj is DateTimeOffset + || obj is Guid + || obj is Uri + || obj is double + || obj is float + || obj is decimal + || obj is BigInteger) + { + rv = obj; + } + else if (obj is Newtonsoft.Json.Linq.JObject jObject) { - if (element is IDictionary) + rv = jObject.ToObject>(); + } + else + { + Type t = obj.GetType(); + + if (t.IsPrimitive || (t.IsEnum && ExperimentalFeature.IsEnabled(ExperimentalFeature.PSSerializeJSONLongEnumAsNumber))) { - IDictionary dic = element as IDictionary; - PSObject dicResult = PopulateFromDictionary(dic, out error); - if (error != null) - { - return null; - } - result.Add(dicResult); + rv = obj; } - else if (element is ICollection) + else if (t.IsEnum) { - ICollection subList = element as ICollection; - ICollection listResult = PopulateFromList(subList, out error); - if (error != null) + // Win8:378368 Enums based on System.Int64 or System.UInt64 are not JSON-serializable + // because JavaScript does not support the necessary precision. + Type enumUnderlyingType = Enum.GetUnderlyingType(obj.GetType()); + if (enumUnderlyingType.Equals(typeof(long)) || enumUnderlyingType.Equals(typeof(ulong))) + { + rv = obj.ToString(); + } + else { - return null; + rv = obj; } - result.Add(listResult); } else { - result.Add(element); + if (currentDepth > context.MaxDepth) + { + if (!_maxDepthWarningWritten && context.Cmdlet != null) + { + _maxDepthWarningWritten = true; + string maxDepthMessage = string.Format( + CultureInfo.CurrentCulture, + WebCmdletStrings.JsonMaxDepthReached, + context.MaxDepth); + context.Cmdlet.WriteWarning(maxDepthMessage); + } + + if (pso != null && pso.ImmediateBaseObjectIsEmpty) + { + // The obj is a pure PSObject, we convert the original PSObject to a string, + // instead of its base object in this case + rv = LanguagePrimitives.ConvertTo(pso, typeof(string), + CultureInfo.InvariantCulture); + isPurePSObj = true; + } + else + { + rv = LanguagePrimitives.ConvertTo(obj, typeof(string), + CultureInfo.InvariantCulture); + } + } + else + { + if (obj is IDictionary dict) + { + rv = ProcessDictionary(dict, currentDepth, in context); + } + else + { + if (obj is IEnumerable enumerable) + { + rv = ProcessEnumerable(enumerable, currentDepth, in context); + } + else + { + rv = ProcessCustomObject(obj, currentDepth, in context); + isCustomObj = true; + } + } + } } } - return result.ToArray(); + rv = AddPsProperties(pso, rv, currentDepth, isPurePSObj, isCustomObj, in context); + + return rv; } - private static PSObject PopulateFromDictionary(IDictionary entries, out ErrorRecord error) + /// + /// Add to a base object any properties that might have been added to an object (via PSObject) through the Add-Member cmdlet. + /// + /// The containing PSObject, or null if the base object was not contained in a PSObject. + /// The base object that might have been decorated with additional properties. + /// The current depth into the object graph. + /// The processed object is a pure PSObject. + /// The processed object is a custom object. + /// The context for the operation. + /// + /// The original base object if no additional properties had been added, + /// otherwise a dictionary containing the value of the original base object in the "value" key + /// as well as the names and values of an additional properties. + /// + private static object AddPsProperties(object psObj, object obj, int depth, bool isPurePSObj, bool isCustomObj, in ConvertToJsonContext context) { - error = null; - PSObject result = new PSObject(); - foreach (KeyValuePair entry in entries) + if (psObj is not PSObject pso) + { + return obj; + } + + // when isPurePSObj is true, the obj is guaranteed to be a string converted by LanguagePrimitives + if (isPurePSObj) + { + return obj; + } + + bool wasDictionary = true; + + if (obj is not IDictionary dict) + { + wasDictionary = false; + dict = new Dictionary(); + dict.Add("value", obj); + } + + AppendPsProperties(pso, dict, depth, isCustomObj, in context); + + if (!wasDictionary && dict.Count == 1) + { + return obj; + } + + return dict; + } + + /// + /// Append to a dictionary any properties that might have been added to an object (via PSObject) through the Add-Member cmdlet. + /// If the passed in object is a custom object (not a simple object, not a dictionary, not a list, get processed in ProcessCustomObject method), + /// we also take Adapted properties into account. Otherwise, we only consider the Extended properties. + /// When the object is a pure PSObject, it also gets processed in "ProcessCustomObject" before reaching this method, so we will + /// iterate both extended and adapted properties for it. Since it's a pure PSObject, there will be no adapted properties. + /// + /// The containing PSObject, or null if the base object was not contained in a PSObject. + /// The dictionary to which any additional properties will be appended. + /// The current depth into the object graph. + /// The processed object is a custom object. + /// The context for the operation. + private static void AppendPsProperties(PSObject psObj, IDictionary receiver, int depth, bool isCustomObject, in ConvertToJsonContext context) + { + // if the psObj is a DateTime or String type, we don't serialize any extended or adapted properties + if (psObj.BaseObject is string || psObj.BaseObject is DateTime) + { + return; + } + + // serialize only Extended and Adapted properties.. + PSMemberInfoCollection srcPropertiesToSearch = + new PSMemberInfoIntegratingCollection(psObj, + isCustomObject ? PSObject.GetPropertyCollection(PSMemberViewTypes.Extended | PSMemberViewTypes.Adapted) : + PSObject.GetPropertyCollection(PSMemberViewTypes.Extended)); + + foreach (PSPropertyInfo prop in srcPropertiesToSearch) { - PSPropertyInfo property = result.Properties[entry.Key]; - if (property != null) + object value = null; + try + { + value = prop.Value; + } + catch (Exception) { - string errorMsg = string.Format(CultureInfo.InvariantCulture, - WebCmdletStrings.DuplicateKeysInJsonString, property.Name, entry.Key); - error = new ErrorRecord( - new InvalidOperationException(errorMsg), - "DuplicateKeysInJsonString", - ErrorCategory.InvalidOperation, - null); - return null; } - if (entry.Value is IDictionary) + if (!receiver.Contains(prop.Name)) { - IDictionary subEntries = entry.Value as IDictionary; - PSObject dicResult = PopulateFromDictionary(subEntries, out error); - if (error != null) + receiver[prop.Name] = ProcessValue(value, depth + 1, in context); + } + } + } + + /// + /// Return an alternate representation of the specified dictionary that serializes the same JSON, except + /// that any contained properties that cannot be evaluated are treated as having the value null. + /// + private static object ProcessDictionary(IDictionary dict, int depth, in ConvertToJsonContext context) + { + Dictionary result = new(dict.Count); + + foreach (DictionaryEntry entry in dict) + { + string name = entry.Key as string; + if (name == null) + { + // use the error string that matches the message from JavaScriptSerializer + string errorMsg = string.Format( + CultureInfo.CurrentCulture, + WebCmdletStrings.NonStringKeyInDictionary, + dict.GetType().FullName); + + var exception = new InvalidOperationException(errorMsg); + if (context.Cmdlet != null) { - return null; + var errorRecord = new ErrorRecord(exception, "NonStringKeyInDictionary", ErrorCategory.InvalidOperation, dict); + context.Cmdlet.ThrowTerminatingError(errorRecord); + } + else + { + throw exception; } - result.Properties.Add(new PSNoteProperty(entry.Key, dicResult)); } - else if (entry.Value is ICollection) + + result.Add(name, ProcessValue(entry.Value, depth + 1, in context)); + } + + return result; + } + + /// + /// Return an alternate representation of the specified collection that serializes the same JSON, except + /// that any contained properties that cannot be evaluated are treated as having the value null. + /// + private static object ProcessEnumerable(IEnumerable enumerable, int depth, in ConvertToJsonContext context) + { + List result = new(); + + foreach (object o in enumerable) + { + result.Add(ProcessValue(o, depth + 1, in context)); + } + + return result; + } + + /// + /// Return an alternate representation of the specified aggregate object that serializes the same JSON, except + /// that any contained properties that cannot be evaluated are treated as having the value null. + /// + /// The result is a dictionary in which all public fields and public gettable properties of the original object + /// are represented. If any exception occurs while retrieving the value of a field or property, that entity + /// is included in the output dictionary with a value of null. + /// + private static object ProcessCustomObject(object o, int depth, in ConvertToJsonContext context) + { + Dictionary result = new(); + Type t = o.GetType(); + + foreach (FieldInfo info in t.GetFields(BindingFlags.Public | BindingFlags.Instance)) + { + if (!info.IsDefined(typeof(T), true)) { - ICollection list = entry.Value as ICollection; - ICollection listResult = PopulateFromList(list, out error); - if (error != null) + object value; + try { - return null; + value = info.GetValue(o); } - result.Properties.Add(new PSNoteProperty(entry.Key, listResult)); + catch (Exception) + { + value = null; + } + + result.Add(info.Name, ProcessValue(value, depth + 1, in context)); } - else + } + + foreach (PropertyInfo info2 in t.GetProperties(BindingFlags.Public | BindingFlags.Instance)) + { + if (!info2.IsDefined(typeof(T), true)) { - result.Properties.Add(new PSNoteProperty(entry.Key, entry.Value)); + MethodInfo getMethod = info2.GetGetMethod(); + if ((getMethod != null) && (getMethod.GetParameters().Length == 0)) + { + object value; + try + { + value = getMethod.Invoke(o, Array.Empty()); + } + catch (Exception) + { + value = null; + } + + result.Add(info2.Name, ProcessValue(value, depth + 1, in context)); + } } } return result; } -#endif + + #endregion ConvertToJson } -} \ No newline at end of file +} diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/PSUserAgent.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/PSUserAgent.cs index da3ccefec59..1a19d6b0457 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/PSUserAgent.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/PSUserAgent.cs @@ -1,145 +1,92 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#nullable enable using System; -using System.Management.Automation; using System.Globalization; +using System.Management.Automation; +using System.Runtime.InteropServices; +using System.Text.RegularExpressions; namespace Microsoft.PowerShell.Commands { /// - /// Construct the Useragent string + /// Construct the Useragent string. /// public static class PSUserAgent { - internal static string UserAgent - { - get - { - // format the user-agent string from the various component parts - string userAgent = string.Format(CultureInfo.InvariantCulture, - "{0} ({1}; {2}; {3}) {4}", - Compatibility, Platform, OS, Culture, App); - return (userAgent); - } - } + private static string? s_windowsUserAgent; + + // Format the user-agent string from the various component parts + internal static string UserAgent => string.Create(CultureInfo.InvariantCulture, $"{Compatibility} ({PlatformName}; {OS}; {Culture}) {App}"); /// - /// Useragent string for InternetExplorer (9.0) + /// Useragent string for InternetExplorer (9.0). /// - public static string InternetExplorer - { - get - { - // format the user-agent string from the various component parts - string userAgent = string.Format(CultureInfo.InvariantCulture, - "{0} (compatible; MSIE 9.0; {1}; {2}; {3})", - Compatibility, Platform, OS, Culture); - return (userAgent); - } - } + public static string InternetExplorer => string.Create(CultureInfo.InvariantCulture, $"{Compatibility} (compatible; MSIE 9.0; {PlatformName}; {OS}; {Culture})"); /// - /// Useragent string for Firefox (4.0) + /// Useragent string for Firefox (4.0). /// - public static string FireFox - { - get - { - // format the user-agent string from the various component parts - string userAgent = string.Format(CultureInfo.InvariantCulture, - "{0} ({1}; {2}; {3}) Gecko/20100401 Firefox/4.0", - Compatibility, Platform, OS, Culture); - return (userAgent); - } - } + public static string FireFox => string.Create(CultureInfo.InvariantCulture, $"{Compatibility} ({PlatformName}; {OS}; {Culture}) Gecko/20100401 Firefox/4.0"); /// - /// Useragent string for Chrome (7.0) + /// Useragent string for Chrome (7.0). /// - public static string Chrome - { - get - { - // format the user-agent string from the various component parts - string userAgent = string.Format(CultureInfo.InvariantCulture, - "{0} ({1}; {2}; {3}) AppleWebKit/534.6 (KHTML, like Gecko) Chrome/7.0.500.0 Safari/534.6", - Compatibility, Platform, OS, Culture); - return (userAgent); - } - } + public static string Chrome => string.Create(CultureInfo.InvariantCulture, $"{Compatibility} ({PlatformName}; {OS}; {Culture}) AppleWebKit/534.6 (KHTML, like Gecko) Chrome/7.0.500.0 Safari/534.6"); /// - /// Useragent string for Opera (9.0) + /// Useragent string for Opera (9.0). /// - public static string Opera - { - get - { - // format the user-agent string from the various component parts - string userAgent = string.Format(CultureInfo.InvariantCulture, - "Opera/9.70 ({0}; {1}; {2}) Presto/2.2.1", - Platform, OS, Culture); - return (userAgent); - } - } + public static string Opera => string.Create(CultureInfo.InvariantCulture, $"Opera/9.70 ({PlatformName}; {OS}; {Culture}) Presto/2.2.1"); /// - /// Useragent string for Safari (5.0) + /// Useragent string for Safari (5.0). /// - public static string Safari - { - get - { - // format the user-agent string from the various component parts - string userAgent = string.Format(CultureInfo.InvariantCulture, - "{0} ({1}; {2}; {3}) AppleWebKit/533.16 (KHTML, like Gecko) Version/5.0 Safari/533.16", - Compatibility, Platform, OS, Culture); - return (userAgent); - } - } + public static string Safari => string.Create(CultureInfo.InvariantCulture, $"{Compatibility} ({PlatformName}; {OS}; {Culture}) AppleWebKit/533.16 (KHTML, like Gecko) Version/5.0 Safari/533.16"); - internal static string Compatibility - { - get - { - return ("Mozilla/5.0"); - } - } + internal static string Compatibility => "Mozilla/5.0"; - internal static string App - { - get - { - string app = string.Format(CultureInfo.InvariantCulture, - "WindowsPowerShell/{0}", PSVersionInfo.PSVersion); - return (app); - } - } + internal static string App => string.Create(CultureInfo.InvariantCulture, $"PowerShell/{PSVersionInfo.PSVersion}"); - internal static string Platform + internal static string PlatformName { get { - return ("Windows NT"); - } - } + if (Platform.IsWindows) + { + // Only generate the windows user agent once + if (s_windowsUserAgent is null) + { + // Find the version in the windows operating system description + Regex pattern = new(@"\d+(\.\d+)+"); + string versionText = pattern.Match(OS).Value; + Version windowsPlatformversion = new(versionText); + s_windowsUserAgent = $"Windows NT {windowsPlatformversion.Major}.{windowsPlatformversion.Minor}"; + } - internal static string OS - { - get - { - return System.Runtime.InteropServices.RuntimeInformation.OSDescription; + return s_windowsUserAgent; + } + else if (Platform.IsMacOS) + { + return "Macintosh"; + } + else if (Platform.IsLinux) + { + return "Linux"; + } + else + { + // Unknown/unsupported platform + Diagnostics.Assert(false, "Unable to determine Operating System Platform"); + return string.Empty; + } } } - internal static string Culture - { - get - { - return (CultureInfo.CurrentCulture.Name); - } - } + internal static string OS => RuntimeInformation.OSDescription.Trim(); + + internal static string Culture => CultureInfo.CurrentCulture.Name; } } diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/StreamHelper.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/StreamHelper.cs index cc4f0cacfcd..d24961834b6 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/StreamHelper.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/StreamHelper.cs @@ -1,20 +1,18 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#nullable enable using System; -using System.Text; -using System.Text.RegularExpressions; +using System.Buffers; using System.IO; -using System.IO.Compression; using System.Management.Automation; using System.Management.Automation.Internal; - -#if CORECLR using System.Net.Http; -#else -using System.Net; -#endif +using System.Text; +using System.Text.RegularExpressions; +using System.Threading; +using System.Threading.Tasks; namespace Microsoft.PowerShell.Commands { @@ -24,77 +22,52 @@ namespace Microsoft.PowerShell.Commands /// this class as a wrapper to MemoryStream to lazily initialize. Otherwise, the /// content will unnecessarily be read even if there are no consumers for it. /// - internal class WebResponseContentMemoryStream : MemoryStream + internal sealed class WebResponseContentMemoryStream : MemoryStream { #region Data - private Stream _originalStreamToProxy; + private readonly long? _contentLength; + private readonly Stream _originalStreamToProxy; + private readonly Cmdlet? _ownerCmdlet; + private readonly CancellationToken _cancellationToken; + private readonly TimeSpan _perReadTimeout; private bool _isInitialized = false; - private Cmdlet _ownerCmdlet; - #endregion + #endregion Data #region Constructors /// - /// + /// Initializes a new instance of the class. /// - /// - /// - /// Owner cmdlet if any - internal WebResponseContentMemoryStream(Stream stream, int initialCapacity, Cmdlet cmdlet) - : base(initialCapacity) - { + /// Response stream. + /// Presize the memory stream. + /// Owner cmdlet if any. + /// Expected download size in Bytes. + /// Time permitted between reads or Timeout.InfiniteTimeSpan for no timeout. + /// Cancellation token. + internal WebResponseContentMemoryStream(Stream stream, int initialCapacity, Cmdlet? cmdlet, long? contentLength, TimeSpan perReadTimeout, CancellationToken cancellationToken) : base(initialCapacity) + { + this._contentLength = contentLength; _originalStreamToProxy = stream; _ownerCmdlet = cmdlet; + _cancellationToken = cancellationToken; + _perReadTimeout = perReadTimeout; } - #endregion + #endregion Constructors /// - /// /// - public override bool CanRead - { - get - { - return true; - } - } + public override bool CanRead => true; /// - /// /// - public override bool CanSeek - { - get - { - return true; - } - } + public override bool CanSeek => true; /// - /// /// - public override bool CanTimeout - { - get - { - return base.CanTimeout; - } - } + public override bool CanWrite => true; /// - /// - /// - public override bool CanWrite - { - get - { - return true; - } - } - - /// - /// /// public override long Length { @@ -106,20 +79,18 @@ public override long Length } /// - /// /// /// /// /// /// - public override System.Threading.Tasks.Task CopyToAsync(Stream destination, int bufferSize, System.Threading.CancellationToken cancellationToken) + public override Task CopyToAsync(Stream destination, int bufferSize, CancellationToken cancellationToken) { - Initialize(); + Initialize(cancellationToken); return base.CopyToAsync(destination, bufferSize, cancellationToken); } /// - /// /// /// /// @@ -132,21 +103,19 @@ public override int Read(byte[] buffer, int offset, int count) } /// - /// /// /// /// /// /// /// - public override System.Threading.Tasks.Task ReadAsync(byte[] buffer, int offset, int count, System.Threading.CancellationToken cancellationToken) + public override Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) { - Initialize(); + Initialize(cancellationToken); return base.ReadAsync(buffer, offset, count, cancellationToken); } /// - /// /// /// public override int ReadByte() @@ -156,7 +125,6 @@ public override int ReadByte() } /// - /// /// /// public override void SetLength(long value) @@ -166,7 +134,6 @@ public override void SetLength(long value) } /// - /// /// /// public override byte[] ToArray() @@ -176,7 +143,6 @@ public override byte[] ToArray() } /// - /// /// /// /// @@ -188,21 +154,19 @@ public override void Write(byte[] buffer, int offset, int count) } /// - /// /// /// /// /// /// /// - public override System.Threading.Tasks.Task WriteAsync(byte[] buffer, int offset, int count, System.Threading.CancellationToken cancellationToken) + public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) { - Initialize(); + Initialize(cancellationToken); return base.WriteAsync(buffer, offset, count, cancellationToken); } /// - /// /// /// public override void WriteByte(byte value) @@ -212,7 +176,6 @@ public override void WriteByte(byte value) } /// - /// /// /// public override void WriteTo(Stream stream) @@ -222,67 +185,45 @@ public override void WriteTo(Stream stream) } /// - /// /// protected override void Dispose(bool disposing) { base.Dispose(disposing); } -#if !CORECLR - /// - /// - /// - public override IAsyncResult BeginRead(byte[] buffer, int offset, int count, AsyncCallback callback, object state) - { - Initialize(); - return base.BeginRead(buffer, offset, count, callback, state); - } - - /// - /// - /// - public override IAsyncResult BeginWrite(byte[] buffer, int offset, int count, AsyncCallback callback, object state) - { - Initialize(); - return base.BeginWrite(buffer, offset, count, callback, state); - } - - /// - /// - /// - public override byte[] GetBuffer() + private void Initialize(CancellationToken cancellationToken = default) { - Initialize(); - return base.GetBuffer(); - } + if (_isInitialized) + { + return; + } - /// - /// - /// - public override void Close() - { - base.Close(); - } -#endif + if (cancellationToken == default) + { + cancellationToken = _cancellationToken; + } - /// - /// - /// - private void Initialize() - { - if (_isInitialized) { return; } _isInitialized = true; try { - long totalLength = 0; + long totalRead = 0; byte[] buffer = new byte[StreamHelper.ChunkSize]; - ProgressRecord record = new ProgressRecord(StreamHelper.ActivityId, WebCmdletStrings.ReadResponseProgressActivity, "statusDescriptionPlaceholder"); - for (int read = 1; 0 < read; totalLength += read) + ProgressRecord record = new(StreamHelper.ActivityId, WebCmdletStrings.ReadResponseProgressActivity, "statusDescriptionPlaceholder"); + string totalDownloadSize = _contentLength is null ? "???" : Utils.DisplayHumanReadableFileSize((long)_contentLength); + for (int read = 1; read > 0; totalRead += read) { - if (null != _ownerCmdlet) + if (_ownerCmdlet is not null) { - record.StatusDescription = StringUtil.Format(WebCmdletStrings.ReadResponseProgressStatus, totalLength); + record.StatusDescription = StringUtil.Format( + WebCmdletStrings.ReadResponseProgressStatus, + Utils.DisplayHumanReadableFileSize(totalRead), + totalDownloadSize); + + if (_contentLength > 0) + { + record.PercentComplete = Math.Min((int)(totalRead * 100 / (long)_contentLength), 100); + } + _ownerCmdlet.WriteProgress(record); if (_ownerCmdlet.IsStopping) @@ -291,33 +232,111 @@ private void Initialize() } } - read = _originalStreamToProxy.Read(buffer, 0, buffer.Length); + read = _originalStreamToProxy.ReadAsync(buffer.AsMemory(), _perReadTimeout, cancellationToken).GetAwaiter().GetResult(); - if (0 < read) + if (read > 0) { base.Write(buffer, 0, read); } } - if (_ownerCmdlet != null) + if (_ownerCmdlet is not null) { - record.StatusDescription = StringUtil.Format(WebCmdletStrings.ReadResponseComplete, totalLength); + record.StatusDescription = StringUtil.Format(WebCmdletStrings.ReadResponseComplete, totalRead); record.RecordType = ProgressRecordType.Completed; _ownerCmdlet.WriteProgress(record); } - // make sure the length is set appropriately - base.SetLength(totalLength); - base.Seek(0, SeekOrigin.Begin); + // Make sure the length is set appropriately + base.SetLength(totalRead); + Seek(0, SeekOrigin.Begin); } catch (Exception) { - base.Dispose(); + Dispose(); throw; } } } + internal static class StreamTimeoutExtensions + { + internal static async Task ReadAsync(this Stream stream, Memory buffer, TimeSpan readTimeout, CancellationToken cancellationToken) + { + if (readTimeout == Timeout.InfiniteTimeSpan) + { + return await stream.ReadAsync(buffer, cancellationToken).ConfigureAwait(false); + } + + using var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); + try + { + cts.CancelAfter(readTimeout); + return await stream.ReadAsync(buffer, cts.Token).ConfigureAwait(false); + } + catch (TaskCanceledException ex) + { + if (cts.IsCancellationRequested) + { + throw new TimeoutException($"The request was canceled due to the configured OperationTimeout of {readTimeout.TotalSeconds} seconds elapsing", ex); + } + else + { + throw; + } + } + } + + internal static async Task CopyToAsync(this Stream source, Stream destination, TimeSpan perReadTimeout, CancellationToken cancellationToken) + { + if (perReadTimeout == Timeout.InfiniteTimeSpan) + { + // No timeout - use fast path + await source.CopyToAsync(destination, cancellationToken).ConfigureAwait(false); + return; + } + + byte[] buffer = ArrayPool.Shared.Rent(StreamHelper.ChunkSize); + CancellationTokenSource cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); + try + { + while (true) + { + if (!cts.TryReset()) + { + cts.Dispose(); + cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); + } + + cts.CancelAfter(perReadTimeout); + int bytesRead = await source.ReadAsync(buffer, cts.Token).ConfigureAwait(false); + if (bytesRead == 0) + { + break; + } + + await destination.WriteAsync(buffer.AsMemory(0, bytesRead), cancellationToken).ConfigureAwait(false); + } + } + catch (TaskCanceledException ex) + { + if (cts.IsCancellationRequested) + { + throw new TimeoutException($"The request was canceled due to the configured OperationTimeout of {perReadTimeout.TotalSeconds} seconds elapsing", ex); + } + else + { + throw; + } + } + finally + { + cts.Dispose(); + ArrayPool.Shared.Return(buffer); + } + } + } + internal static class StreamHelper { #region Constants @@ -326,75 +345,84 @@ internal static class StreamHelper internal const int ChunkSize = 10000; - // just picked a random number + // Just picked a random number internal const int ActivityId = 174593042; #endregion Constants #region Static Methods - internal static void WriteToStream(Stream input, Stream output, PSCmdlet cmdlet) + internal static void WriteToStream(Stream input, Stream output, PSCmdlet cmdlet, long? contentLength, TimeSpan perReadTimeout, CancellationToken cancellationToken) { - byte[] data = new byte[ChunkSize]; + ArgumentNullException.ThrowIfNull(cmdlet); - int read = 0; - long totalWritten = 0; - do - { - if (cmdlet != null) - { - ProgressRecord record = new ProgressRecord(ActivityId, - WebCmdletStrings.WriteRequestProgressActivity, - StringUtil.Format(WebCmdletStrings.WriteRequestProgressStatus, totalWritten)); - cmdlet.WriteProgress(record); - } + Task copyTask = input.CopyToAsync(output, perReadTimeout, cancellationToken); - read = input.Read(data, 0, ChunkSize); + bool wroteProgress = false; + ProgressRecord record = new( + ActivityId, + WebCmdletStrings.WriteRequestProgressActivity, + WebCmdletStrings.WriteRequestProgressStatus); + string totalDownloadSize = contentLength is null ? "???" : Utils.DisplayHumanReadableFileSize((long)contentLength); - if (0 < read) + try + { + while (!copyTask.Wait(1000, cancellationToken)) { - output.Write(data, 0, read); - totalWritten += read; - } - } while (read != 0); + record.StatusDescription = StringUtil.Format( + WebCmdletStrings.WriteRequestProgressStatus, + Utils.DisplayHumanReadableFileSize(output.Position), + totalDownloadSize); + if (contentLength > 0) + { + record.PercentComplete = Math.Min((int)(output.Position * 100 / (long)contentLength), 100); + } - if (cmdlet != null) + cmdlet.WriteProgress(record); + wroteProgress = true; + } + } + catch (OperationCanceledException) { - ProgressRecord record = new ProgressRecord(ActivityId, - WebCmdletStrings.WriteRequestProgressActivity, - StringUtil.Format(WebCmdletStrings.WriteRequestComplete, totalWritten)); - record.RecordType = ProgressRecordType.Completed; - cmdlet.WriteProgress(record); } - - output.Flush(); - } - - internal static void WriteToStream(byte[] input, Stream output) - { - output.Write(input, 0, input.Length); - output.Flush(); + finally + { + if (wroteProgress) + { + // Write out the completion progress record only if we did render the progress. + record.StatusDescription = StringUtil.Format( + copyTask.IsCompleted + ? WebCmdletStrings.WriteRequestComplete + : WebCmdletStrings.WriteRequestCancelled, + output.Position); + record.RecordType = ProgressRecordType.Completed; + cmdlet.WriteProgress(record); + } + } } /// /// Saves content from stream into filePath. /// Caller need to ensure position is properly set. /// - /// - /// - /// - internal static void SaveStreamToFile(Stream stream, string filePath, PSCmdlet cmdlet) + /// Input stream. + /// Output file name. + /// Current cmdlet (Invoke-WebRequest or Invoke-RestMethod). + /// Expected download size in Bytes. + /// Time permitted between reads or Timeout.InfiniteTimeSpan for no timeout. + /// CancellationToken to track the cmdlet cancellation. + internal static void SaveStreamToFile(Stream stream, string filePath, PSCmdlet cmdlet, long? contentLength, TimeSpan perReadTimeout, CancellationToken cancellationToken) { - using (FileStream output = File.Create(filePath)) - { - WriteToStream(stream, output, cmdlet); - } + // If the web cmdlet should resume, append the file instead of overwriting. + FileMode fileMode = cmdlet is WebRequestPSCmdlet webCmdlet && webCmdlet.ShouldResume ? FileMode.Append : FileMode.Create; + using FileStream output = new(filePath, fileMode, FileAccess.Write, FileShare.Read); + WriteToStream(stream, output, cmdlet, contentLength, perReadTimeout, cancellationToken); } - private static string StreamToString(Stream stream, Encoding encoding) + private static string StreamToString(Stream stream, Encoding encoding, TimeSpan perReadTimeout, CancellationToken cancellationToken) { - StringBuilder result = new StringBuilder(capacity: ChunkSize); + StringBuilder result = new(capacity: ChunkSize); Decoder decoder = encoding.GetDecoder(); int useBufferSize = 64; @@ -403,163 +431,126 @@ private static string StreamToString(Stream stream, Encoding encoding) useBufferSize = encoding.GetMaxCharCount(10); } - char[] chars = new char[useBufferSize]; - byte[] bytes = new byte[useBufferSize * 4]; - int bytesRead = 0; - do + char[] chars = ArrayPool.Shared.Rent(useBufferSize); + byte[] bytes = ArrayPool.Shared.Rent(useBufferSize * 4); + try { - // Read at most the number of bytes that will fit in the input buffer. The - // return value is the actual number of bytes read, or zero if no bytes remain. - bytesRead = stream.Read(bytes, 0, useBufferSize * 4); + int bytesRead = 0; + do + { + // Read at most the number of bytes that will fit in the input buffer. The + // return value is the actual number of bytes read, or zero if no bytes remain. + bytesRead = stream.ReadAsync(bytes.AsMemory(), perReadTimeout, cancellationToken).GetAwaiter().GetResult(); - bool completed = false; - int byteIndex = 0; - int bytesUsed; - int charsUsed; + bool completed = false; + int byteIndex = 0; - while (!completed) - { - // If this is the last input data, flush the decoder's internal buffer and state. - bool flush = (bytesRead == 0); - decoder.Convert(bytes, byteIndex, bytesRead - byteIndex, - chars, 0, useBufferSize, flush, - out bytesUsed, out charsUsed, out completed); - - // The conversion produced the number of characters indicated by charsUsed. Write that number - // of characters to our result buffer - result.Append(chars, 0, charsUsed); - - // Increment byteIndex to the next block of bytes in the input buffer, if any, to convert. - byteIndex += bytesUsed; + while (!completed) + { + // If this is the last input data, flush the decoder's internal buffer and state. + bool flush = bytesRead is 0; + decoder.Convert(bytes, byteIndex, bytesRead - byteIndex, chars, 0, useBufferSize, flush, out int bytesUsed, out int charsUsed, out completed); + + // The conversion produced the number of characters indicated by charsUsed. Write that number + // of characters to our result buffer + result.Append(chars, 0, charsUsed); + + // Increment byteIndex to the next block of bytes in the input buffer, if any, to convert. + byteIndex += bytesUsed; + + // The behavior of decoder.Convert changed start .NET 3.1-preview2. + // The change was made in https://github.com/dotnet/coreclr/pull/27229 + // The recommendation from .NET team is to not check for 'completed' if 'flush' is false. + // Break out of the loop if all bytes have been read. + if (!flush && bytesRead == byteIndex) + { + break; + } + } } - } while (bytesRead != 0); + while (bytesRead != 0); - return result.ToString(); - } - - internal static string DecodeStream(Stream stream, string characterSet, out Encoding encoding) - { - try - { - encoding = Encoding.GetEncoding(characterSet); + return result.ToString(); } - catch (ArgumentException) + finally { - encoding = null; + ArrayPool.Shared.Return(chars); + ArrayPool.Shared.Return(bytes); } - return DecodeStream(stream, ref encoding); } - static bool TryGetEncoding(string characterSet, out Encoding encoding) + internal static string DecodeStream(Stream stream, string? characterSet, out Encoding encoding, TimeSpan perReadTimeout, CancellationToken cancellationToken) { - bool result = false; - try - { - encoding = Encoding.GetEncoding(characterSet); - result = true; - } - catch (ArgumentException) + bool isDefaultEncoding = !TryGetEncoding(characterSet, out encoding); + + string content = StreamToString(stream, encoding, perReadTimeout, cancellationToken); + if (isDefaultEncoding) { - encoding = null; - } - return result; - } + // We only look within the first 1k characters as the meta element and + // the xml declaration are at the start of the document + string substring = content.Substring(0, Math.Min(content.Length, 1024)); - static readonly Regex s_metaexp = new Regex(@"<]*charset\s*=\s*[""'\n]?(?[A-Za-z].[^\s""'\n<>]*)[\s""'\n>]"); + // Check for a charset attribute on the meta element to override the default + Match match = s_metaRegex.Match(substring); - internal static string DecodeStream(Stream stream, ref Encoding encoding) - { - bool isDefaultEncoding = false; - if (null == encoding) - { - // Use the default encoding if one wasn't provided - encoding = ContentHelper.GetDefaultEncoding(); - isDefaultEncoding = true; - } + // Check for a encoding attribute on the xml declaration to override the default + if (!match.Success) + { + match = s_xmlRegex.Match(substring); + } - string content = StreamToString (stream, encoding); - if (isDefaultEncoding) do - { - // check for a charset attribute on the meta element to override the default. - Match match = s_metaexp.Match(content); if (match.Success) { - Encoding localEncoding = null; - string characterSet = match.Groups["charset"].Value; + characterSet = match.Groups["charset"].Value; - if (TryGetEncoding(characterSet, out localEncoding)) + if (TryGetEncoding(characterSet, out Encoding localEncoding)) { stream.Seek(0, SeekOrigin.Begin); - content = StreamToString(stream, localEncoding); - // report the encoding used. + content = StreamToString(stream, localEncoding, perReadTimeout, cancellationToken); encoding = localEncoding; } } - } while (false); + } return content; } - internal static Byte[] EncodeToBytes(String str, Encoding encoding) + internal static bool TryGetEncoding(string? characterSet, out Encoding encoding) { - if (null == encoding) + bool result = false; + try + { + encoding = Encoding.GetEncoding(characterSet!); + result = true; + } + catch (ArgumentException) { - // just use the default encoding if one wasn't provided + // Use the default encoding if one wasn't provided encoding = ContentHelper.GetDefaultEncoding(); } - return encoding.GetBytes(str); - } - - internal static Byte[] EncodeToBytes(String str) - { - return EncodeToBytes(str, null); + return result; } -#if CORECLR - internal static Stream GetResponseStream(HttpResponseMessage response) - { - Stream responseStream = response.Content.ReadAsStreamAsync().GetAwaiter().GetResult(); - var contentEncoding = response.Content.Headers.ContentEncoding; + private static readonly Regex s_metaRegex = new( + @"<]*charset\s*=\s*[""'\n]?(?[A-Za-z].[^\s""'\n<>]*)[\s""'\n>]", + RegexOptions.Compiled | RegexOptions.Singleline | RegexOptions.ExplicitCapture | RegexOptions.CultureInvariant | RegexOptions.IgnoreCase | RegexOptions.NonBacktracking + ); - // HttpClient by default will automatically decompress GZip and Deflate content. - // We keep this decompression logic here just in case. - if (contentEncoding != null && contentEncoding.Count > 0) - { - if (contentEncoding.Contains("gzip")) - { - responseStream = new GZipStream(responseStream, CompressionMode.Decompress); - } - else if (contentEncoding.Contains("deflate")) - { - responseStream = new DeflateStream(responseStream, CompressionMode.Decompress); - } - } + private static readonly Regex s_xmlRegex = new( + @"<\?xml\s.*[^.><]*encoding\s*=\s*[""'\n]?(?[A-Za-z].[^\s""'\n<>]*)[\s""'\n>]", + RegexOptions.Compiled | RegexOptions.Singleline | RegexOptions.ExplicitCapture | RegexOptions.CultureInvariant | RegexOptions.IgnoreCase | RegexOptions.NonBacktracking + ); - return responseStream; - } -#else - internal static Stream GetResponseStream(WebResponse response) + internal static byte[] EncodeToBytes(string str, Encoding encoding) { - Stream responseStream = response.GetResponseStream(); - - // See if it had a content-encoding, wrap in a decoding stream if so. - string contentEncoding = response.Headers["Content-Encoding"]; - if (contentEncoding != null) - { - if (contentEncoding.IndexOf("gzip", StringComparison.OrdinalIgnoreCase) >= 0) - { - responseStream = new GZipStream(responseStream, CompressionMode.Decompress); - } - else if (contentEncoding.IndexOf("deflate", StringComparison.OrdinalIgnoreCase) >= 0) - { - responseStream = new DeflateStream(responseStream, CompressionMode.Decompress); - } - } + // Just use the default encoding if one wasn't provided + encoding ??= ContentHelper.GetDefaultEncoding(); - return responseStream; + return encoding.GetBytes(str); } -#endif + + internal static Stream GetResponseStream(HttpResponseMessage response, CancellationToken cancellationToken) => response.Content.ReadAsStreamAsync(cancellationToken).GetAwaiter().GetResult(); #endregion Static Methods } diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/WebCmdletElementCollection.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/WebCmdletElementCollection.cs index 146a0707f05..99326898d9f 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/WebCmdletElementCollection.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/WebCmdletElementCollection.cs @@ -1,57 +1,45 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. +#nullable enable + +using System.Collections.Generic; using System.Collections.ObjectModel; using System.Management.Automation; -using System.Collections.Generic; namespace Microsoft.PowerShell.Commands { /// - /// WebCmdletElementCollection for elements in html web responses + /// WebCmdletElementCollection for elements in html web responses. /// public class WebCmdletElementCollection : ReadOnlyCollection { - internal WebCmdletElementCollection(IList list) - : base(list) + internal WebCmdletElementCollection(IList list) : base(list) { } /// - /// Finds the element with name or id + /// Finds the element with name or id. /// /// - /// - public PSObject Find(string nameOrId) - { - // try Id first - PSObject result = FindById(nameOrId) ?? FindByName(nameOrId); - - return (result); - } + /// Found element as PSObject. + public PSObject? Find(string nameOrId) => FindById(nameOrId) ?? FindByName(nameOrId); /// - /// Finds the element by id + /// Finds the element by id. /// /// - /// - public PSObject FindById(string id) - { - return Find(id, true); - } + /// Found element as PSObject. + public PSObject? FindById(string id) => Find(id, findById: true); /// - /// Finds the element by name + /// Finds the element by name. /// /// - /// - public PSObject FindByName(string name) - { - return Find(name, false); - } + /// Found element as PSObject. + public PSObject? FindByName(string name) => Find(name, findById: false); - private PSObject Find(string nameOrId, bool findById) + private PSObject? Find(string nameOrId, bool findById) { foreach (PSObject candidate in this) { diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/WebRequestMethod.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/WebRequestMethod.cs index 8dfe117b17d..9b90115a1d5 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/WebRequestMethod.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/WebRequestMethod.cs @@ -1,61 +1,62 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#nullable enable namespace Microsoft.PowerShell.Commands { /// - /// enums for web request method. + /// Enums for web request method. /// public enum WebRequestMethod { /// - /// Default method + /// Default method. /// Default, /// - /// GET method + /// GET method. /// Get, /// - /// HEAD method + /// HEAD method. /// Head, /// - /// POST method + /// POST method. /// Post, /// - /// PUT method + /// PUT method. /// Put, /// - /// DELETE method + /// DELETE method. /// Delete, /// - /// TRACE method + /// TRACE method. /// Trace, /// - /// OPTIONS method + /// OPTIONS method. /// Options, /// - /// MERGE method + /// MERGE method. /// Merge, /// - /// PATCH method + /// PATCH method. /// Patch, } diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/WebRequestSession.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/WebRequestSession.cs index 1f7a318ce48..efee6f3240e 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/WebRequestSession.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WebCmdlet/WebRequestSession.cs @@ -1,96 +1,163 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +#nullable enable using System; -using System.Net; using System.Collections.Generic; +using System.Net; +using System.Net.Http; +using System.Net.Sockets; +using System.Security.Authentication; using System.Security.Cryptography.X509Certificates; +using System.Threading; namespace Microsoft.PowerShell.Commands { /// /// WebRequestSession for holding session infos. /// - public class WebRequestSession + public class WebRequestSession : IDisposable { + #region Fields + + private HttpClient? _client; + private CookieContainer _cookies; + private bool _useDefaultCredentials; + private ICredentials? _credentials; + private X509CertificateCollection? _certificates; + private IWebProxy? _proxy; + private int _maximumRedirection; + private WebSslProtocol _sslProtocol; + private bool _allowAutoRedirect; + private bool _skipCertificateCheck; + private bool _noProxy; + private bool _disposed; + private TimeSpan _connectionTimeout; + private UnixDomainSocketEndPoint? _unixSocket; + + /// + /// Contains true if an existing HttpClient had to be disposed and recreated since the WebSession was last used. + /// + private bool _disposedClient; + + #endregion Fields + /// - /// gets or sets the Header property + /// Gets or sets the Header property. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")] public Dictionary Headers { get; set; } -#if CORECLR /// - /// gets or sets the content Headers when using HttpClient + /// Gets or sets the content Headers when using HttpClient. /// internal Dictionary ContentHeaders { get; set; } -#endif /// - /// gets or sets the Cookies property + /// Gets or sets the Cookies property. /// - public CookieContainer Cookies { get; set; } + public CookieContainer Cookies { get => _cookies; set => SetClassVar(ref _cookies, value); } #region Credentials /// - /// gets or sets the UseDefaultCredentials property + /// Gets or sets the UseDefaultCredentials property. /// - public bool UseDefaultCredentials { get; set; } + public bool UseDefaultCredentials { get => _useDefaultCredentials; set => SetStructVar(ref _useDefaultCredentials, value); } /// - /// gets or sets the Credentials property + /// Gets or sets the Credentials property. /// - public ICredentials Credentials { get; set; } + public ICredentials? Credentials { get => _credentials; set => SetClassVar(ref _credentials, value); } /// - /// gets or sets the Certificates property + /// Gets or sets the Certificates property. /// [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")] - public X509CertificateCollection Certificates { get; set; } + public X509CertificateCollection? Certificates { get => _certificates; set => SetClassVar(ref _certificates, value); } - #endregion + #endregion Credentials /// - /// gets or sets the UserAgent property + /// Gets or sets the UserAgent property. /// public string UserAgent { get; set; } /// - /// gets or sets the Proxy property + /// Gets or sets the Proxy property. + /// + public IWebProxy? Proxy + { + get => _proxy; + set + { + SetClassVar(ref _proxy, value); + if (_proxy is not null) + { + NoProxy = false; + } + } + } + + /// + /// Gets or sets the MaximumRedirection property. + /// + public int MaximumRedirection { get => _maximumRedirection; set => SetStructVar(ref _maximumRedirection, value); } + + /// + /// Gets or sets the count of retries for request failures. /// - public IWebProxy Proxy { get; set; } + public int MaximumRetryCount { get; set; } /// - /// gets or sets the RedirectMax property + /// Gets or sets the interval in seconds between retries. /// - public int MaximumRedirection { get; set; } + public int RetryIntervalInSeconds { get; set; } /// - /// Construct a new instance of a WebRequestSession object. + /// Initializes a new instance of the class. /// public WebRequestSession() { - // build the headers collection + // Build the headers collection Headers = new Dictionary(StringComparer.OrdinalIgnoreCase); -#if CORECLR ContentHeaders = new Dictionary(StringComparer.OrdinalIgnoreCase); -#endif - // build the cookie jar - Cookies = new CookieContainer(); + // Build the cookie jar + _cookies = new CookieContainer(); - // initialize the credential and certificate caches - UseDefaultCredentials = false; - Credentials = null; - Certificates = null; + // Initialize the credential and certificate caches + _useDefaultCredentials = false; + _credentials = null; + _certificates = null; - // setup the default UserAgent + // Setup the default UserAgent UserAgent = PSUserAgent.UserAgent; - Proxy = null; - MaximumRedirection = -1; + _proxy = null; + _maximumRedirection = -1; + _allowAutoRedirect = true; + } + + internal WebSslProtocol SslProtocol { set => SetStructVar(ref _sslProtocol, value); } + + internal bool SkipCertificateCheck { set => SetStructVar(ref _skipCertificateCheck, value); } + + internal TimeSpan ConnectionTimeout { set => SetStructVar(ref _connectionTimeout, value); } + + internal UnixDomainSocketEndPoint UnixSocket { set => SetClassVar(ref _unixSocket, value); } + + internal bool NoProxy + { + set + { + SetStructVar(ref _noProxy, value); + if (_noProxy) + { + Proxy = null; + } + } } /// @@ -99,11 +166,150 @@ public WebRequestSession() /// The certificate to be added. internal void AddCertificate(X509Certificate certificate) { - if (null == Certificates) + Certificates ??= new X509CertificateCollection(); + if (!Certificates.Contains(certificate)) + { + ResetClient(); + Certificates.Add(certificate); + } + } + + /// + /// Gets an existing or creates a new HttpClient for this WebRequest session if none currently exists (either because it was never + /// created, or because changes to the WebSession properties required the existing HttpClient to be disposed). + /// + /// True if the caller does not want the HttpClient to ever handle redirections automatically. + /// Contains true if an existing HttpClient had to be disposed and recreated since the WebSession was last used. + /// The HttpClient cached in the WebSession, based on all current settings. + internal HttpClient GetHttpClient(bool suppressHttpClientRedirects, out bool clientWasReset) + { + // Do not auto redirect if the caller does not want it, or maximum redirections is 0 + SetStructVar(ref _allowAutoRedirect, !(suppressHttpClientRedirects || MaximumRedirection == 0)); + + clientWasReset = _disposedClient; + + if (_client is null) + { + _client = CreateHttpClient(); + _disposedClient = false; + } + + return _client; + } + + private HttpClient CreateHttpClient() + { + SocketsHttpHandler handler = new(); + + if (_unixSocket is not null) + { + handler.ConnectCallback = async (context, token) => + { + Socket socket = new(AddressFamily.Unix, SocketType.Stream, ProtocolType.IP); + await socket.ConnectAsync(_unixSocket).ConfigureAwait(false); + + return new NetworkStream(socket, ownsSocket: false); + }; + } + + handler.CookieContainer = Cookies; + handler.AutomaticDecompression = DecompressionMethods.All; + + if (Credentials is not null) + { + handler.Credentials = Credentials; + } + else if (UseDefaultCredentials) + { + handler.Credentials = CredentialCache.DefaultCredentials; + } + + if (_noProxy) + { + handler.UseProxy = false; + } + else if (Proxy is not null) + { + handler.Proxy = Proxy; + } + + if (Certificates is not null) + { + handler.SslOptions.ClientCertificates = new X509CertificateCollection(Certificates); + } + + if (_skipCertificateCheck) + { + handler.SslOptions.RemoteCertificateValidationCallback = delegate { return true; }; + } + + handler.AllowAutoRedirect = _allowAutoRedirect; + if (_allowAutoRedirect && MaximumRedirection > 0) + { + handler.MaxAutomaticRedirections = MaximumRedirection; + } + + handler.SslOptions.EnabledSslProtocols = (SslProtocols)_sslProtocol; + + // Check timeout setting (in seconds) + return new HttpClient(handler) + { + Timeout = _connectionTimeout + }; + } + + private void SetClassVar(ref T oldValue, T newValue) where T : class? + { + if (oldValue != newValue) + { + ResetClient(); + oldValue = newValue; + } + } + + private void SetStructVar(ref T oldValue, T newValue) where T : struct + { + if (!oldValue.Equals(newValue)) + { + ResetClient(); + oldValue = newValue; + } + } + + private void ResetClient() + { + if (_client is not null) + { + _disposedClient = true; + _client.Dispose(); + _client = null; + } + } + + /// + /// Dispose the WebRequestSession. + /// + /// True when called from Dispose() and false when called from finalizer. + protected virtual void Dispose(bool disposing) + { + if (!_disposed) { - Certificates = new X509CertificateCollection(); + if (disposing) + { + _client?.Dispose(); + } + + _disposed = true; } - Certificates.Add(certificate); + } + + /// + /// Dispose the WebRequestSession. + /// + public void Dispose() + { + Dispose(disposing: true); + GC.SuppressFinalize(this); } } } diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Write-Object.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Write-Object.cs index b0c9fd70d0f..2d7fc9d233d 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Write-Object.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Write-Object.cs @@ -1,6 +1,5 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. using System.Management.Automation; @@ -8,58 +7,38 @@ namespace Microsoft.PowerShell.Commands { #region WriteOutputCommand /// - /// This class implements Write-output command - /// + /// This class implements Write-Output command. /// - [Cmdlet(VerbsCommunications.Write, "Output", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113427", RemotingCapability = RemotingCapability.None)] + [Cmdlet(VerbsCommunications.Write, "Output", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2097117", RemotingCapability = RemotingCapability.None)] public sealed class WriteOutputCommand : PSCmdlet { - private PSObject[] _inputObjects = null; - /// - /// Holds the list of objects to be Written + /// Holds the list of objects to be written. /// [Parameter(Position = 0, Mandatory = true, ValueFromPipeline = true, ValueFromRemainingArguments = true)] [AllowNull] [AllowEmptyCollection] - public PSObject[] InputObject - { - get { return _inputObjects; } - set { _inputObjects = value; } - } + public PSObject InputObject { get; set; } /// - /// Prevents Write-Output from unravelling collections passed to the InputObject - /// parameter. + /// Prevents Write-Output from unravelling collections passed to the InputObject parameter. /// - [Parameter()] - public SwitchParameter NoEnumerate - { - get; - set; - } + [Parameter] + public SwitchParameter NoEnumerate { get; set; } /// - /// This method implements the ProcessRecord method for Write-output command + /// This method implements the ProcessRecord method for Write-output command. /// protected override void ProcessRecord() { - if (null == _inputObjects) + if (InputObject == null) { - WriteObject(_inputObjects); + WriteObject(InputObject); return; } - bool enumerate = true; - if (NoEnumerate.IsPresent) - { - enumerate = false; - } - foreach (PSObject inputObject in _inputObjects) // compensate for ValueFromRemainingArguments - { - WriteObject(inputObject, enumerate); - } - }//processrecord - }//WriteOutputCommand + WriteObject(InputObject, !NoEnumerate.IsPresent); + } + } #endregion } diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Write.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Write.cs new file mode 100644 index 00000000000..d20d7d8712b --- /dev/null +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/Write.cs @@ -0,0 +1,463 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Management.Automation; +using System.Management.Automation.Internal; +using System.Runtime.Serialization; + +namespace Microsoft.PowerShell.Commands +{ + #region WriteDebugCommand + /// + /// This class implements Write-Debug command. + /// + [Cmdlet(VerbsCommunications.Write, "Debug", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2097132", RemotingCapability = RemotingCapability.None)] + public sealed class WriteDebugCommand : PSCmdlet + { + /// + /// Message to be sent and processed if debug mode is on. + /// + [Parameter(Position = 0, Mandatory = true, ValueFromPipeline = true)] + [AllowEmptyString] + [Alias("Msg")] + public string Message { get; set; } + + /// + /// This method implements the ProcessRecord method for Write-Debug command. + /// + protected override void ProcessRecord() + { + // + // The write-debug command must use the script's InvocationInfo rather than its own, + // so we create the DebugRecord here and fill it up with the appropriate InvocationInfo; + // then, we call the command runtime directly and pass this record to WriteDebug(). + // + if (this.CommandRuntime is MshCommandRuntime mshCommandRuntime) + { + DebugRecord record = new(Message); + + if (GetVariableValue(SpecialVariables.MyInvocation) is InvocationInfo invocationInfo) + { + record.SetInvocationInfo(invocationInfo); + } + + mshCommandRuntime.WriteDebug(record); + } + else + { + WriteDebug(Message); + } + } + } + #endregion WriteDebugCommand + + #region WriteVerboseCommand + /// + /// This class implements Write-Verbose command. + /// + [Cmdlet(VerbsCommunications.Write, "Verbose", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2097043", RemotingCapability = RemotingCapability.None)] + public sealed class WriteVerboseCommand : PSCmdlet + { + /// + /// Message to be sent if verbose messages are being shown. + /// + [Parameter(Position = 0, Mandatory = true, ValueFromPipeline = true)] + [AllowEmptyString] + [Alias("Msg")] + public string Message { get; set; } + + /// + /// This method implements the ProcessRecord method for Write-verbose command. + /// + protected override void ProcessRecord() + { + // + // The write-verbose command must use the script's InvocationInfo rather than its own, + // so we create the VerboseRecord here and fill it up with the appropriate InvocationInfo; + // then, we call the command runtime directly and pass this record to WriteVerbose(). + // + if (this.CommandRuntime is MshCommandRuntime mshCommandRuntime) + { + VerboseRecord record = new(Message); + + if (GetVariableValue(SpecialVariables.MyInvocation) is InvocationInfo invocationInfo) + { + record.SetInvocationInfo(invocationInfo); + } + + mshCommandRuntime.WriteVerbose(record); + } + else + { + WriteVerbose(Message); + } + } + } + #endregion WriteVerboseCommand + + #region WriteWarningCommand + /// + /// This class implements Write-Warning command. + /// + [Cmdlet(VerbsCommunications.Write, "Warning", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2097044", RemotingCapability = RemotingCapability.None)] + public sealed class WriteWarningCommand : PSCmdlet + { + /// + /// Message to be sent if warning messages are being shown. + /// + [Parameter(Position = 0, Mandatory = true, ValueFromPipeline = true)] + [AllowEmptyString] + [Alias("Msg")] + public string Message { get; set; } + + /// + /// This method implements the ProcessRecord method for Write-Warning command. + /// + protected override void ProcessRecord() + { + // + // The write-warning command must use the script's InvocationInfo rather than its own, + // so we create the WarningRecord here and fill it up with the appropriate InvocationInfo; + // then, we call the command runtime directly and pass this record to WriteWarning(). + // + if (this.CommandRuntime is MshCommandRuntime mshCommandRuntime) + { + WarningRecord record = new(Message); + + if (GetVariableValue(SpecialVariables.MyInvocation) is InvocationInfo invocationInfo) + { + record.SetInvocationInfo(invocationInfo); + } + + mshCommandRuntime.WriteWarning(record); + } + else + { + WriteWarning(Message); + } + } + } + #endregion WriteWarningCommand + + #region WriteInformationCommand + /// + /// This class implements Write-Information command. + /// + [Cmdlet(VerbsCommunications.Write, "Information", HelpUri = "https://go.microsoft.com/fwlink/?LinkId=2097040", RemotingCapability = RemotingCapability.None)] + public sealed class WriteInformationCommand : PSCmdlet + { + /// + /// Object to be sent to the Information stream. + /// + [Parameter(Position = 0, Mandatory = true, ValueFromPipeline = true)] + [Alias("Msg", "Message")] + [AllowNull] + public object MessageData { get; set; } + + /// + /// Any tags to be associated with this information. + /// + [Parameter(Position = 1)] + public string[] Tags { get; set; } + + /// + /// This method implements the processing of the Write-Information command. + /// + protected override void BeginProcessing() + { + if (Tags != null) + { + foreach (string tag in Tags) + { + if (tag.StartsWith("PS", StringComparison.OrdinalIgnoreCase)) + { + ErrorRecord er = new( + new InvalidOperationException(StringUtil.Format(UtilityCommonStrings.PSPrefixReservedInInformationTag, tag)), + "PSPrefixReservedInInformationTag", ErrorCategory.InvalidArgument, tag); + ThrowTerminatingError(er); + } + } + } + } + + /// + /// This method implements the ProcessRecord method for Write-Information command. + /// + protected override void ProcessRecord() + { + WriteInformation(MessageData, Tags); + } + } + + #endregion WriteInformationCommand + + #region WriteOrThrowErrorCommand + + /// + /// This class implements the Write-Error command. + /// + public class WriteOrThrowErrorCommand : PSCmdlet + { + /// + /// ErrorRecord.Exception -- if not specified, ErrorRecord.Exception is System.Exception. + /// + [Parameter(Position = 0, ParameterSetName = "WithException", Mandatory = true)] + public Exception Exception { get; set; } + + /// + /// If Exception is specified, this is ErrorRecord.ErrorDetails.Message; + /// otherwise, the Exception is System.Exception, and this is Exception.Message. + /// + [Parameter(Position = 0, ParameterSetName = "NoException", Mandatory = true, ValueFromPipeline = true)] + [Parameter(ParameterSetName = "WithException")] + [AllowNull] + [AllowEmptyString] + [Alias("Msg")] + public string Message { get; set; } + + /// + /// If Exception is specified, this is ErrorRecord.ErrorDetails.Message; + /// otherwise, the Exception is System.Exception, and this is Exception.Message. + /// + [Parameter(Position = 0, ParameterSetName = "ErrorRecord", Mandatory = true)] + public ErrorRecord ErrorRecord { get; set; } + + /// + /// ErrorRecord.CategoryInfo.Category. + /// + [Parameter(ParameterSetName = "NoException")] + [Parameter(ParameterSetName = "WithException")] + public ErrorCategory Category { get; set; } = ErrorCategory.NotSpecified; + + /// + /// ErrorRecord.ErrorId. + /// + [Parameter(ParameterSetName = "NoException")] + [Parameter(ParameterSetName = "WithException")] + public string ErrorId { get; set; } = string.Empty; + + /// + /// ErrorRecord.TargetObject. + /// + [Parameter(ParameterSetName = "NoException")] + [Parameter(ParameterSetName = "WithException")] + public object TargetObject { get; set; } + + /// + /// ErrorRecord.ErrorDetails.RecommendedAction. + /// + [Parameter] + public string RecommendedAction { get; set; } = string.Empty; + + /* 2005/01/25 removing throw-error + /// + /// If true, this is throw-error. Otherwise, this is write-error. + /// + internal bool _terminating = false; + */ + + /// + /// ErrorRecord.CategoryInfo.Activity. + /// + [Parameter] + [Alias("Activity")] + public string CategoryActivity { get; set; } = string.Empty; + + /// + /// ErrorRecord.CategoryInfo.Reason. + /// + [Parameter] + [Alias("Reason")] + public string CategoryReason { get; set; } = string.Empty; + + /// + /// ErrorRecord.CategoryInfo.TargetName. + /// + [Parameter] + [Alias("TargetName")] + public string CategoryTargetName { get; set; } = string.Empty; + + /// + /// ErrorRecord.CategoryInfo.TargetType. + /// + [Parameter] + [Alias("TargetType")] + public string CategoryTargetType { get; set; } = string.Empty; + + /// + /// Write an error to the output pipe, or throw a terminating error. + /// + protected override void ProcessRecord() + { + ErrorRecord errorRecord = this.ErrorRecord; + if (errorRecord != null) + { + // copy constructor + errorRecord = new ErrorRecord(errorRecord, null); + } + else + { + Exception e = this.Exception; + string msg = Message; + e ??= new WriteErrorException(msg); + + string errid = ErrorId; + if (string.IsNullOrEmpty(errid)) + { + errid = e.GetType().FullName; + } + + errorRecord = new ErrorRecord( + e, + errid, + Category, + TargetObject + ); + + if (this.Exception != null && !string.IsNullOrEmpty(msg)) + { + errorRecord.ErrorDetails = new ErrorDetails(msg); + } + } + + string recact = RecommendedAction; + if (!string.IsNullOrEmpty(recact)) + { + errorRecord.ErrorDetails ??= new ErrorDetails(errorRecord.ToString()); + + errorRecord.ErrorDetails.RecommendedAction = recact; + } + + if (!string.IsNullOrEmpty(CategoryActivity)) + errorRecord.CategoryInfo.Activity = CategoryActivity; + if (!string.IsNullOrEmpty(CategoryReason)) + errorRecord.CategoryInfo.Reason = CategoryReason; + if (!string.IsNullOrEmpty(CategoryTargetName)) + errorRecord.CategoryInfo.TargetName = CategoryTargetName; + if (!string.IsNullOrEmpty(CategoryTargetType)) + errorRecord.CategoryInfo.TargetType = CategoryTargetType; + + /* 2005/01/25 removing throw-error + if (_terminating) + { + ThrowTerminatingError(errorRecord); + } + else + { + */ + + // 2005/07/14-913791 "write-error output is confusing and misleading" + // set InvocationInfo to the script not the command + if (GetVariableValue(SpecialVariables.MyInvocation) is InvocationInfo myInvocation) + { + errorRecord.SetInvocationInfo(myInvocation); + errorRecord.PreserveInvocationInfoOnce = true; + if (!string.IsNullOrEmpty(CategoryActivity)) + errorRecord.CategoryInfo.Activity = CategoryActivity; + else + errorRecord.CategoryInfo.Activity = "Write-Error"; + } + + WriteError(errorRecord); + /* + } + */ + } + } + + /// + /// This class implements Write-Error command. + /// + [Cmdlet(VerbsCommunications.Write, "Error", DefaultParameterSetName = "NoException", + HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2097039", RemotingCapability = RemotingCapability.None)] + public sealed class WriteErrorCommand : WriteOrThrowErrorCommand + { + /// + /// Initializes a new instance of the class. + /// + public WriteErrorCommand() + { + } + } + + /* 2005/01/25 removing throw-error + /// + /// This class implements Write-Error command. + /// + [Cmdlet("Throw", "Error", DefaultParameterSetName = "NoException")] + public sealed class ThrowErrorCommand : WriteOrThrowErrorCommand + { + /// + /// Constructor. + /// + public ThrowErrorCommand() + { + using (tracer.TraceConstructor(this)) + { + _terminating = true; + } + } + } + */ + + #endregion WriteOrThrowErrorCommand + + #region WriteErrorException + /// + /// The write-error cmdlet uses WriteErrorException + /// when the user only specifies a string and not + /// an Exception or ErrorRecord. + /// + public class WriteErrorException : SystemException + { + #region ctor + /// + /// Initializes a new instance of the class. + /// + /// Constructed object. + public WriteErrorException() + : base(StringUtil.Format(WriteErrorStrings.WriteErrorException)) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// + /// Constructed object. + public WriteErrorException(string message) + : base(message) + { + } + + /// + /// Initializes a new instance of the class. + /// + /// + /// + /// Constructed object. + public WriteErrorException(string message, + Exception innerException) + : base(message, innerException) + { + } + #endregion ctor + + #region Serialization + /// + /// Initializes a new instance of the class for serialization. + /// + /// Serialization information. + /// Streaming context. + /// Constructed object. + [Obsolete("Legacy serialization support is deprecated since .NET 8", DiagnosticId = "SYSLIB0051")] + protected WriteErrorException(SerializationInfo info, + StreamingContext context) + { + throw new NotSupportedException(); + } + #endregion Serialization + } + #endregion WriteErrorException +} diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WriteAliasCommandBase.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WriteAliasCommandBase.cs index 7320cda732c..31670935fcf 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WriteAliasCommandBase.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WriteAliasCommandBase.cs @@ -1,56 +1,46 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. -using System; using System.Management.Automation; -using Dbg = System.Management.Automation; namespace Microsoft.PowerShell.Commands { /// - /// The base class for the SetAliasCommand and NewAliasCommand + /// The base class for the SetAliasCommand and NewAliasCommand. /// - /// public class WriteAliasCommandBase : PSCmdlet { #region Parameters /// - /// The Name parameter for the command + /// The Name parameter for the command. /// - /// [Parameter(Position = 0, Mandatory = true, ValueFromPipelineByPropertyName = true)] public string Name { get; set; } /// - /// The Value parameter for the command + /// The Value parameter for the command. /// - /// [Parameter(Position = 1, Mandatory = true, ValueFromPipelineByPropertyName = true)] public string Value { get; set; } /// /// The description for the alias. /// - /// [Parameter] - public string Description { get; set; } = String.Empty; + public string Description { get; set; } = string.Empty; /// /// The Option parameter allows the alias to be set to /// ReadOnly (for existing aliases) and/or Constant (only /// for new aliases). /// - /// [Parameter] public ScopedItemOptions Option { get; set; } = ScopedItemOptions.None; /// - /// If set to true, the alias that is set is passed to the - /// pipeline. + /// If set to true, the alias that is set is passed to the pipeline. /// - /// [Parameter] public SwitchParameter PassThru { @@ -64,22 +54,20 @@ public SwitchParameter PassThru _passThru = value; } } + private bool _passThru; /// - /// The scope parameter for the command determines - /// which scope the alias is set in. + /// The scope parameter for the command determines which scope the alias is set in. /// - /// [Parameter] + [ArgumentCompleter(typeof(ScopeArgumentCompleter))] public string Scope { get; set; } - /// /// If set to true and an existing alias of the same name exists /// and is ReadOnly, the alias will be overwritten. /// - /// [Parameter] public SwitchParameter Force { @@ -93,9 +81,8 @@ public SwitchParameter Force _force = value; } } + private bool _force; #endregion Parameters - - } // class WriteAliasCommandBase -}//Microsoft.PowerShell.Commands - + } +} diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WriteConsoleCmdlet.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WriteConsoleCmdlet.cs index d83ef795fbd..48d84636ce2 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WriteConsoleCmdlet.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WriteConsoleCmdlet.cs @@ -1,45 +1,29 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. -using System; -using System.Management.Automation; using System.Collections; +using System.Management.Automation; using System.Text; +using System.Xml; namespace Microsoft.PowerShell.Commands { /// - /// - /// Class comment - /// + /// WriteHost cmdlet. /// - - [Cmdlet(VerbsCommunications.Write, "Host", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113426", RemotingCapability = RemotingCapability.None)] + [Cmdlet(VerbsCommunications.Write, "Host", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2097137", RemotingCapability = RemotingCapability.None)] public sealed class WriteHostCommand : ConsoleColorCmdlet { - // - // Parameters - // - - - /// - /// /// Object to be output. - /// /// - [Parameter(Position = 0, ValueFromRemainingArguments = true, ValueFromPipeline = true)] - public object Object { get; set; } = null; - + [Alias("Msg", "Message")] + public object Object { get; set; } /// - /// /// False to add a newline to the end of the output string, true if not. - /// /// - [Parameter] public SwitchParameter NoNewline { @@ -47,36 +31,28 @@ public SwitchParameter NoNewline { return _notAppendNewline; } + set { _notAppendNewline = value; } } - - /// - /// - /// The separator to print between objects - /// + /// Gets and sets the separator to print between objects. /// /// - [Parameter] public object Separator { get; set; } = " "; - // // Cmdlet Overrides // - private string ProcessObject(object o) { if (o != null) { - string s = o as string; - IEnumerable enumerable = null; - if (s != null) + if (o is string s) { // strings are IEnumerable, so we special case them if (s.Length > 0) @@ -84,16 +60,20 @@ private string ProcessObject(object o) return s; } } - else if ((enumerable = o as IEnumerable) != null) + else if (o is XmlNode xmlNode) + { + return xmlNode.Name; + } + else if (o is IEnumerable enumerable) { // unroll enumerables, including arrays. bool printSeparator = false; - StringBuilder result = new StringBuilder(); + StringBuilder result = new(); foreach (object element in enumerable) { - if (printSeparator == true && Separator != null) + if (printSeparator && Separator != null) { result.Append(Separator.ToString()); } @@ -118,18 +98,14 @@ private string ProcessObject(object o) return null; } - - /// - /// - /// Outputs the object to the host console, with optional newline - /// + /// Outputs the object to the host console, with optional newline. /// protected override void ProcessRecord() { - string result = ProcessObject(Object) ?? ""; + string result = ProcessObject(Object) ?? string.Empty; - HostInformationMessage informationMessage = new HostInformationMessage(); + HostInformationMessage informationMessage = new(); informationMessage.Message = result; informationMessage.NoNewLine = NoNewline.IsPresent; @@ -145,9 +121,8 @@ protected override void ProcessRecord() } this.WriteInformation(informationMessage, new string[] { "PSHOST" }); - this.Host.UI.TranscribeResult(result); } - private Boolean _notAppendNewline = false; + private bool _notAppendNewline = false; } -} // namespace Microsoft.PowerShell.Commands \ No newline at end of file +} diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WriteProgressCmdlet.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WriteProgressCmdlet.cs index 8901b36e10f..0751954c54e 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WriteProgressCmdlet.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/WriteProgressCmdlet.cs @@ -1,48 +1,29 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. using System; using System.Management.Automation; -using Dbg = System.Management.Automation.Diagnostics; - - - namespace Microsoft.PowerShell.Commands { /// - /// - /// Implements the write-progress cmdlet - /// + /// Implements the write-progress cmdlet. /// - - [Cmdlet(VerbsCommunications.Write, "Progress", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113428", RemotingCapability = RemotingCapability.None)] + [Cmdlet(VerbsCommunications.Write, "Progress", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2097036", RemotingCapability = RemotingCapability.None)] public sealed class WriteProgressCommand : PSCmdlet { /// - /// /// Describes the activity for which progress is being reported. - /// /// - /// - [Parameter( Position = 0, - Mandatory = true, HelpMessageBaseName = HelpMessageBaseName, HelpMessageResourceId = "ActivityParameterHelpMessage")] public string Activity { get; set; } - /// - /// /// Describes the current state of the activity. - /// /// - /// - [Parameter( Position = 1, HelpMessageBaseName = HelpMessageBaseName, @@ -50,73 +31,43 @@ public sealed class WriteProgressCommand : PSCmdlet [ValidateNotNullOrEmpty] public string Status { get; set; } = WriteProgressResourceStrings.Processing; - /// - /// /// Uniquely identifies this activity for purposes of chaining subordinate activities. - /// /// - /// - [Parameter(Position = 2)] - [ValidateRange(0, Int32.MaxValue)] - public int Id { get; set; } = 0; - + [ValidateRange(0, int.MaxValue)] + public int Id { get; set; } /// - /// - /// Percentage completion of the activity, or -1 if n/a - /// + /// Percentage completion of the activity, or -1 if n/a. /// - /// - [Parameter] [ValidateRange(-1, 100)] public int PercentComplete { get; set; } = -1; - /// - /// - /// Seconds remaining to complete the operation, or -1 if n/a - /// + /// Seconds remaining to complete the operation, or -1 if n/a. /// - /// - [Parameter] public int SecondsRemaining { get; set; } = -1; - /// - /// - /// Description of current operation in activity, empty if n/a - /// + /// Description of current operation in activity, empty if n/a. /// - /// - [Parameter] public string CurrentOperation { get; set; } - /// - /// /// Identifies the parent Id of this activity, or -1 if none. - /// /// - /// - [Parameter] - [ValidateRange(-1, Int32.MaxValue)] + [ValidateRange(-1, int.MaxValue)] public int ParentId { get; set; } = -1; - /// - /// /// Identifies whether the activity has completed (and the display for it should be removed), /// or if it is proceeding (and the display for it should be shown). - /// /// - /// - [Parameter] public SwitchParameter Completed { @@ -124,36 +75,49 @@ public SwitchParameter Completed { return _completed; } + set { _completed = value; } } - - /// - /// /// Identifies the source of the record. - /// /// - /// - [Parameter] public int SourceId { get; set; } - /// - /// /// Writes a ProgressRecord created from the parameters. - /// /// - protected override void ProcessRecord() { - ProgressRecord pr = new ProgressRecord(Id, Activity, Status); + ProgressRecord pr; + if (string.IsNullOrEmpty(Activity)) + { + if (!Completed) + { + ThrowTerminatingError(new ErrorRecord( + new ArgumentException("Missing value for mandatory parameter.", nameof(Activity)), + "MissingActivity", + ErrorCategory.InvalidArgument, + Activity)); + return; + } + else + { + pr = new(Id); + pr.StatusDescription = Status; + } + } + else + { + pr = new(Id, Activity, Status); + } + pr.ParentActivityId = ParentId; pr.PercentComplete = PercentComplete; pr.SecondsRemaining = SecondsRemaining; @@ -163,11 +127,8 @@ protected override WriteProgress(SourceId, pr); } - private bool _completed; - private const string HelpMessageBaseName = "WriteProgressResourceStrings"; } } - diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/XmlCommands.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/XmlCommands.cs index 2a9263060bf..6473d6d4fae 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/XmlCommands.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/XmlCommands.cs @@ -1,12 +1,10 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. using System; using System.Collections; using System.Collections.Generic; using System.Collections.ObjectModel; -using System.Diagnostics.CodeAnalysis; using System.Globalization; using System.IO; using System.Management.Automation; @@ -19,9 +17,9 @@ namespace Microsoft.PowerShell.Commands { /// - /// implementation for the Export-Clixml command + /// Implementation for the Export-Clixml command. /// - [Cmdlet(VerbsData.Export, "Clixml", SupportsShouldProcess = true, DefaultParameterSetName = "ByPath", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113297")] + [Cmdlet(VerbsData.Export, "Clixml", SupportsShouldProcess = true, DefaultParameterSetName = "ByPath", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2096926")] public sealed class ExportClixmlCommand : PSCmdlet, IDisposable { #region Command Line Parameters @@ -30,39 +28,41 @@ public sealed class ExportClixmlCommand : PSCmdlet, IDisposable // implementation will need to be modified. /// - /// Depth of serialization + /// Depth of serialization. /// [Parameter] [ValidateRange(1, int.MaxValue)] - public int Depth { get; set; } = 0; + public int Depth { get; set; } /// - /// mandatory file name to write to + /// Mandatory file name to write to. /// [Parameter(Mandatory = true, Position = 0, ParameterSetName = "ByPath")] public string Path { get; set; } /// - /// mandatory file name to write to + /// Mandatory file name to write to. /// [Parameter(Mandatory = true, ParameterSetName = "ByLiteralPath")] - [Alias("PSPath")] + [Alias("PSPath", "LP")] public string LiteralPath { get { return Path; } + set { Path = value; _isLiteralPath = true; } } + private bool _isLiteralPath = false; /// - /// Input object to be exported + /// Input object to be exported. /// [Parameter(ValueFromPipeline = true, Mandatory = true)] [AllowNull] @@ -78,11 +78,13 @@ public SwitchParameter Force { return _force; } + set { _force = value; } } + private bool _force; /// @@ -96,27 +98,44 @@ public SwitchParameter NoClobber { return _noclobber; } + set { _noclobber = value; } } + private bool _noclobber; /// - /// Encoding optional flag + /// Encoding optional flag. /// - /// [Parameter] - [ValidateSetAttribute(new string[] { "Unicode", "UTF7", "UTF8", "ASCII", "UTF32", "BigEndianUnicode", "Default", "OEM" })] - public string Encoding { get; set; } = "Unicode"; + [ArgumentToEncodingTransformation] + [ArgumentEncodingCompletions] + [ValidateNotNullOrEmpty] + public Encoding Encoding + { + get + { + return _encoding; + } + + set + { + EncodingConversion.WarnIfObsolete(this, value); + _encoding = value; + } + } + + private Encoding _encoding = Encoding.Default; #endregion Command Line Parameters #region Overrides /// - /// BeginProcessing override + /// BeginProcessing override. /// protected override void @@ -125,15 +144,13 @@ protected override CreateFileStream(); } - /// - /// /// protected override void ProcessRecord() { - if (null != _serializer) + if (_serializer != null) { _serializer.Serialize(InputObject); _xw.Flush(); @@ -141,7 +158,6 @@ protected override } /// - /// /// protected override void @@ -152,11 +168,11 @@ protected override _serializer.Done(); _serializer = null; } + CleanUp(); } /// - /// /// protected override void StopProcessing() { @@ -169,22 +185,22 @@ protected override void StopProcessing() #region file /// - /// handle to file stream + /// Handle to file stream. /// private FileStream _fs; /// - /// stream writer used to write to file + /// Stream writer used to write to file. /// private XmlWriter _xw; /// - /// Serializer used for serialization + /// Serializer used for serialization. /// private Serializer _serializer; /// - /// FileInfo of file to clear read-only flag when operation is complete + /// FileInfo of file to clear read-only flag when operation is complete. /// private FileInfo _readOnlyFileInfo = null; @@ -192,7 +208,10 @@ private void CreateFileStream() { Dbg.Assert(Path != null, "FileName is mandatory parameter"); - if (!ShouldProcess(Path)) return; + if (!ShouldProcess(Path)) + { + return; + } StreamWriter sw; PathUtils.MasterStreamOpen( @@ -210,7 +229,7 @@ private void CreateFileStream() ); // create xml writer - XmlWriterSettings xmlSettings = new XmlWriterSettings(); + XmlWriterSettings xmlSettings = new(); xmlSettings.CloseOutput = true; xmlSettings.Encoding = sw.Encoding; xmlSettings.Indent = true; @@ -237,6 +256,7 @@ private void CreateFileStream() _xw.Dispose(); _xw = null; } + _fs.Dispose(); _fs = null; } @@ -247,21 +267,22 @@ private void CreateFileStream() #region IDisposable Members /// - /// Set to true when object is disposed + /// Set to true when object is disposed. /// private bool _disposed; /// - /// public dispose method + /// Public dispose method. /// public void Dispose() { - if (_disposed == false) + if (!_disposed) { CleanUp(); } + _disposed = true; } @@ -269,39 +290,39 @@ private void CreateFileStream() } /// - /// Implements Import-Clixml command + /// Implements Import-Clixml command. /// - [Cmdlet(VerbsData.Import, "Clixml", SupportsPaging = true, DefaultParameterSetName = "ByPath", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113340")] + [Cmdlet(VerbsData.Import, "Clixml", SupportsPaging = true, DefaultParameterSetName = "ByPath", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2096618")] public sealed class ImportClixmlCommand : PSCmdlet, IDisposable { #region Command Line Parameters /// - /// mandatory file name to read from + /// Mandatory file name to read from. /// [Parameter(Position = 0, Mandatory = true, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true, ParameterSetName = "ByPath")] - public String[] Path { get; set; } + public string[] Path { get; set; } /// - /// mandatory file name to read from + /// Mandatory file name to read from. /// [Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true, ParameterSetName = "ByLiteralPath")] - [Alias("PSPath")] - [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] - public String[] LiteralPath + [Alias("PSPath", "LP")] + public string[] LiteralPath { get { return Path; } + set { Path = value; _isLiteralPath = true; } } - private bool _isLiteralPath = false; + private bool _isLiteralPath = false; #endregion Command Line Parameters @@ -310,7 +331,7 @@ public String[] LiteralPath private bool _disposed = false; /// - /// public dispose method + /// Public dispose method. /// public void Dispose() { @@ -322,6 +343,7 @@ public void Dispose() _helper.Dispose(); _helper = null; } + _disposed = true; } } @@ -331,7 +353,7 @@ public void Dispose() private ImportXmlHelper _helper; /// - /// ProcessRecord overload + /// ProcessRecord overload. /// protected override void ProcessRecord() { @@ -346,7 +368,6 @@ protected override void ProcessRecord() } /// - /// /// protected override void StopProcessing() { @@ -355,34 +376,30 @@ protected override void StopProcessing() } } - /// - /// implementation for the convertto-xml command + /// Implementation for the convertto-xml command. /// [Cmdlet(VerbsData.ConvertTo, "Xml", SupportsShouldProcess = false, - HelpUri = "https://go.microsoft.com/fwlink/?LinkID=135204", RemotingCapability = RemotingCapability.None)] - [OutputType(typeof(XmlDocument), typeof(String))] + HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2096603", RemotingCapability = RemotingCapability.None)] + [OutputType(typeof(XmlDocument), typeof(string))] public sealed class ConvertToXmlCommand : PSCmdlet, IDisposable { #region Command Line Parameters - /// - /// Depth of serialization + /// Depth of serialization. /// [Parameter(HelpMessage = "Specifies how many levels of contained objects should be included in the XML representation")] [ValidateRange(1, int.MaxValue)] - public int Depth { get; set; } = 0; - + public int Depth { get; set; } /// - /// Input Object which is written to XML format + /// Input Object which is written to XML format. /// [Parameter(Position = 0, ValueFromPipeline = true, Mandatory = true)] [AllowNull] public PSObject InputObject { get; set; } - /// /// Property that sets NoTypeInformation parameter. /// @@ -393,11 +410,13 @@ public SwitchParameter NoTypeInformation { return _notypeinformation; } + set { _notypeinformation = value; } } + private bool _notypeinformation; /// @@ -410,11 +429,10 @@ public SwitchParameter NoTypeInformation #endregion Command Line Parameters - #region Overrides /// - /// BeginProcessing override + /// BeginProcessing override. /// protected override void BeginProcessing() { @@ -424,14 +442,13 @@ protected override void BeginProcessing() } else { - WriteObject(string.Format(CultureInfo.InvariantCulture, "", Encoding.UTF8.WebName)); + WriteObject(string.Create(CultureInfo.InvariantCulture, $"")); WriteObject(""); } } - /// - /// override ProcessRecord + /// Override ProcessRecord. /// protected override void ProcessRecord() { @@ -439,37 +456,33 @@ protected override void ProcessRecord() { CreateMemoryStream(); - if (null != _serializer) - _serializer.SerializeAsStream(InputObject); + _serializer?.SerializeAsStream(InputObject); - - if (null != _serializer) + if (_serializer != null) { _serializer.DoneAsStream(); _serializer = null; } - //Loading to the XML Document + // Loading to the XML Document _ms.Position = 0; - StreamReader read = new StreamReader(_ms); + StreamReader read = new(_ms); string data = read.ReadToEnd(); WriteObject(data); - //Cleanup + // Cleanup CleanUp(); } else { - if (null != _serializer) - _serializer.Serialize(InputObject); + _serializer?.Serialize(InputObject); } } /// - /// /// protected override void EndProcessing() { - if (null != _serializer) + if (_serializer != null) { _serializer.Done(); _serializer = null; @@ -481,29 +494,29 @@ protected override void EndProcessing() } else { - //Loading to the XML Document + // Loading to the XML Document _ms.Position = 0; if (As.Equals("Document", StringComparison.OrdinalIgnoreCase)) { // this is a trusted xml doc - the cmdlet generated the doc into a private memory stream - XmlDocument xmldoc = new XmlDocument(); + XmlDocument xmldoc = new(); xmldoc.Load(_ms); WriteObject(xmldoc); } else if (As.Equals("String", StringComparison.OrdinalIgnoreCase)) { - StreamReader read = new StreamReader(_ms); + StreamReader read = new(_ms); string data = read.ReadToEnd(); WriteObject(data); } } - //Cleaning up + // Cleaning up CleanUp(); } /// - /// + /// StopProcessing. /// protected override void StopProcessing() { @@ -515,17 +528,17 @@ protected override void StopProcessing() #region memory /// - /// XmlText writer + /// XmlText writer. /// private XmlWriter _xw; /// - /// Serializer used for serialization + /// Serializer used for serialization. /// private CustomSerialization _serializer; /// - ///Memory Stream used for serialization + /// Memory Stream used for serialization. /// private MemoryStream _ms; @@ -575,7 +588,7 @@ private void CreateMemoryStream() } /// - ///Cleaning up the MemoryStream + ///Cleaning up the MemoryStream. /// private void CleanUp() { @@ -597,45 +610,125 @@ private void CleanUp() #region IDisposable Members /// - /// Set to true when object is disposed + /// Set to true when object is disposed. /// private bool _disposed; /// - /// public dispose method + /// Public dispose method. /// public void Dispose() { - if (_disposed == false) + if (!_disposed) { CleanUp(); } + _disposed = true; } #endregion IDisposable Members } + /// + /// Implements ConvertTo-CliXml command. + /// + [Cmdlet(VerbsData.ConvertTo, "CliXml", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2280866")] + [OutputType(typeof(string))] + public sealed class ConvertToClixmlCommand : PSCmdlet + { + #region Parameters + + /// + /// Gets or sets input objects to be converted to CliXml object. + /// + [Parameter(Position = 0, Mandatory = true, ValueFromPipeline = true)] + public PSObject InputObject { get; set; } + + /// + /// Gets or sets depth of serialization. + /// + [Parameter] + [ValidateRange(1, int.MaxValue)] + public int Depth { get; set; } = 2; + + #endregion Parameters + + #region Private Members + + private readonly List _inputObjectBuffer = new(); + + #endregion Private Members + + #region Overrides + + /// + /// Process record. + /// + protected override void ProcessRecord() + { + _inputObjectBuffer.Add(InputObject); + } + + /// + /// End Processing. + /// + protected override void EndProcessing() + { + WriteObject(PSSerializer.Serialize(_inputObjectBuffer, Depth, enumerate: true)); + } + + #endregion Overrides + } + + /// + /// Implements ConvertFrom-CliXml command. + /// + [Cmdlet(VerbsData.ConvertFrom, "CliXml", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2280770")] + public sealed class ConvertFromClixmlCommand : PSCmdlet + { + #region Parameters + + /// + /// Gets or sets input object which is written in CliXml format. + /// + [Parameter(Position = 0, Mandatory = true, ValueFromPipeline = true)] + public string InputObject { get; set; } + + #endregion Parameters + + #region Overrides + + /// + /// Process record. + /// + protected override void ProcessRecord() + { + WriteObject(PSSerializer.Deserialize(InputObject)); + } + + #endregion Overrides + } /// - /// Helper class to import single XML file + /// Helper class to import single XML file. /// - internal class ImportXmlHelper : IDisposable + internal sealed class ImportXmlHelper : IDisposable { #region constructor /// - /// XML file to import + /// XML file to import. /// private readonly string _path; /// - /// Reference to cmdlet which is using this helper class + /// Reference to cmdlet which is using this helper class. /// private readonly PSCmdlet _cmdlet; - private bool _isLiteralPath; + private readonly bool _isLiteralPath; internal ImportXmlHelper(string fileName, PSCmdlet cmdlet, bool isLiteralPath) { @@ -651,12 +744,12 @@ internal ImportXmlHelper(string fileName, PSCmdlet cmdlet, bool isLiteralPath) #region file /// - /// handle to file stream + /// Handle to file stream. /// internal FileStream _fs; /// - /// XmlReader used to read file + /// XmlReader used to read file. /// internal XmlReader _xr; @@ -698,19 +791,20 @@ private void CleanUp() #region IDisposable Members /// - /// Set to true when object is disposed + /// Set to true when object is disposed. /// private bool _disposed; /// - /// public dispose method + /// Public dispose method. /// public void Dispose() { - if (_disposed == false) + if (!_disposed) { CleanUp(); } + _disposed = true; GC.SuppressFinalize(this); } @@ -730,7 +824,6 @@ internal void Import() _cmdlet.WriteObject(totalCount); } - ulong skip = _cmdlet.PagingParameters.Skip; ulong first = _cmdlet.PagingParameters.First; @@ -751,12 +844,10 @@ internal void Import() while (!_deserializer.Done() && count < first) { object result = _deserializer.Deserialize(); - PSObject psObject = result as PSObject; - if (psObject != null) + if (result is PSObject psObject) { - ICollection c = psObject.BaseObject as ICollection; - if (c != null) + if (psObject.BaseObject is ICollection c) { foreach (object o in c) { @@ -789,63 +880,56 @@ internal void Import() } } - internal void Stop() - { - if (_deserializer != null) - { - _deserializer.Stop(); - } - } - } // ImportXmlHelper + internal void Stop() => _deserializer?.Stop(); + } #region Select-Xml - /// - ///This cmdlet is used to search an xml document based on the XPath Query. - /// - [Cmdlet(VerbsCommon.Select, "Xml", DefaultParameterSetName = "Xml", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=135255")] + /// + /// This cmdlet is used to search an xml document based on the XPath Query. + /// + [Cmdlet(VerbsCommon.Select, "Xml", DefaultParameterSetName = "Xml", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2097031")] [OutputType(typeof(SelectXmlInfo))] public class SelectXmlCommand : PSCmdlet { - # region parameters + #region parameters /// - /// Specifies the path which contains the xml files. The default is the current - /// user directory + /// Specifies the path which contains the xml files. The default is the current user directory. /// - [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] [Parameter(Position = 1, Mandatory = true, ValueFromPipelineByPropertyName = true, ParameterSetName = "Path")] [ValidateNotNullOrEmpty] - public String[] Path { get; set; } + public string[] Path { get; set; } /// - /// Specifies the literal path which contains the xml files. The default is the current - /// user directory + /// Specifies the literal path which contains the xml files. The default is the current user directory. /// - [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] [Parameter(Mandatory = true, ValueFromPipelineByPropertyName = true, ParameterSetName = "LiteralPath")] [ValidateNotNullOrEmpty] - [Alias("PSPath")] - public String[] LiteralPath + [Alias("PSPath", "LP")] + public string[] LiteralPath { - get { return Path; } + get + { + return Path; + } + set { Path = value; _isLiteralPath = true; } } + private bool _isLiteralPath = false; /// /// The following is the definition of the input parameter "XML". - /// Specifies the xml Node + /// Specifies the xml Node. /// [Parameter(Position = 1, Mandatory = true, ValueFromPipelineByPropertyName = true, ValueFromPipeline = true, ParameterSetName = "Xml")] [ValidateNotNullOrEmpty] - [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] - [SuppressMessage("Microsoft.Design", "CA1059:MembersShouldNotExposeCertainConcreteTypes", MessageId = "System.Xml.XmlNode")] [Alias("Node")] public System.Xml.XmlNode[] Xml { get; set; } @@ -853,7 +937,6 @@ public String[] LiteralPath /// The following is the definition of the input parameter in string format. /// Specifies the string format of a fully qualified xml. /// - [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] [Parameter(Mandatory = true, ValueFromPipeline = true, ParameterSetName = "Content")] [ValidateNotNullOrEmpty] @@ -869,17 +952,15 @@ public String[] LiteralPath public string XPath { get; set; } /// - /// The following definition used to specify the - /// NameSpace of xml. + /// The following definition used to specify the NameSpace of xml. /// [Parameter] [ValidateNotNullOrEmpty] - [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")] public Hashtable Namespace { get; set; } - # endregion parameters + #endregion parameters - # region private + #region private private void WriteResults(XmlNodeList foundXmlNodes, string filePath) { @@ -887,7 +968,7 @@ private void WriteResults(XmlNodeList foundXmlNodes, string filePath) foreach (XmlNode foundXmlNode in foundXmlNodes) { - SelectXmlInfo selectXmlInfo = new SelectXmlInfo(); + SelectXmlInfo selectXmlInfo = new(); selectXmlInfo.Node = foundXmlNode; selectXmlInfo.Pattern = XPath; selectXmlInfo.Path = filePath; @@ -910,14 +991,15 @@ private void ProcessXmlNode(XmlNode xmlNode, string filePath) { xList = xmlNode.SelectNodes(XPath); } + this.WriteResults(xList, filePath); } private void ProcessXmlFile(string filePath) { - //Cannot use ImportXMLHelper because it will throw terminating error which will - //not be inline with Select-String - //So doing self processing of the file. + // Cannot use ImportXMLHelper because it will throw terminating error which will + // not be inline with Select-String + // So doing self processing of the file. try { XmlDocument xmlDocument = InternalDeserializer.LoadUnsafeXmlDocument( @@ -962,8 +1044,8 @@ private void WriteFileReadError(string filePath, Exception exception) filePath, exception.Message); - ArgumentException argumentException = new ArgumentException(errorMessage, exception); - ErrorRecord errorRecord = new ErrorRecord(argumentException, "ProcessingFile", ErrorCategory.InvalidArgument, filePath); + ArgumentException argumentException = new(errorMessage, exception); + ErrorRecord errorRecord = new(argumentException, "ProcessingFile", ErrorCategory.InvalidArgument, filePath); this.WriteError(errorRecord); } @@ -990,15 +1072,15 @@ private XmlNamespaceManager AddNameSpaceTable(string parametersetname, XmlDocume catch (NullReferenceException) { string message = StringUtil.Format(UtilityCommonStrings.SearchXMLPrefixNullError); - InvalidOperationException e = new InvalidOperationException(message); - ErrorRecord er = new ErrorRecord(e, "PrefixError", ErrorCategory.InvalidOperation, namespacetable); + InvalidOperationException e = new(message); + ErrorRecord er = new(e, "PrefixError", ErrorCategory.InvalidOperation, namespacetable); WriteError(er); } catch (ArgumentNullException) { string message = StringUtil.Format(UtilityCommonStrings.SearchXMLPrefixNullError); - InvalidOperationException e = new InvalidOperationException(message); - ErrorRecord er = new ErrorRecord(e, "PrefixError", ErrorCategory.InvalidOperation, namespacetable); + InvalidOperationException e = new(message); + ErrorRecord er = new(e, "PrefixError", ErrorCategory.InvalidOperation, namespacetable); WriteError(er); } } @@ -1006,7 +1088,7 @@ private XmlNamespaceManager AddNameSpaceTable(string parametersetname, XmlDocume return xmlns; } - # endregion private + #endregion private #region override @@ -1026,8 +1108,8 @@ protected override void ProcessRecord() (ParameterSetName.Equals("Path", StringComparison.OrdinalIgnoreCase) || (ParameterSetName.Equals("LiteralPath", StringComparison.OrdinalIgnoreCase)))) { - //If any file not resolved, execution stops. this is to make consistent with select-string. - List fullresolvedPaths = new List(); + // If any file not resolved, execution stops. this is to make consistent with select-string. + List fullresolvedPaths = new(); foreach (string fpath in Path) { if (_isLiteralPath) @@ -1041,16 +1123,18 @@ protected override void ProcessRecord() Collection resolvedPaths = GetResolvedProviderPathFromPSPath(fpath, out provider); if (!provider.NameEquals(this.Context.ProviderNames.FileSystem)) { - //Cannot open File error + // Cannot open File error string message = StringUtil.Format(UtilityCommonStrings.FileOpenError, provider.FullName); - InvalidOperationException e = new InvalidOperationException(message); - ErrorRecord er = new ErrorRecord(e, "ProcessingFile", ErrorCategory.InvalidOperation, fpath); + InvalidOperationException e = new(message); + ErrorRecord er = new(e, "ProcessingFile", ErrorCategory.InvalidOperation, fpath); WriteError(er); continue; } + fullresolvedPaths.AddRange(resolvedPaths); } } + foreach (string file in fullresolvedPaths) { ProcessXmlFile(file); @@ -1078,10 +1162,9 @@ protected override void ProcessRecord() { Dbg.Assert(false, "Unrecognized parameterset"); } - }//End ProcessRecord() - + } #endregion overrides - }//End Class + } /// /// The object returned by Select-Xml representing the result of a match. @@ -1096,9 +1179,8 @@ public sealed class SelectXmlInfo private const string SimpleFormat = "{0}"; /// - /// The XmlNode that matches search + /// The XmlNode that matches search. /// - [SuppressMessage("Microsoft.Design", "CA1059:MembersShouldNotExposeCertainConcreteTypes", MessageId = "System.Xml.XmlNode")] public XmlNode Node { get; set; } /// @@ -1110,9 +1192,10 @@ public string Path { return _path; } + set { - if (String.IsNullOrEmpty(value)) + if (string.IsNullOrEmpty(value)) { _path = inputStream; } @@ -1122,10 +1205,11 @@ public string Path } } } + private string _path; /// - /// The pattern used to search + /// The pattern used to search. /// public string Pattern { get; set; } @@ -1140,7 +1224,7 @@ public override string ToString() } /// - /// Return String representation of the object + /// Return String representation of the object. /// /// /// @@ -1156,7 +1240,7 @@ private string ToString(string directory) /// internal string GetNodeText() { - string nodeText = String.Empty; + string nodeText = string.Empty; if (Node != null) { if (Node.Value != null) @@ -1168,6 +1252,7 @@ internal string GetNodeText() nodeText = Node.InnerXml.Trim(); } } + return nodeText; } @@ -1185,7 +1270,7 @@ private string RelativePath(string directory) string relPath = _path; if (!relPath.Equals(inputStream)) { - if (relPath.StartsWith(directory, StringComparison.CurrentCultureIgnoreCase)) + if (relPath.StartsWith(directory, StringComparison.OrdinalIgnoreCase)) { int offset = directory.Length; if (offset < relPath.Length) @@ -1197,6 +1282,7 @@ private string RelativePath(string directory) } } } + return relPath; } @@ -1218,7 +1304,5 @@ private string FormatLine(string text, string displaypath) } } } - #endregion Select-Xml } - diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/compare-object.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/compare-object.cs deleted file mode 100644 index 6b6fc0f3a32..00000000000 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/compare-object.cs +++ /dev/null @@ -1,485 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -using System; -using System.Collections.Generic; -using System.Collections; -using System.Management.Automation; -using Microsoft.PowerShell.Commands.Internal.Format; - -namespace Microsoft.PowerShell.Commands -{ - /// - /// - /// - [Cmdlet(VerbsData.Compare, "Object", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113286", - RemotingCapability = RemotingCapability.None)] - public sealed class CompareObjectCommand : ObjectCmdletBase - { - #region Parameters - /// - /// - /// - [Parameter(Position = 0, Mandatory = true)] - [AllowEmptyCollection] - public PSObject[] ReferenceObject { get; set; } - - /// - /// - /// - [Parameter(Position = 1, Mandatory = true, ValueFromPipeline = true)] - [AllowEmptyCollection] - public PSObject[] DifferenceObject { get; set; } - - /// - /// - /// - [Parameter] - [ValidateRange(0, Int32.MaxValue)] - public int SyncWindow { get; set; } = Int32.MaxValue; - - /// - /// - /// - /// - [Parameter] - public object[] Property { get; set; } - - /* not implemented - /// - /// - /// - [Parameter] - public SwitchParameter IgnoreWhiteSpace - { - get { return _ignoreWhiteSpace; } - set { _ignoreWhiteSpace = value; } - } - private bool _ignoreWhiteSpace = false; - */ - - /// - /// - /// - [Parameter] - public SwitchParameter ExcludeDifferent - { - get { return _excludeDifferent; } - set { _excludeDifferent = value; } - } - private bool _excludeDifferent /*=false*/; - - /// - /// - /// - [Parameter] - public SwitchParameter IncludeEqual - { - get { return _includeEqual; } - set - { - _isIncludeEqualSpecified = true; - _includeEqual = value; - } - } - private bool _includeEqual /* = false */; - private bool _isIncludeEqualSpecified /* = false */; - - /// - /// - /// - [Parameter] - public SwitchParameter PassThru - { - get { return _passThru; } - set { _passThru = value; } - } - private bool _passThru /* = false */; - #endregion Parameters - - #region Internal - private List _referenceEntries; - private List _referenceEntryBacklog - = new List(); - private List _differenceEntryBacklog - = new List(); - private OrderByProperty _orderByProperty = null; - private OrderByPropertyComparer _comparer = null; - - private int _referenceObjectIndex /* = 0 */; - - // These are programmatic strings, not subject to INTL - private const string SideIndicatorPropertyName = "SideIndicator"; - private const string SideIndicatorMatch = "=="; - private const string SideIndicatorReference = "<="; - private const string SideIndicatorDifference = "=>"; - private const string InputObjectPropertyName = "InputObject"; - - /// - /// The following is the matching algorithm: - /// Retrieve the incoming object (differenceEntry) if any - /// Retrieve the next reference object (referenceEntry) if any - /// If differenceEntry matches referenceEntry - /// Emit referenceEntry as a match - /// Return - /// If differenceEntry matches any entry in referenceEntryBacklog - /// Emit the backlog entry as a match - /// Remove the backlog entry from referenceEntryBacklog - /// Clear differenceEntry - /// If referenceEntry (if any) matches any entry in differenceEntryBacklog - /// Emit referenceEntry as a match - /// Remove the backlog entry from differenceEntryBacklog - /// Clear referenceEntry - /// If differenceEntry is still present - /// If SyncWindow is 0 - /// Emit differenceEntry as unmatched - /// Else - /// While there is no space in differenceEntryBacklog - /// Emit oldest entry in differenceEntryBacklog as unmatched - /// Remove oldest entry from differenceEntryBacklog - /// Add differenceEntry to differenceEntryBacklog - /// If referenceEntry is still present - /// If SyncWindow is 0 - /// Emit referenceEntry as unmatched - /// Else - /// While there is no space in referenceEntryBacklog - /// Emit oldest entry in referenceEntryBacklog as unmatched - /// Remove oldest entry from referenceEntryBacklog - /// Add referenceEntry to referenceEntryBacklog - /// - /// - private void Process(OrderByPropertyEntry differenceEntry) - { - Diagnostics.Assert(null != _referenceEntries, "null referenceEntries"); - - // Retrieve the next reference object (referenceEntry) if any - OrderByPropertyEntry referenceEntry = null; - if (_referenceObjectIndex < _referenceEntries.Count) - { - referenceEntry = _referenceEntries[_referenceObjectIndex++]; - } - - // If differenceEntry matches referenceEntry - // Emit referenceEntry as a match - // Return - // 2005/07/19 Switched order of referenceEntry and differenceEntry - // so that we cast differenceEntry to the type of referenceEntry. - if (null != referenceEntry && null != differenceEntry && - 0 == _comparer.Compare(referenceEntry, differenceEntry)) - { - EmitMatch(referenceEntry); - return; - } - - // If differenceEntry matches any entry in referenceEntryBacklog - // Emit the backlog entry as a match - // Remove the backlog entry from referenceEntryBacklog - // Clear differenceEntry - OrderByPropertyEntry matchingEntry = - MatchAndRemove(differenceEntry, _referenceEntryBacklog); - if (null != matchingEntry) - { - EmitMatch(matchingEntry); - differenceEntry = null; - } - - // If referenceEntry (if any) matches any entry in differenceEntryBacklog - // Emit referenceEntry as a match - // Remove the backlog entry from differenceEntryBacklog - // Clear referenceEntry - matchingEntry = - MatchAndRemove(referenceEntry, _differenceEntryBacklog); - if (null != matchingEntry) - { - EmitMatch(referenceEntry); - referenceEntry = null; - } - - // If differenceEntry is still present - // If SyncWindow is 0 - // Emit differenceEntry as unmatched - // Else - // While there is no space in differenceEntryBacklog - // Emit oldest entry in differenceEntryBacklog as unmatched - // Remove oldest entry from differenceEntryBacklog - // Add differenceEntry to differenceEntryBacklog - if (null != differenceEntry) - { - if (0 < SyncWindow) - { - while (_differenceEntryBacklog.Count >= SyncWindow) - { - EmitDifferenceOnly(_differenceEntryBacklog[0]); - _differenceEntryBacklog.RemoveAt(0); - } - _differenceEntryBacklog.Add(differenceEntry); - } - else - { - EmitDifferenceOnly(differenceEntry); - } - } - - // If referenceEntry is still present - // If SyncWindow is 0 - // Emit referenceEntry as unmatched - // Else - // While there is no space in referenceEntryBacklog - // Emit oldest entry in referenceEntryBacklog as unmatched - // Remove oldest entry from referenceEntryBacklog - // Add referenceEntry to referenceEntryBacklog - if (null != referenceEntry) - { - if (0 < SyncWindow) - { - while (_referenceEntryBacklog.Count >= SyncWindow) - { - EmitReferenceOnly(_referenceEntryBacklog[0]); - _referenceEntryBacklog.RemoveAt(0); - } - _referenceEntryBacklog.Add(referenceEntry); - } - else - { - EmitReferenceOnly(referenceEntry); - } - } - } - - private void InitComparer() - { - if (null != _comparer) - return; - - List referenceObjectList = new List(ReferenceObject); - _orderByProperty = new OrderByProperty( - this, referenceObjectList, Property, true, _cultureInfo, CaseSensitive); - Diagnostics.Assert(_orderByProperty.Comparer != null, "no comparer"); - Diagnostics.Assert( - _orderByProperty.OrderMatrix != null && - _orderByProperty.OrderMatrix.Count == ReferenceObject.Length, - "no OrderMatrix"); - if (_orderByProperty.Comparer == null || _orderByProperty.OrderMatrix == null || _orderByProperty.OrderMatrix.Count == 0) - { - return; - } - - _comparer = _orderByProperty.Comparer; - _referenceEntries = _orderByProperty.OrderMatrix; - } - - private OrderByPropertyEntry MatchAndRemove( - OrderByPropertyEntry match, - List list) - { - if (null == match || null == list) - return null; - Diagnostics.Assert(null != _comparer, "null comparer"); - for (int i = 0; i < list.Count; i++) - { - OrderByPropertyEntry listEntry = list[i]; - Diagnostics.Assert(null != listEntry, "null listEntry " + i); - if (0 == _comparer.Compare(match, listEntry)) - { - list.RemoveAt(i); - return listEntry; - } - } - return null; - } - - #region Emit - private void EmitMatch(OrderByPropertyEntry entry) - { - if (_includeEqual) - Emit(entry, SideIndicatorMatch); - } - - private void EmitDifferenceOnly(OrderByPropertyEntry entry) - { - if (!ExcludeDifferent) - Emit(entry, SideIndicatorDifference); - } - - private void EmitReferenceOnly(OrderByPropertyEntry entry) - { - if (!ExcludeDifferent) - Emit(entry, SideIndicatorReference); - } - - private void Emit(OrderByPropertyEntry entry, string sideIndicator) - { - Diagnostics.Assert(null != entry, "null entry"); - - PSObject mshobj; - if (PassThru) - { - mshobj = PSObject.AsPSObject(entry.inputObject); - } - else - { - mshobj = new PSObject(); - if (null == Property || 0 == Property.Length) - { - PSNoteProperty inputNote = new PSNoteProperty( - InputObjectPropertyName, entry.inputObject); - mshobj.Properties.Add(inputNote); - } - else - { - List mshParameterList = _orderByProperty.MshParameterList; - Diagnostics.Assert(null != mshParameterList, "null mshParameterList"); - Diagnostics.Assert(mshParameterList.Count == Property.Length, "mshParameterList.Count " + mshParameterList.Count); - - for (int i = 0; i < Property.Length; i++) - { - // 2005/07/05 This is the closest we can come to - // the string typed by the user - MshParameter mshParameter = mshParameterList[i]; - Diagnostics.Assert(null != mshParameter, "null mshParameter"); - Hashtable hash = mshParameter.hash; - Diagnostics.Assert(null != hash, "null hash"); - object prop = hash[FormatParameterDefinitionKeys.ExpressionEntryKey]; - Diagnostics.Assert(null != prop, "null prop"); - string propName = prop.ToString(); - PSNoteProperty propertyNote = new PSNoteProperty( - propName, - entry.orderValues[i].PropertyValue); - try - { - mshobj.Properties.Add(propertyNote); - } - catch (ExtendedTypeSystemException) - { - // this is probably a duplicate add - } - } - } - } - mshobj.Properties.Remove(SideIndicatorPropertyName); - PSNoteProperty sideNote = new PSNoteProperty( - SideIndicatorPropertyName, sideIndicator); - mshobj.Properties.Add(sideNote); - WriteObject(mshobj); - } - #endregion Emit - #endregion Internal - - #region Overrides - - /// - /// If the parameter 'ExcludeDifferent' is present, then we need to turn on the - /// 'IncludeEqual' switch unless it's turned off by the user specifically. - /// - protected override void BeginProcessing() - { - if (ExcludeDifferent) - { - if (_isIncludeEqualSpecified == false) - { - return; - } - if (_isIncludeEqualSpecified && !_includeEqual) - { - return; - } - - _includeEqual = true; - } - } - - /// - /// - /// - protected override void ProcessRecord() - { - if (ReferenceObject == null || ReferenceObject.Length == 0) - { - HandleDifferenceObjectOnly(); - return; - } - else if (DifferenceObject == null || DifferenceObject.Length == 0) - { - HandleReferenceObjectOnly(); - return; - } - - if (null == _comparer && 0 < DifferenceObject.Length) - { - InitComparer(); - } - - List differenceList = new List(DifferenceObject); - List differenceEntries = - OrderByProperty.CreateOrderMatrix( - this, differenceList, _orderByProperty.MshParameterList); - - foreach (OrderByPropertyEntry incomingEntry in differenceEntries) - { - Process(incomingEntry); - } - } - - /// - /// - /// - protected override void EndProcessing() - { - // Clear remaining reference objects if there are more - // reference objects than difference objects - if (_referenceEntries != null) - { - while (_referenceObjectIndex < _referenceEntries.Count) - { - Process(null); - } - } - - // emit all remaining backlogged objects - foreach (OrderByPropertyEntry differenceEntry in _differenceEntryBacklog) - { - EmitDifferenceOnly(differenceEntry); - } - _differenceEntryBacklog.Clear(); - foreach (OrderByPropertyEntry referenceEntry in _referenceEntryBacklog) - { - EmitReferenceOnly(referenceEntry); - } - _referenceEntryBacklog.Clear(); - } - #endregion Overrides - - private void HandleDifferenceObjectOnly() - { - if (DifferenceObject == null || DifferenceObject.Length == 0) - { - return; - } - - List differenceList = new List(DifferenceObject); - _orderByProperty = new OrderByProperty( - this, differenceList, Property, true, _cultureInfo, CaseSensitive); - List differenceEntries = - OrderByProperty.CreateOrderMatrix( - this, differenceList, _orderByProperty.MshParameterList); - - foreach (OrderByPropertyEntry entry in differenceEntries) - { - EmitDifferenceOnly(entry); - } - } - - private void HandleReferenceObjectOnly() - { - if (ReferenceObject == null || ReferenceObject.Length == 0) - { - return; - } - - InitComparer(); - Process(null); - } - } -} - diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/convert-HTML.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/convert-HTML.cs deleted file mode 100644 index 37e63ebe4dc..00000000000 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/convert-HTML.cs +++ /dev/null @@ -1,611 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -using System; -using System.Collections; -using System.Collections.Generic; -using System.Collections.Specialized; -using System.Diagnostics.CodeAnalysis; -using System.Management.Automation; -using System.Management.Automation.Internal; -using System.Net; -using System.Text; -using Microsoft.PowerShell.Commands.Internal.Format; - -namespace Microsoft.PowerShell.Commands -{ - /// - /// - /// Class comment - /// - /// - - [Cmdlet(VerbsData.ConvertTo, "Html", DefaultParameterSetName = "Page", - HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113290", RemotingCapability = RemotingCapability.None)] - public sealed - class ConvertToHtmlCommand : PSCmdlet - { - /// The incoming object - /// - [Parameter(ValueFromPipeline = true)] - public PSObject InputObject - { - get - { - return _inputObject; - } - set - { - _inputObject = value; - } - } - private PSObject _inputObject; - - /// - /// The list of properties to display - /// These take the form of an MshExpression - /// - /// - [Parameter(Position = 0)] - public object[] Property - { - get - { - return _property; - } - set - { - _property = value; - } - } - private object[] _property; - - /// - /// Text to go after the opening body tag - /// and before the table - /// - /// - [Parameter(ParameterSetName = "Page", Position = 3)] - public string[] Body - { - get - { - return _body; - } - set - { - _body = value; - } - } - private string[] _body; - - /// - /// Text to go into the head section - /// of the html doc - /// - /// - [Parameter(ParameterSetName = "Page", Position = 1)] - public string[] Head - { - get - { - return _head; - } - set - { - _head = value; - } - } - private string[] _head; - - /// - /// The string for the title tag - /// The title is also placed in the body of the document - /// before the table between h3 tags - /// If the -Head parameter is used, this parameter has no - /// effect. - /// - /// - [Parameter(ParameterSetName = "Page", Position = 2)] - [ValidateNotNullOrEmpty] - public string Title - { - get - { - return _title; - } - set - { - _title = value; - } - } - private string _title = "HTML TABLE"; - - /// - /// This specifies whether the objects should - /// be rendered as an HTML TABLE or - /// HTML LIST - /// - /// - [Parameter] - [ValidateNotNullOrEmpty] - [ValidateSet("Table", "List")] - public string As - { - get - { - return _as; - } - set - { - _as = value; - } - } - private string _as = "Table"; - - /// - /// This specifies a full or partial URI - /// for the CSS information. - /// The html should reference the css file specified - /// - [Parameter(ParameterSetName = "Page")] - [Alias("cu", "uri")] - [ValidateNotNullOrEmpty] - public Uri CssUri - { - get - { - return _cssuri; - } - set - { - _cssuri = value; - _cssuriSpecified = true; - } - } - private Uri _cssuri; - private bool _cssuriSpecified; - - /// - /// When this switch is specified generate only the - /// HTML representation of the incoming object - /// without the HTML,HEAD,TITLE,BODY,etc tags. - /// - [Parameter(ParameterSetName = "Fragment")] - [ValidateNotNullOrEmpty] - public SwitchParameter Fragment - { - get - { - return _fragment; - } - set - { - _fragment = value; - } - } - private SwitchParameter _fragment; - - /// - /// Specifies the text to include prior the - /// closing body tag of the HTML output - /// - [Parameter] - [ValidateNotNullOrEmpty] - [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] - public string[] PostContent - { - get - { - return _postContent; - } - set - { - _postContent = value; - } - } - private string[] _postContent; - - /// - /// Specifies the text to include after the - /// body tag of the HTML output - /// - [Parameter] - [ValidateNotNullOrEmpty] - [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] - public string[] PreContent - { - get - { - return _preContent; - } - set - { - _preContent = value; - } - } - private string[] _preContent; - - /// - /// definitions for hash table keys - /// - internal static class ConvertHTMLParameterDefinitionKeys - { - internal const string LabelEntryKey = "label"; - internal const string AlignmentEntryKey = "alignment"; - internal const string WidthEntryKey = "width"; - } - - /// - /// This allows for @{e='foo';label='bar';alignment='center';width='20'} - /// - internal class ConvertHTMLExpressionParameterDefinition : CommandParameterDefinition - { - protected override void SetEntries() - { - this.hashEntries.Add(new ExpressionEntryDefinition()); - this.hashEntries.Add(new HashtableEntryDefinition(ConvertHTMLParameterDefinitionKeys.LabelEntryKey, new Type[] { typeof(string) })); - this.hashEntries.Add(new HashtableEntryDefinition(ConvertHTMLParameterDefinitionKeys.AlignmentEntryKey, new Type[] { typeof(string) })); - this.hashEntries.Add(new HashtableEntryDefinition(ConvertHTMLParameterDefinitionKeys.WidthEntryKey, new Type[] { typeof(string) })); - } - } - - /// - /// Create a list of MshParameter from properties - /// - /// can be a string, ScriptBlock, or Hashtable - /// - private List ProcessParameter(object[] properties) - { - TerminatingErrorContext invocationContext = new TerminatingErrorContext(this); - ParameterProcessor processor = - new ParameterProcessor(new ConvertHTMLExpressionParameterDefinition()); - if (properties == null) - { - properties = new object[] { "*" }; - } - return processor.ProcessParameters(properties, invocationContext); - } - - /// - /// Resolve all wildcards in user input Property into resolvedNameMshParameters - /// - private void InitializeResolvedNameMshParameters() - { - // temp list of properties with wildcards resolved - ArrayList resolvedNameProperty = new ArrayList(); - - foreach (MshParameter p in _propertyMshParameterList) - { - string label = p.GetEntry(ConvertHTMLParameterDefinitionKeys.LabelEntryKey) as string; - string alignment = p.GetEntry(ConvertHTMLParameterDefinitionKeys.AlignmentEntryKey) as string; - string width = p.GetEntry(ConvertHTMLParameterDefinitionKeys.WidthEntryKey) as string; - MshExpression ex = p.GetEntry(FormatParameterDefinitionKeys.ExpressionEntryKey) as MshExpression; - List resolvedNames = ex.ResolveNames(_inputObject); - foreach (MshExpression resolvedName in resolvedNames) - { - Hashtable ht = CreateAuxPropertyHT(label, alignment, width); - ht.Add(FormatParameterDefinitionKeys.ExpressionEntryKey, resolvedName.ToString()); - resolvedNameProperty.Add(ht); - } - } - _resolvedNameMshParameters = ProcessParameter(resolvedNameProperty.ToArray()); - } - - private static Hashtable CreateAuxPropertyHT( - string label, - string alignment, - string width) - { - Hashtable ht = new Hashtable(); - if (label != null) - { - ht.Add(ConvertHTMLParameterDefinitionKeys.LabelEntryKey, label); - } - if (alignment != null) - { - ht.Add(ConvertHTMLParameterDefinitionKeys.AlignmentEntryKey, alignment); - } - if (width != null) - { - ht.Add(ConvertHTMLParameterDefinitionKeys.WidthEntryKey, width); - } - return ht; - } - - /// - /// calls ToString. If an exception occurs, eats it and return string.Empty - /// - /// - /// - private static string SafeToString(object obj) - { - if (obj == null) - { - return ""; - } - try - { - return obj.ToString(); - } - catch (Exception) - { - // eats exception if safe - } - return ""; - } - - - /// - /// - /// - protected override void BeginProcessing() - { - //ValidateNotNullOrEmpty attribute is not working for System.Uri datatype, so handling it here - if ((_cssuriSpecified) && (string.IsNullOrEmpty(_cssuri.OriginalString.Trim()))) - { - ArgumentException ex = new ArgumentException(StringUtil.Format(UtilityCommonStrings.EmptyCSSUri, "CSSUri")); - ErrorRecord er = new ErrorRecord(ex, "ArgumentException", ErrorCategory.InvalidArgument, "CSSUri"); - ThrowTerminatingError(er); - } - - _propertyMshParameterList = ProcessParameter(_property); - - if (!String.IsNullOrEmpty(_title)) - { - WebUtility.HtmlEncode(_title); - } - - - // This first line ensures w3c validation will succeed. However we are not specifying - // an encoding in the HTML because we don't know where the text will be written and - // if a particular encoding will be used. - - if (!_fragment) - { - WriteObject(""); - WriteObject(""); - WriteObject(""); - WriteObject(_head ?? new string[] { "" + _title + "" }, true); - if (_cssuriSpecified) - { - WriteObject(""); - } - WriteObject(""); - if (_body != null) - { - WriteObject(_body, true); - } - } - if (_preContent != null) - { - WriteObject(_preContent, true); - } - WriteObject(""); - _isTHWritten = false; - _propertyCollector = new StringCollection(); - } - - /// - /// Reads Width and Alignment from Property and write Col tags - /// - /// - private void WriteColumns(List mshParams) - { - StringBuilder COLTag = new StringBuilder(); - - COLTag.Append(""); - foreach (MshParameter p in mshParams) - { - COLTag.Append(""); - } - - COLTag.Append(""); - - // The columngroup and col nodes will be printed in a single line. - WriteObject(COLTag.ToString()); - } - - /// - /// Writes the list entries when the As parameter has value List - /// - private void WriteListEntry() - { - foreach (MshParameter p in _resolvedNameMshParameters) - { - StringBuilder Listtag = new StringBuilder(); - Listtag.Append(""); - - //for writing the property value - Listtag.Append(""); - - WriteObject(Listtag.ToString()); - } - } - - /// - /// To write the Property name - /// - private void WritePropertyName(StringBuilder Listtag, MshParameter p) - { - //for writing the property name - string label = p.GetEntry(ConvertHTMLParameterDefinitionKeys.LabelEntryKey) as string; - if (label != null) - { - Listtag.Append(label); - } - else - { - MshExpression ex = p.GetEntry(FormatParameterDefinitionKeys.ExpressionEntryKey) as MshExpression; - Listtag.Append(ex.ToString()); - } - } - - /// - /// To write the Property value - /// - private void WritePropertyValue(StringBuilder Listtag, MshParameter p) - { - MshExpression exValue = p.GetEntry(FormatParameterDefinitionKeys.ExpressionEntryKey) as MshExpression; - - // get the value of the property - List resultList = exValue.GetValues(_inputObject); - foreach (MshExpressionResult result in resultList) - { - // create comma sep list for multiple results - if (result.Result != null) - { - string htmlEncodedResult = WebUtility.HtmlEncode(SafeToString(result.Result)); - Listtag.Append(htmlEncodedResult); - } - Listtag.Append(", "); - } - if (Listtag.ToString().EndsWith(", ", StringComparison.Ordinal)) - { - Listtag.Remove(Listtag.Length - 2, 2); - } - } - - /// - /// To write the Table header for the object property names - /// - private void WriteTableHeader(StringBuilder THtag, List resolvedNameMshParameters) - { - //write the property names - foreach (MshParameter p in resolvedNameMshParameters) - { - THtag.Append(""); - } - } - - /// - /// To write the Table row for the object property values - /// - private void WriteTableRow(StringBuilder TRtag, List resolvedNameMshParameters) - { - //write the property values - foreach (MshParameter p in resolvedNameMshParameters) - { - TRtag.Append(""); - } - } - - //count of the objects - private int _numberObjects = 0; - - /// - /// - /// - /// - protected override void ProcessRecord() - { - // writes the table headers - // it is not in BeginProcessing because the first inputObject is needed for - // the number of columns and column name - if (_inputObject == null || _inputObject == AutomationNull.Value) - { - return; - } - _numberObjects++; - if (!_isTHWritten) - { - InitializeResolvedNameMshParameters(); - if (_resolvedNameMshParameters == null || _resolvedNameMshParameters.Count == 0) - { - return; - } - - //if the As parameter is given as List - if (_as.Equals("List", StringComparison.OrdinalIgnoreCase)) - { - //if more than one object,write the horizontal rule to put visual separator - if (_numberObjects > 1) - WriteObject(""); - WriteListEntry(); - } - else //if the As parameter is Table, first we have to write the property names - { - WriteColumns(_resolvedNameMshParameters); - - StringBuilder THtag = new StringBuilder(""); - - //write the table header - WriteTableHeader(THtag, _resolvedNameMshParameters); - - THtag.Append(""); - WriteObject(THtag.ToString()); - _isTHWritten = true; - } - } - //if the As parameter is Table, write the property values - if (_as.Equals("Table", StringComparison.OrdinalIgnoreCase)) - { - StringBuilder TRtag = new StringBuilder(""); - - //write the table row - WriteTableRow(TRtag, _resolvedNameMshParameters); - - TRtag.Append(""); - WriteObject(TRtag.ToString()); - } - } - - /// - /// - /// - protected override void EndProcessing() - { - //if fragment,end with table - WriteObject("
"); - - //for writing the property name - WritePropertyName(Listtag, p); - Listtag.Append(":"); - Listtag.Append(""); - WritePropertyValue(Listtag, p); - Listtag.Append("
"); - WritePropertyName(THtag, p); - THtag.Append(""); - WritePropertyValue(TRtag, p); - TRtag.Append("

"); - if (_postContent != null) - WriteObject(_postContent, true); - - //if not fragment end with body and html also - if (!_fragment) - { - WriteObject(""); - } - } - - #region private - - /// - /// list of incoming objects to compare - /// - private bool _isTHWritten; - private StringCollection _propertyCollector; - private List _propertyMshParameterList; - private List _resolvedNameMshParameters; - //private string ResourcesBaseName = "ConvertHTMLStrings"; - - #endregion private - } -} - diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/group-object.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/group-object.cs deleted file mode 100644 index 1dd466add3b..00000000000 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/group-object.cs +++ /dev/null @@ -1,410 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -using System; -using System.Collections; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Collections.Specialized; -using System.Diagnostics.CodeAnalysis; -using System.Globalization; -using System.Management.Automation; -using System.Management.Automation.Internal; -using System.Text; - -namespace Microsoft.PowerShell.Commands -{ - /// - /// PSTuple is a helper class used to create Tuple from an input array. - /// - internal static class PSTuple - { - /// - /// ArrayToTuple is a helper method used to create a tuple for the supplied input array. - /// - /// Input objects used to create a tuple. - /// Tuple object. - internal static object ArrayToTuple(object[] inputObjects) - { - Diagnostics.Assert(inputObjects != null, "inputObjects is null"); - Diagnostics.Assert(inputObjects.Length > 0, "inputObjects is empty"); - - return ArrayToTuple(inputObjects, 0); - } - - /// - /// ArrayToTuple is a helper method used to create a tuple for the supplied input array. - /// - /// Input objects used to create a tuple - /// Start index of the array from which the objects have to considered for the tuple creation. - /// Tuple object. - internal static object ArrayToTuple(object[] inputObjects, int startIndex) - { - Diagnostics.Assert(inputObjects != null, "inputObjects is null"); - Diagnostics.Assert(inputObjects.Length > 0, "inputObjects is empty"); - - switch (inputObjects.Length - startIndex) - { - case 0: - return null; - case 1: - return Tuple.Create(inputObjects[startIndex]); - case 2: - return Tuple.Create(inputObjects[startIndex], inputObjects[startIndex + 1]); - case 3: - return Tuple.Create(inputObjects[startIndex], inputObjects[startIndex + 1], inputObjects[startIndex + 2]); - case 4: - return Tuple.Create(inputObjects[startIndex], inputObjects[startIndex + 1], inputObjects[startIndex + 2], inputObjects[startIndex + 3]); - case 5: - return Tuple.Create(inputObjects[startIndex], inputObjects[startIndex + 1], inputObjects[startIndex + 2], inputObjects[startIndex + 3], inputObjects[startIndex + 4]); - case 6: - return Tuple.Create(inputObjects[startIndex], inputObjects[startIndex + 1], inputObjects[startIndex + 2], inputObjects[startIndex + 3], inputObjects[startIndex + 4], - inputObjects[startIndex + 5]); - case 7: - return Tuple.Create(inputObjects[startIndex], inputObjects[startIndex + 1], inputObjects[startIndex + 2], inputObjects[startIndex + 3], inputObjects[startIndex + 4], - inputObjects[startIndex + 5], inputObjects[startIndex + 6]); - case 8: - return Tuple.Create(inputObjects[startIndex], inputObjects[startIndex + 1], inputObjects[startIndex + 2], inputObjects[startIndex + 3], inputObjects[startIndex + 4], - inputObjects[startIndex + 5], inputObjects[startIndex + 6], inputObjects[startIndex + 7]); - default: - return Tuple.Create(inputObjects[startIndex], inputObjects[startIndex + 1], inputObjects[startIndex + 2], inputObjects[startIndex + 3], inputObjects[startIndex + 4], - inputObjects[startIndex + 5], inputObjects[startIndex + 6], ArrayToTuple(inputObjects, startIndex + 7)); - } - } - } - - /// - /// Emitted by Group-Object when the NoElement option is true - /// - public sealed class GroupInfoNoElement : GroupInfo - { - internal GroupInfoNoElement(OrderByPropertyEntry groupValue) - : base(groupValue) - { - } - - internal override void Add(PSObject groupValue) - { - Count++; - } - } - - /// - /// Emitted by Group-Object - /// - public class GroupInfo - { - internal GroupInfo(OrderByPropertyEntry groupValue) - { - Group = new Collection(); - this.Add(groupValue.inputObject); - GroupValue = groupValue; - Name = BuildName(groupValue.orderValues); - } - - internal virtual void Add(PSObject groupValue) - { - Group.Add(groupValue); - Count++; - } - - - - private static string BuildName(List propValues) - { - StringBuilder sb = new StringBuilder(); - foreach (ObjectCommandPropertyValue propValue in propValues) - { - if (propValue != null && propValue.PropertyValue != null) - { - var propertyValueItems = propValue.PropertyValue as ICollection; - if (propertyValueItems != null) - { - sb.Append("{"); - var length = sb.Length; - - foreach (object item in propertyValueItems) - { - sb.Append(string.Format(CultureInfo.InvariantCulture, "{0}, ", item.ToString())); - } - - sb = sb.Length > length ? sb.Remove(sb.Length - 2, 2) : sb; - sb.Append("}, "); - } - else - { - sb.Append(string.Format(CultureInfo.InvariantCulture, "{0}, ", propValue.PropertyValue.ToString())); - } - } - } - return sb.Length >= 2 ? sb.Remove(sb.Length - 2, 2).ToString() : string.Empty; - } - - - /// - /// - /// Values of the group - /// - /// - public ArrayList Values - { - get - { - ArrayList values = new ArrayList(); - foreach (ObjectCommandPropertyValue propValue in GroupValue.orderValues) - { - values.Add(propValue.PropertyValue); - } - return values; - } - } - - /// - /// - /// Number of objects in the group - /// - /// - public int Count { get; internal set; } - - /// - /// - /// The list of objects in this group - /// - /// - public Collection Group { get; } = null; - - /// - /// - /// The name of the group - /// - /// - public string Name { get; } = null; - - /// - /// - /// The OrderByPropertyEntry used to build this group object - /// - /// - internal OrderByPropertyEntry GroupValue { get; } = null; - } - - /// - /// - /// Group-Object implementation - /// - /// - [Cmdlet(VerbsData.Group, "Object", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113338", RemotingCapability = RemotingCapability.None)] - [OutputType(typeof(Hashtable), typeof(GroupInfo))] - public class GroupObjectCommand : ObjectBase - { - #region tracer - - /// - /// An instance of the PSTraceSource class used for trace output - /// - [TraceSourceAttribute( - "GroupObjectCommand", - "Class that has group base implementation")] - private static PSTraceSource s_tracer = - PSTraceSource.GetTracer("GroupObjectCommand", - "Class that has group base implementation"); - - #endregion tracer - - #region Command Line Switches - - /// - /// - /// Flatten the groups - /// - /// - /// - [Parameter] - public SwitchParameter NoElement - { - get { return _noElement; } - set { _noElement = value; } - } - private bool _noElement; - /// - /// the AsHashTable parameter - /// - /// - [Parameter(ParameterSetName = "HashTable")] - [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "HashTable")] - [Alias("AHT")] - public SwitchParameter AsHashTable { get; set; } - - /// - /// - /// - /// - [Parameter(ParameterSetName = "HashTable")] - public SwitchParameter AsString { get; set; } - - private List _groups = new List(); - private OrderByProperty _orderByProperty = new OrderByProperty(); - private bool _hasProcessedFirstInputObject; - private Dictionary _tupleToGroupInfoMappingDictionary = new Dictionary(); - private OrderByPropertyComparer _orderByPropertyComparer = null; - - #endregion - - #region utils - - /// - /// Utility function called by Group-Object to create Groups. - /// - /// Input object that needs to be grouped. - /// true if we are not accumulating objects - /// List containing Groups. - /// Dictionary used to keep track of the groups with hash of the property values being the key. - /// The Comparer to be used while comparing to check if new group has to be created. - internal static void DoGrouping(OrderByPropertyEntry currentObjectEntry, bool noElement, List groups, Dictionary groupInfoDictionary, - OrderByPropertyComparer orderByPropertyComparer) - { - if (currentObjectEntry != null && currentObjectEntry.orderValues != null && currentObjectEntry.orderValues.Count > 0) - { - object currentTupleObject = PSTuple.ArrayToTuple(currentObjectEntry.orderValues.ToArray()); - - GroupInfo currentGroupInfo = null; - if (groupInfoDictionary.TryGetValue(currentTupleObject, out currentGroupInfo)) - { - if (currentGroupInfo != null) - { - //add this inputObject to an existing group - currentGroupInfo.Add(currentObjectEntry.inputObject); - } - } - else - { - bool isCurrentItemGrouped = false; - - for (int groupsIndex = 0; groupsIndex < groups.Count; groupsIndex++) - { - // Check if the current input object can be converted to one of the already known types - // by looking up in the type to GroupInfo mapping. - if (orderByPropertyComparer.Compare(groups[groupsIndex].GroupValue, currentObjectEntry) == 0) - { - groups[groupsIndex].Add(currentObjectEntry.inputObject); - isCurrentItemGrouped = true; - break; - } - } - - if (!isCurrentItemGrouped) - { - // create a new group - s_tracer.WriteLine("Create a new group: {0}", currentObjectEntry.orderValues); - GroupInfo newObjGrp = noElement ? new GroupInfoNoElement(currentObjectEntry) : new GroupInfo(currentObjectEntry); - groups.Add(newObjGrp); - - groupInfoDictionary.Add(currentTupleObject, newObjGrp); - } - } - } - } - private void WriteNonTerminatingError(Exception exception, string resourceIdAndErrorId, - ErrorCategory category) - { - Exception ex = new Exception(StringUtil.Format(resourceIdAndErrorId), exception); - WriteError(new ErrorRecord(ex, resourceIdAndErrorId, category, null)); - } - #endregion utils - - /// - /// Process every input object to group them. - /// - protected override void ProcessRecord() - { - if (InputObject != null && InputObject != AutomationNull.Value) - { - OrderByPropertyEntry currentEntry = null; - - if (!_hasProcessedFirstInputObject) - { - if (Property == null) - { - Property = OrderByProperty.GetDefaultKeyPropertySet(InputObject); - } - _orderByProperty.ProcessExpressionParameter(this, Property); - - currentEntry = _orderByProperty.CreateOrderByPropertyEntry(this, InputObject, CaseSensitive, _cultureInfo); - bool[] ascending = new bool[currentEntry.orderValues.Count]; - for (int index = 0; index < currentEntry.orderValues.Count; index++) - { - ascending[index] = true; - } - _orderByPropertyComparer = new OrderByPropertyComparer(ascending, _cultureInfo, CaseSensitive); - - _hasProcessedFirstInputObject = true; - } - else - { - currentEntry = _orderByProperty.CreateOrderByPropertyEntry(this, InputObject, CaseSensitive, _cultureInfo); - } - - DoGrouping(currentEntry, this.NoElement, _groups, _tupleToGroupInfoMappingDictionary, _orderByPropertyComparer); - } - } - - /// - /// - /// - protected override void EndProcessing() - { - s_tracer.WriteLine(_groups.Count); - if (_groups.Count > 0) - { - if (AsHashTable) - { - Hashtable _table = CollectionsUtil.CreateCaseInsensitiveHashtable(); - try - { - foreach (GroupInfo _grp in _groups) - { - if (AsString) - { - _table.Add(_grp.Name, _grp.Group); - } - else - { - if (_grp.Values.Count == 1) - { - _table.Add(_grp.Values[0], _grp.Group); - } - else - { - ArgumentException ex = new ArgumentException(UtilityCommonStrings.GroupObjectSingleProperty); - ErrorRecord er = new ErrorRecord(ex, "ArgumentException", ErrorCategory.InvalidArgument, Property); - ThrowTerminatingError(er); - } - } - } - } - catch (ArgumentException e) - { - WriteNonTerminatingError(e, UtilityCommonStrings.InvalidOperation, ErrorCategory.InvalidArgument); - return; - } - WriteObject(_table); - } - else - { - if (AsString) - { - ArgumentException ex = new ArgumentException(UtilityCommonStrings.GroupObjectWithHashTable); - ErrorRecord er = new ErrorRecord(ex, "ArgumentException", ErrorCategory.InvalidArgument, AsString); - ThrowTerminatingError(er); - } - else - { - WriteObject(_groups, true); - } - } - } - } - } -} - diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/new-object.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/new-object.cs deleted file mode 100644 index 5c3f136f2bb..00000000000 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/new-object.cs +++ /dev/null @@ -1,489 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -#region Using directives - -using System; -using System.Reflection; -using System.Collections; -using System.Runtime.InteropServices; -using System.Threading; -using System.Management.Automation; -using System.Management.Automation.Security; -using System.Management.Automation.Internal; -using System.Diagnostics.CodeAnalysis; -using System.Globalization; -using System.Management.Automation.Language; -using Dbg = System.Management.Automation.Diagnostics; - -#endregion - -namespace Microsoft.PowerShell.Commands -{ - /// Create a new .net object - [Cmdlet(VerbsCommon.New, "Object", DefaultParameterSetName = netSetName, HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113355")] - public sealed class NewObjectCommand : PSCmdlet - { - #region parameters - - /// the number - [Parameter(ParameterSetName = netSetName, Mandatory = true, Position = 0)] - public string TypeName { get; set; } = null; - - - private Guid _comObjectClsId = Guid.Empty; - /// the ProgID of the Com object - [Parameter(ParameterSetName = "Com", Mandatory = true, Position = 0)] - public string ComObject { get; set; } = null; - - - /// - /// The parameters for the constructor - /// - /// - [Parameter(ParameterSetName = netSetName, Mandatory = false, Position = 1)] - [Alias("Args")] - public object[] ArgumentList { get; set; } = null; - - /// - /// True if we should have an error when Com objects will use an interop assembly - /// - [Parameter(ParameterSetName = "Com")] - public SwitchParameter Strict { get; set; } - - // Updated from Hashtable to IDictionary to support the work around ordered hashtables. - /// - /// gets the properties to be set. - /// - [Parameter] - [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")] - public IDictionary Property { get; set; } - - # endregion parameters - - #region private - private object CallConstructor(Type type, ConstructorInfo[] constructors, object[] args) - { - object result = null; - try - { - result = DotNetAdapter.ConstructorInvokeDotNet(type, constructors, args); - } - catch (MethodException e) - { - ThrowTerminatingError( - new ErrorRecord( - e, - "ConstructorInvokedThrowException", - ErrorCategory.InvalidOperation, null)); - } - // let other exceptions propagate - return result; - } - - private void CreateMemberNotFoundError(PSObject pso, DictionaryEntry property, Type resultType) - { - string message = StringUtil.Format(NewObjectStrings.MemberNotFound, null, property.Key.ToString(), ParameterSet2ResourceString(ParameterSetName)); - - ThrowTerminatingError( - new ErrorRecord( - new InvalidOperationException(message), - "InvalidOperationException", - ErrorCategory.InvalidOperation, - null)); - } - - private void CreateMemberSetValueError(SetValueException e) - { - Exception ex = new Exception(StringUtil.Format(NewObjectStrings.InvalidValue, e)); - ThrowTerminatingError( - new ErrorRecord(ex, "SetValueException", ErrorCategory.InvalidData, null)); - } - - - private static string ParameterSet2ResourceString(string parameterSet) - { - if (parameterSet.Equals(netSetName, StringComparison.OrdinalIgnoreCase)) - { - return ".NET"; - } - else if (parameterSet.Equals("Com", StringComparison.OrdinalIgnoreCase)) - { - return "COM"; - } - else - { - Dbg.Assert(false, "Should never get here - unknown parameter set"); - return parameterSet; - } - } - - #endregion private - - #region Overrides - /// Create the object - protected override void BeginProcessing() - { - Type type = null; - PSArgumentException mshArgE = null; - - if (string.Compare(ParameterSetName, netSetName, StringComparison.Ordinal) == 0) - { - object _newObject = null; - try - { - type = LanguagePrimitives.ConvertTo(TypeName, typeof(Type), CultureInfo.InvariantCulture) as Type; - } - catch (Exception e) - { - // these complications in Exception handling are aim to make error messages better. - if (e is InvalidCastException || e is ArgumentException) - { - if (e.InnerException != null && e.InnerException is TypeResolver.AmbiguousTypeException) - { - ThrowTerminatingError( - new ErrorRecord( - e, - "AmbiguousTypeReference", - ErrorCategory.InvalidType, null)); - } - - mshArgE = PSTraceSource.NewArgumentException( - "TypeName", - NewObjectStrings.TypeNotFound, - TypeName); - ThrowTerminatingError( - new ErrorRecord( - mshArgE, - "TypeNotFound", - ErrorCategory.InvalidType, null)); - } - throw e; - } - - Diagnostics.Assert(type != null, "LanguagePrimitives.TryConvertTo failed but returned true"); - - if (Context.LanguageMode == PSLanguageMode.ConstrainedLanguage) - { - if (!CoreTypes.Contains(type)) - { - ThrowTerminatingError( - new ErrorRecord( - new PSNotSupportedException(NewObjectStrings.CannotCreateTypeConstrainedLanguage), "CannotCreateTypeConstrainedLanguage", ErrorCategory.PermissionDenied, null)); - } - } - - //WinRT does not support creating instances of attribute & delegate WinRT types. - if (WinRTHelper.IsWinRTType(type) && ((typeof(System.Attribute)).IsAssignableFrom(type) || (typeof(System.Delegate)).IsAssignableFrom(type))) - { - ThrowTerminatingError(new ErrorRecord(new InvalidOperationException(NewObjectStrings.CannotInstantiateWinRTType), - "CannotInstantiateWinRTType", ErrorCategory.InvalidOperation, null)); - } - - if (ArgumentList == null || ArgumentList.Length == 0) - { - ConstructorInfo ci = type.GetConstructor(PSTypeExtensions.EmptyTypes); - if (ci != null && ci.IsPublic) - { - _newObject = CallConstructor(type, new ConstructorInfo[] { ci }, new object[] { }); - if (_newObject != null && Property != null) - { - // The method invocation is disabled for "Hashtable to Object conversion" (Win8:649519), but we need to keep it enabled for New-Object for compatibility to PSv2 - _newObject = LanguagePrimitives.SetObjectProperties(_newObject, Property, type, CreateMemberNotFoundError, CreateMemberSetValueError, enableMethodCall: true); - } - WriteObject(_newObject); - return; - } - else if (type.GetTypeInfo().IsValueType) - { - // This is for default parameterless struct ctor which is not returned by - // Type.GetConstructor(System.Type.EmptyTypes). - try - { - _newObject = Activator.CreateInstance(type); - if (_newObject != null && Property != null) - { - // Win8:649519 - _newObject = LanguagePrimitives.SetObjectProperties(_newObject, Property, type, CreateMemberNotFoundError, CreateMemberSetValueError, enableMethodCall: true); - } - } - catch (TargetInvocationException e) - { - ThrowTerminatingError( - new ErrorRecord( - e.InnerException ?? e, - "ConstructorCalledThrowException", - ErrorCategory.InvalidOperation, null)); - } - WriteObject(_newObject); - return; - } - } - else - { - ConstructorInfo[] ctorInfos = type.GetConstructors(); - - if (ctorInfos.Length != 0) - { - _newObject = CallConstructor(type, ctorInfos, ArgumentList); - if (_newObject != null && Property != null) - { - // Win8:649519 - _newObject = LanguagePrimitives.SetObjectProperties(_newObject, Property, type, CreateMemberNotFoundError, CreateMemberSetValueError, enableMethodCall: true); - } - WriteObject(_newObject); - return; - } - } - - mshArgE = PSTraceSource.NewArgumentException( - "TypeName", NewObjectStrings.CannotFindAppropriateCtor, TypeName); - ThrowTerminatingError( - new ErrorRecord( - mshArgE, - "CannotFindAppropriateCtor", - ErrorCategory.ObjectNotFound, null)); - } - else // Parameterset -Com - { - int result = NewObjectNativeMethods.CLSIDFromProgID(ComObject, out _comObjectClsId); - - // If we're in ConstrainedLanguage, do additional restrictions - if (Context.LanguageMode == PSLanguageMode.ConstrainedLanguage) - { - bool isAllowed = false; - - // If it's a system-wide lockdown, we may allow additional COM types - if (SystemPolicy.GetSystemLockdownPolicy() == SystemEnforcementMode.Enforce) - { - if ((result >= 0) && - SystemPolicy.IsClassInApprovedList(_comObjectClsId)) - { - isAllowed = true; - } - } - - if (!isAllowed) - { - ThrowTerminatingError( - new ErrorRecord( - new PSNotSupportedException(NewObjectStrings.CannotCreateTypeConstrainedLanguage), "CannotCreateComTypeConstrainedLanguage", ErrorCategory.PermissionDenied, null)); - return; - } - } - - object comObject = CreateComObject(); - string comObjectTypeName = comObject.GetType().FullName; - if (!comObjectTypeName.Equals("System.__ComObject")) - { - mshArgE = PSTraceSource.NewArgumentException( - "TypeName", NewObjectStrings.ComInteropLoaded, comObjectTypeName); - WriteVerbose(mshArgE.Message); - if (Strict) - { - WriteError(new ErrorRecord( - mshArgE, - "ComInteropLoaded", - ErrorCategory.InvalidArgument, comObject)); - } - } - if (comObject != null && Property != null) - { - // Win8:649519 - comObject = LanguagePrimitives.SetObjectProperties(comObject, Property, type, CreateMemberNotFoundError, CreateMemberSetValueError, enableMethodCall: true); - } - WriteObject(comObject); - } - }//protected override void BeginProcessing() - - #endregion Overrides - - #region Com - - private object SafeCreateInstance(Type t, object[] args) - { - object result = null; - try - { - result = Activator.CreateInstance(t, args); - } - // Does not catch InvalidComObjectException because ComObject is obtained from GetTypeFromProgID - catch (ArgumentException e) - { - ThrowTerminatingError( - new ErrorRecord( - e, - "CannotNewNonRuntimeType", - ErrorCategory.InvalidOperation, null)); - } - catch (NotSupportedException e) - { - ThrowTerminatingError( - new ErrorRecord( - e, - "CannotNewTypeBuilderTypedReferenceArgIteratorRuntimeArgumentHandle", - ErrorCategory.InvalidOperation, null)); - } - catch (MethodAccessException e) - { - ThrowTerminatingError( - new ErrorRecord( - e, - "CtorAccessDenied", - ErrorCategory.PermissionDenied, null)); - } - catch (MissingMethodException e) - { - ThrowTerminatingError( - new ErrorRecord( - e, - "NoPublicCtorMatch", - ErrorCategory.InvalidOperation, null)); - } - catch (MemberAccessException e) - { - ThrowTerminatingError( - new ErrorRecord( - e, - "CannotCreateAbstractClass", - ErrorCategory.InvalidOperation, null)); - } - catch (COMException e) - { - if (e.HResult == RPC_E_CHANGED_MODE) - { - throw; - } - - ThrowTerminatingError( - new ErrorRecord( - e, - "NoCOMClassIdentified", - ErrorCategory.ResourceUnavailable, null)); - } - - return result; - } - -#if !CORECLR - private class ComCreateInfo - { - public Object objectCreated; - public bool success; - public Exception e; - } - - private ComCreateInfo createInfo; - - private void STAComCreateThreadProc(Object createstruct) - { - ComCreateInfo info = (ComCreateInfo)createstruct; - try - { - Type type = null; - PSArgumentException mshArgE = null; - - type = Type.GetTypeFromCLSID(_comObjectClsId); - if (type == null) - { - mshArgE = PSTraceSource.NewArgumentException( - "ComObject", - NewObjectStrings.CannotLoadComObjectType, - ComObject); - - info.e = mshArgE; - info.success = false; - return; - } - info.objectCreated = SafeCreateInstance(type, ArgumentList); - info.success = true; - } - catch (Exception e) - { - info.e = e; - info.success = false; - } - } -#endif - - private object CreateComObject() - { - Type type = null; - PSArgumentException mshArgE = null; - - try - { - type = Marshal.GetTypeFromCLSID(_comObjectClsId); - if (type == null) - { - mshArgE = PSTraceSource.NewArgumentException("ComObject", NewObjectStrings.CannotLoadComObjectType, ComObject); - ThrowTerminatingError( - new ErrorRecord(mshArgE, "CannotLoadComObjectType", ErrorCategory.InvalidType, null)); - } - return SafeCreateInstance(type, ArgumentList); - } - catch (COMException e) - { - //Check Error Code to see if Error is because of Com apartment Mismatch. - if (e.HResult == RPC_E_CHANGED_MODE) - { -#if CORECLR - ThrowTerminatingError( - new ErrorRecord( - new COMException(StringUtil.Format(NewObjectStrings.ApartmentNotSupported, e.Message), e), - "NoCOMClassIdentified", - ErrorCategory.ResourceUnavailable, null)); -#else - createInfo = new ComCreateInfo(); - - Thread thread = new Thread(new ParameterizedThreadStart(STAComCreateThreadProc)); - thread.SetApartmentState(ApartmentState.STA); - thread.Start(createInfo); - - thread.Join(); - - if (createInfo.success == true) - { - return createInfo.objectCreated; - } - - ThrowTerminatingError( - new ErrorRecord(createInfo.e, "NoCOMClassIdentified", - ErrorCategory.ResourceUnavailable, null)); -#endif - } - else - { - ThrowTerminatingError( - new ErrorRecord( - e, - "NoCOMClassIdentified", - ErrorCategory.ResourceUnavailable, null)); - } - - return null; - } - } - - #endregion Com - - // HResult code '-2147417850' - Cannot change thread mode after it is set. - private const int RPC_E_CHANGED_MODE = unchecked((int)0x80010106); - private const string netSetName = "Net"; - }//internal class NewObjectCommand: PSCmdlet - - /// - /// Native methods for dealing with COM objects - /// - internal class NewObjectNativeMethods - { - private NewObjectNativeMethods() - { - } - - /// Return Type: HRESULT->LONG->int - [DllImport(PinvokeDllNames.CLSIDFromProgIDDllName)] - internal static extern int CLSIDFromProgID([MarshalAs(UnmanagedType.LPWStr)] string lpszProgID, out Guid pclsid); - } -} diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/neweventcommand.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/neweventcommand.cs deleted file mode 100644 index 4584ffb9977..00000000000 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/neweventcommand.cs +++ /dev/null @@ -1,124 +0,0 @@ -// -// Copyright (C) Microsoft. All rights reserved. -// - -using System; -using System.Diagnostics.CodeAnalysis; -using System.Management.Automation; - -namespace Microsoft.PowerShell.Commands -{ - /// - /// Generates a new event notification. - /// - [Cmdlet(VerbsCommon.New, "Event", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=135234")] - [OutputType(typeof(PSEventArgs))] - public class NewEventCommand : PSCmdlet - { - #region parameters - - /// - /// Adds an event to the event queue - /// - [Parameter(Position = 0, Mandatory = true)] - public string SourceIdentifier - { - get - { - return _sourceIdentifier; - } - set - { - _sourceIdentifier = value; - } - } - private string _sourceIdentifier = null; - - /// - /// Data relating to this event - /// - [Parameter(Position = 1)] - [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] - public PSObject Sender - { - get - { - return _sender; - } - set - { - _sender = value; - } - } - private PSObject _sender = null; - - /// - /// Data relating to this event - /// - [Parameter(Position = 2)] - [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] - public PSObject[] EventArguments - { - get - { - return _eventArguments; - } - set - { - if (_eventArguments != null) - { - _eventArguments = value; - } - } - } - private PSObject[] _eventArguments = new PSObject[0]; - - /// - /// Data relating to this event - /// - [Parameter(Position = 3)] - public PSObject MessageData - { - get - { - return _messageData; - } - set - { - _messageData = value; - } - } - private PSObject _messageData = null; - - #endregion parameters - - - /// - /// Add the event to the event queue - /// - protected override void EndProcessing() - { - object[] baseEventArgs = null; - - // Get the BaseObject from the event arguments - if (_eventArguments != null) - { - baseEventArgs = new object[_eventArguments.Length]; - int loopCounter = 0; - foreach (PSObject eventArg in _eventArguments) - { - if (eventArg != null) - baseEventArgs[loopCounter] = eventArg.BaseObject; - - loopCounter++; - } - } - - Object messageSender = null; - if (_sender != null) { messageSender = _sender.BaseObject; } - - // And then generate the event - WriteObject(Events.GenerateEvent(_sourceIdentifier, messageSender, baseEventArgs, _messageData, true, false)); - } - } -} \ No newline at end of file diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/select-object.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/select-object.cs deleted file mode 100644 index dcc86a8e887..00000000000 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/select-object.cs +++ /dev/null @@ -1,776 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -using System; -using System.Collections.Generic; -using System.Globalization; -using System.Management.Automation; -using Microsoft.PowerShell.Commands.Internal.Format; -using System.Management.Automation.Internal; -using System.Diagnostics.CodeAnalysis; - -namespace Microsoft.PowerShell.Commands -{ - /// - /// helper class to do wildcard matching on MshExpressions - /// - internal sealed class MshExpressionFilter - { - /// - /// construct the class, using an array of patterns - /// - /// array of pattern strings to use - internal MshExpressionFilter(string[] wildcardPatternsStrings) - { - if (wildcardPatternsStrings == null) - { - throw new ArgumentNullException("wildcardPatternsStrings"); - } - - _wildcardPatterns = new WildcardPattern[wildcardPatternsStrings.Length]; - for (int k = 0; k < wildcardPatternsStrings.Length; k++) - { - _wildcardPatterns[k] = WildcardPattern.Get(wildcardPatternsStrings[k], WildcardOptions.IgnoreCase); - } - } - - /// - /// try to match the expression against the array of wildcard patterns. - /// the first match shortcircuits the search - /// - /// MshExpression to test against - /// true if there is a match, else false - internal bool IsMatch(MshExpression expression) - { - for (int k = 0; k < _wildcardPatterns.Length; k++) - { - if (_wildcardPatterns[k].IsMatch(expression.ToString())) - return true; - } - return false; - } - - private WildcardPattern[] _wildcardPatterns; - } - - internal class SelectObjectExpressionParameterDefinition : CommandParameterDefinition - { - protected override void SetEntries() - { - this.hashEntries.Add(new ExpressionEntryDefinition()); - this.hashEntries.Add(new NameEntryDefinition()); - } - } - - /// - /// - /// - [Cmdlet(VerbsCommon.Select, "Object", DefaultParameterSetName = "DefaultParameter", - HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113387", RemotingCapability = RemotingCapability.None)] - public sealed class SelectObjectCommand : PSCmdlet - { - #region Command Line Switches - - /// - /// - /// - /// - [Parameter(ValueFromPipeline = true)] - public PSObject InputObject { set; get; } = AutomationNull.Value; - - - /// - /// - /// - /// - [Parameter(Position = 0, ParameterSetName = "DefaultParameter")] - [Parameter(Position = 0, ParameterSetName = "SkipLastParameter")] - public object[] Property { get; set; } - - /// - /// - /// - /// - [Parameter(ParameterSetName = "DefaultParameter")] - [Parameter(ParameterSetName = "SkipLastParameter")] - public string[] ExcludeProperty { get; set; } = null; - - /// - /// - /// - /// - [Parameter(ParameterSetName = "DefaultParameter")] - [Parameter(ParameterSetName = "SkipLastParameter")] - public string ExpandProperty { get; set; } = null; - - /// - /// - /// - /// - [Parameter] - public SwitchParameter Unique - { - get { return _unique; } - set { _unique = value; } - } - private bool _unique; - - /// - /// - /// - /// - [Parameter(ParameterSetName = "DefaultParameter")] - // NTRAID#Windows Out Of Band Releases-927878-2006/03/02 - // Allow zero - [ValidateRange(0, int.MaxValue)] - public int Last - { - get { return _last; } - set { _last = value; _firstOrLastSpecified = true; } - } - private int _last = 0; - - /// - /// - /// - /// - [Parameter(ParameterSetName = "DefaultParameter")] - // NTRAID#Windows Out Of Band Releases-927878-2006/03/02 - // Allow zero - [ValidateRange(0, int.MaxValue)] - public int First - { - get { return _first; } - set { _first = value; _firstOrLastSpecified = true; } - } - private int _first = 0; - private bool _firstOrLastSpecified; - - - /// - /// Skips the specified number of items from top when used with First,from end when used with Last - /// - /// - [Parameter(ParameterSetName = "DefaultParameter")] - [ValidateRange(0, int.MaxValue)] - public int Skip { get; set; } = 0; - - /// - /// Skip the specified number of items from end. - /// - [Parameter(ParameterSetName = "SkipLastParameter")] - [ValidateRange(0, int.MaxValue)] - public int SkipLast { get; set; } = 0; - - /// - /// With this switch present, the cmdlet won't "short-circuit" - /// (i.e. won't stop upstream cmdlets after it knows that no further objects will be emitted downstream) - /// - [Parameter(ParameterSetName = "DefaultParameter")] - [Parameter(ParameterSetName = "IndexParameter")] - public SwitchParameter Wait { get; set; } - - /// - /// Used to display the object at specified index - /// - /// - [Parameter(ParameterSetName = "IndexParameter")] - [ValidateRangeAttribute(0, int.MaxValue)] - [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] - public int[] Index - { - get - { - return _index; - } - set - { - _index = value; - _indexSpecified = true; - Array.Sort(_index); - } - } - private int[] _index; - private bool _indexSpecified; - - #endregion - - private SelectObjectQueue _selectObjectQueue; - - private class SelectObjectQueue : Queue - { - internal SelectObjectQueue(int first, int last, int skip, int skipLast, bool firstOrLastSpecified) - { - _first = first; - _last = last; - _skip = skip; - _skipLast = skipLast; - _firstOrLastSpecified = firstOrLastSpecified; - } - - public bool AllRequestedObjectsProcessed - { - get - { - return _firstOrLastSpecified && _last == 0 && _first != 0 && _streamedObjectCount >= _first; - } - } - - public new void Enqueue(PSObject obj) - { - if (_last > 0 && this.Count >= (_last + _skip) && _first == 0) - { - base.Dequeue(); - } - else if (_last > 0 && this.Count >= _last && _first != 0) - { - base.Dequeue(); - } - base.Enqueue(obj); - } - - public PSObject StreamingDequeue() - { - //if skip parameter is not mentioned or there are no more objects to skip - if (_skip == 0) - { - if (_skipLast > 0) - { - // We are going to skip some items from end, but it's okay to process - // the early input objects once we have more items in queue than the - // specified 'skipLast' value. - if (this.Count > _skipLast) - { - return Dequeue(); - } - } - else - { - if (_streamedObjectCount < _first || !_firstOrLastSpecified) - { - Diagnostics.Assert(this.Count > 0, "Streaming an empty queue"); - _streamedObjectCount++; - return Dequeue(); - } - - if (_last == 0) - { - Dequeue(); - } - } - } - else - { - //if last parameter is not mentioned,remove the objects and decrement the skip - if (_last == 0) - { - Dequeue(); - _skip--; - } - else if (_first != 0) - { - _skip--; - Dequeue(); - } - } - - return null; - } - - private int _streamedObjectCount; - private int _first,_last,_skip,_skipLast; - private bool _firstOrLastSpecified; - } - - /// - /// list of processed parameters obtained from the Expression array - /// - private List _propertyMshParameterList; - - /// - /// singleton list of process parameters obtained from ExpandProperty - /// - private List _expandMshParameterList; - - - - private MshExpressionFilter _exclusionFilter; - - private class UniquePSObjectHelper - { - internal UniquePSObjectHelper(PSObject o, int notePropertyCount) - { - WrittenObject = o; - NotePropertyCount = notePropertyCount; - } - internal readonly PSObject WrittenObject; - internal int NotePropertyCount { get; } - } - - private List _uniques = null; - - private void ProcessExpressionParameter() - { - TerminatingErrorContext invocationContext = new TerminatingErrorContext(this); - ParameterProcessor processor = - new ParameterProcessor(new SelectObjectExpressionParameterDefinition()); - if ((Property != null) && (Property.Length != 0)) - { - // Build property list taking into account the wildcards and @{name=;expression=} - _propertyMshParameterList = processor.ProcessParameters(Property, invocationContext); - } - else - { - // Property don't exist - _propertyMshParameterList = new List(); - } - - if (!string.IsNullOrEmpty(ExpandProperty)) - { - _expandMshParameterList = processor.ProcessParameters(new string[] { ExpandProperty }, invocationContext); - } - - if (ExcludeProperty != null) - { - _exclusionFilter = new MshExpressionFilter(ExcludeProperty); - // ExcludeProperty implies -Property * for better UX - if ((Property == null) || (Property.Length == 0)) - { - Property = new Object[]{"*"}; - _propertyMshParameterList = processor.ProcessParameters(Property, invocationContext); - } - } - } - - private void ProcessObject(PSObject inputObject) - { - if ((Property == null || Property.Length == 0) && string.IsNullOrEmpty(ExpandProperty)) - { - FilteredWriteObject(inputObject, new List()); - return; - } - - - //If property parameter is mentioned - List matchedProperties = new List(); - foreach (MshParameter p in _propertyMshParameterList) - { - ProcessParameter(p, inputObject, matchedProperties); - } - - if (string.IsNullOrEmpty(ExpandProperty)) - { - PSObject result = new PSObject(); - if (matchedProperties.Count != 0) - { - HashSet propertyNames = new HashSet(StringComparer.OrdinalIgnoreCase); - - foreach (PSNoteProperty noteProperty in matchedProperties) - { - try - { - if (!propertyNames.Contains(noteProperty.Name)) - { - propertyNames.Add(noteProperty.Name); - result.Properties.Add(noteProperty); - } - else - { - WriteAlreadyExistingPropertyError(noteProperty.Name, inputObject, - "AlreadyExistingUserSpecifiedPropertyNoExpand"); - } - } - catch (ExtendedTypeSystemException) - { - WriteAlreadyExistingPropertyError(noteProperty.Name, inputObject, - "AlreadyExistingUserSpecifiedPropertyNoExpand"); - } - } - } - FilteredWriteObject(result, matchedProperties); - } - else - { - ProcessExpandParameter(_expandMshParameterList[0], inputObject, matchedProperties); - } - } - - - - private void ProcessParameter(MshParameter p, PSObject inputObject, List result) - { - string name = p.GetEntry(NameEntryDefinition.NameEntryKey) as string; - - MshExpression ex = p.GetEntry(FormatParameterDefinitionKeys.ExpressionEntryKey) as MshExpression; - List expressionResults = new List(); - foreach (MshExpression resolvedName in ex.ResolveNames(inputObject)) - { - if (_exclusionFilter == null || !_exclusionFilter.IsMatch(resolvedName)) - { - List tempExprResults = resolvedName.GetValues(inputObject); - if (tempExprResults == null) continue; - foreach (MshExpressionResult mshExpRes in tempExprResults) - { - expressionResults.Add(mshExpRes); - } - } - } - - // allow 'Select-Object -Property noexist-name' to return a PSObject with property noexist-name, - // unless noexist-name itself contains wildcards - if (expressionResults.Count == 0 && !ex.HasWildCardCharacters) - { - expressionResults.Add(new MshExpressionResult(null, ex, null)); - } - - // if we have an expansion, renaming is not acceptable - else if (!string.IsNullOrEmpty(name) && expressionResults.Count > 1) - { - string errorMsg = SelectObjectStrings.RenamingMultipleResults; - ErrorRecord errorRecord = new ErrorRecord( - new InvalidOperationException(errorMsg), - "RenamingMultipleResults", - ErrorCategory.InvalidOperation, - inputObject); - WriteError(errorRecord); - return; - } - - foreach (MshExpressionResult r in expressionResults) - { - // filter the exclusions, if any - if (_exclusionFilter != null && _exclusionFilter.IsMatch(r.ResolvedExpression)) - continue; - - PSNoteProperty mshProp; - if (string.IsNullOrEmpty(name)) - { - string resolvedExpressionName = r.ResolvedExpression.ToString(); - if (string.IsNullOrEmpty(resolvedExpressionName)) - { - PSArgumentException mshArgE = PSTraceSource.NewArgumentException( - "Property", - SelectObjectStrings.EmptyScriptBlockAndNoName); - ThrowTerminatingError( - new ErrorRecord( - mshArgE, - "EmptyScriptBlockAndNoName", - ErrorCategory.InvalidArgument, null)); - } - mshProp = new PSNoteProperty(resolvedExpressionName, r.Result); - } - else - { - mshProp = new PSNoteProperty(name, r.Result); - } - result.Add(mshProp); - } - } - private void ProcessExpandParameter(MshParameter p, PSObject inputObject, - List matchedProperties) - { - MshExpression ex = p.GetEntry(FormatParameterDefinitionKeys.ExpressionEntryKey) as MshExpression; - List expressionResults = ex.GetValues(inputObject); - - - if (expressionResults.Count == 0) - { - ErrorRecord errorRecord = new ErrorRecord( - PSTraceSource.NewArgumentException("ExpandProperty", SelectObjectStrings.PropertyNotFound, ExpandProperty), - "ExpandPropertyNotFound", - ErrorCategory.InvalidArgument, - inputObject); - throw new SelectObjectException(errorRecord); - } - if (expressionResults.Count > 1) - { - ErrorRecord errorRecord = new ErrorRecord( - PSTraceSource.NewArgumentException("ExpandProperty", SelectObjectStrings.MutlipleExpandProperties, ExpandProperty), - "MutlipleExpandProperties", - ErrorCategory.InvalidArgument, - inputObject); - throw new SelectObjectException(errorRecord); - } - - MshExpressionResult r = expressionResults[0]; - if (r.Exception == null) - { - // ignore the property value if it's null - if (r.Result == null) { return; } - - System.Collections.IEnumerable results = LanguagePrimitives.GetEnumerable(r.Result); - if (results == null) - { - // add NoteProperties if there is any - // If r.Result is a base object, we don't want to associate the NoteProperty - // directly with it. We want the NoteProperty to be associated only with this - // particular PSObject, so that when the user uses the base object else where, - // its members remain the same as before the Select-Object command run. - PSObject expandedObject = PSObject.AsPSObject(r.Result, true); - AddNoteProperties(expandedObject, inputObject, matchedProperties); - - FilteredWriteObject(expandedObject, matchedProperties); - return; - } - - foreach (object expandedValue in results) - { - // ignore the element if it's null - if (expandedValue == null) { continue; } - - // add NoteProperties if there is any - // If expandedValue is a base object, we don't want to associate the NoteProperty - // directly with it. We want the NoteProperty to be associated only with this - // particular PSObject, so that when the user uses the base object else where, - // its members remain the same as before the Select-Object command run. - PSObject expandedObject = PSObject.AsPSObject(expandedValue, true); - AddNoteProperties(expandedObject, inputObject, matchedProperties); - - FilteredWriteObject(expandedObject, matchedProperties); - } - } - else - { - ErrorRecord errorRecord = new ErrorRecord( - r.Exception, - "PropertyEvaluationExpand", - ErrorCategory.InvalidResult, - inputObject); - throw new SelectObjectException(errorRecord); - } - } - - private void AddNoteProperties(PSObject expandedObject, PSObject inputObject, IEnumerable matchedProperties) - { - foreach (PSNoteProperty noteProperty in matchedProperties) - { - try - { - if (expandedObject.Properties[noteProperty.Name] != null) - { - WriteAlreadyExistingPropertyError(noteProperty.Name, inputObject, "AlreadyExistingUserSpecifiedPropertyExpand"); - } - else - { - expandedObject.Properties.Add(noteProperty); - } - } - catch (ExtendedTypeSystemException) - { - WriteAlreadyExistingPropertyError(noteProperty.Name, inputObject, "AlreadyExistingUserSpecifiedPropertyExpand"); - } - } - } - - private void WriteAlreadyExistingPropertyError(string name, object inputObject, string errorId) - { - ErrorRecord errorRecord = new ErrorRecord( - PSTraceSource.NewArgumentException("Property", SelectObjectStrings.AlreadyExistingProperty, name), - errorId, - ErrorCategory.InvalidOperation, - inputObject); - WriteError(errorRecord); - } - private void FilteredWriteObject(PSObject obj, List addedNoteProperties) - { - Diagnostics.Assert(obj != null, "This command should never write null"); - - if (!_unique) - { - if (obj != AutomationNull.Value) - { - SetPSCustomObject(obj); - WriteObject(obj); - } - return; - } - //if only unique is mentioned - else if ((_unique)) - { - bool isObjUnique = true; - foreach (UniquePSObjectHelper uniqueObj in _uniques) - { - ObjectCommandComparer comparer = new ObjectCommandComparer(true, CultureInfo.CurrentCulture, true); - if ((comparer.Compare(obj.BaseObject, uniqueObj.WrittenObject.BaseObject) == 0) && - (uniqueObj.NotePropertyCount == addedNoteProperties.Count)) - { - bool found = true; - foreach (PSNoteProperty note in addedNoteProperties) - { - PSMemberInfo prop = uniqueObj.WrittenObject.Properties[note.Name]; - if (prop == null || comparer.Compare(prop.Value, note.Value) != 0) - { - found = false; - break; - } - } - if (found) - { - isObjUnique = false; - break; - } - } - else - { - continue; - } - } - if (isObjUnique) - { - SetPSCustomObject(obj); - _uniques.Add(new UniquePSObjectHelper(obj, addedNoteProperties.Count)); - } - } - } - - private void SetPSCustomObject(PSObject psObj) - { - if (psObj.ImmediateBaseObject is PSCustomObject) - psObj.TypeNames.Insert(0, "Selected." + InputObject.BaseObject.GetType().ToString()); - } - - private void ProcessObjectAndHandleErrors(PSObject pso) - { - Diagnostics.Assert(pso != null, "Caller should verify pso != null"); - - try - { - ProcessObject(pso); - } - catch (SelectObjectException e) - { - WriteError(e.ErrorRecord); - } - } - - /// - /// - /// - protected override void BeginProcessing() - { - ProcessExpressionParameter(); - - if (_unique) - { - _uniques = new List(); - } - - _selectObjectQueue = new SelectObjectQueue(_first, _last, Skip, SkipLast, _firstOrLastSpecified); - } - - private int _indexOfCurrentObject = 0; - private int _indexCount = 0; - /// - /// - /// - protected override void ProcessRecord() - { - if (InputObject != AutomationNull.Value && InputObject != null) - { - if (!_indexSpecified) - { - _selectObjectQueue.Enqueue(InputObject); - PSObject streamingInputObject = _selectObjectQueue.StreamingDequeue(); - if (streamingInputObject != null) - { - ProcessObjectAndHandleErrors(streamingInputObject); - } - if (_selectObjectQueue.AllRequestedObjectsProcessed && !this.Wait) - { - this.EndProcessing(); - throw new StopUpstreamCommandsException(this); - } - } - else - { - if (_indexOfCurrentObject < _index.Length) - { - int currentlyRequestedIndex = _index[_indexOfCurrentObject]; - if (_indexCount == currentlyRequestedIndex) - { - ProcessObjectAndHandleErrors(InputObject); - while ((_indexOfCurrentObject < _index.Length) && (_index[_indexOfCurrentObject] == currentlyRequestedIndex)) - { - _indexOfCurrentObject++; - } - } - } - - if (!this.Wait && _indexOfCurrentObject >= _index.Length) - { - this.EndProcessing(); - throw new StopUpstreamCommandsException(this); - } - - _indexCount++; - } - } - } - - /// - /// - /// - protected override void EndProcessing() - { - // We can skip this part for 'IndexParameter' and 'SkipLastParameter' sets because: - // 1. 'IndexParameter' set doesn't use selectObjectQueue. - // 2. 'SkipLastParameter' set should have processed all valid input in the ProcessRecord. - if (ParameterSetName == "DefaultParameter") - { - if (_first != 0) - { - while ((_selectObjectQueue.Count > 0)) - { - ProcessObjectAndHandleErrors(_selectObjectQueue.Dequeue()); - } - } - else - { - while ((_selectObjectQueue.Count > 0)) - { - int lenQueue = _selectObjectQueue.Count; - if (lenQueue > Skip) - { - ProcessObjectAndHandleErrors(_selectObjectQueue.Dequeue()); - } - else - { - break; - } - } - } - } - - if (_uniques != null) - { - foreach (UniquePSObjectHelper obj in _uniques) - { - if (obj.WrittenObject == null || obj.WrittenObject == AutomationNull.Value) - { - continue; - } - - WriteObject(obj.WrittenObject); - } - } - } - } - - /// - /// Used only internally for select-object - /// - [SuppressMessage("Microsoft.Usage", "CA2237:MarkISerializableTypesWithSerializable", Justification = "This exception is internal and never thrown by any public API")] - [SuppressMessage("Microsoft.Design", "CA1032:ImplementStandardExceptionConstructors", Justification = "This exception is internal and never thrown by any public API")] - [SuppressMessage("Microsoft.Design", "CA1064:ExceptionsShouldBePublic", Justification = "This exception is internal and never thrown by any public API")] - internal class SelectObjectException : SystemException - { - internal ErrorRecord ErrorRecord { get; } - - internal SelectObjectException(ErrorRecord errorRecord) - { - ErrorRecord = errorRecord; - } - } -} - diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/sort-object.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/sort-object.cs deleted file mode 100644 index 1ccbbe0483b..00000000000 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/sort-object.cs +++ /dev/null @@ -1,264 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -using System.Collections.Generic; -using System.Management.Automation; - -namespace Microsoft.PowerShell.Commands -{ - /// - /// - /// - [Cmdlet("Sort", - "Object", - HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113403", - DefaultParameterSetName="Default", - RemotingCapability = RemotingCapability.None)] - public sealed class SortObjectCommand : OrderObjectBase - { - #region Command Line Switches - /// - /// This param specifies if sort order is ascending. - /// - [Parameter] - public SwitchParameter Descending - { - get { return DescendingOrder; } - set { DescendingOrder = value; } - } - /// - /// This param specifies if only unique objects are filtered. - /// - /// - [Parameter] - public SwitchParameter Unique - { - get { return _unique; } - set { _unique = value; } - } - private bool _unique; - #endregion - - /// - /// This param specifies you only want the top N items returned. - /// - [Parameter(ParameterSetName="Default")] - [ValidateRange(1,int.MaxValue)] - public int Top { get; set; } = 0; - - /// - /// This param specifies you only want the bottom N items returned. - /// - [Parameter(ParameterSetName="Bottom", Mandatory=true)] - [ValidateRange(1,int.MaxValue)] - public int Bottom { get; set; } = 0; - - /// - /// Moves unique entries to the front of the list. - /// - private int MoveUniqueEntriesToFront(List sortedData, OrderByPropertyComparer comparer) - { - // If we have sorted data then we know we have at least one unique item - int uniqueCount = sortedData.Count > 0 ? 1 : 0; - - // Move the first of each unique entry to the front of the list - for (int uniqueItemIndex = 0, nextUniqueItemIndex = 1; uniqueItemIndex < sortedData.Count && uniqueCount != Top; uniqueItemIndex++, nextUniqueItemIndex++) - { - // Identify the index of the next unique item - while (nextUniqueItemIndex < sortedData.Count && comparer.Compare(sortedData[uniqueItemIndex], sortedData[nextUniqueItemIndex]) == 0) - { - nextUniqueItemIndex++; - } - - // If there are no more unique items, break - if (nextUniqueItemIndex == sortedData.Count) - { - break; - } - - // Move the next unique item forward and increment the unique item counter - sortedData[uniqueItemIndex + 1] = sortedData[nextUniqueItemIndex]; - uniqueCount++; - } - - return uniqueCount; - } - - /// - /// Sort unsorted OrderByPropertyEntry data using a full sort - /// - private int FullSort(List dataToSort, OrderByPropertyComparer comparer) - { - // Track how many items in the list are sorted - int sortedItemCount = dataToSort.Count; - - // Future: It may be worth comparing List.Sort with SortedSet when handling unique - // records in case SortedSet is faster (SortedSet was not an option in earlier - // versions of PowerShell). - dataToSort.Sort(comparer); - - if (_unique) - { - // Move unique entries to the front of the list (this is significantly faster - // than removing them) - sortedItemCount = MoveUniqueEntriesToFront(dataToSort, comparer); - } - - return sortedItemCount; - } - - /// - /// Sort unsorted OrderByPropertyEntry data using an indexed min-/max-heap sort - /// - private int Heapify(List dataToSort, OrderByPropertyComparer orderByPropertyComparer) - { - // Instantiate the Heapify comparer, which takes index into account for sort stability - var comparer = new IndexedOrderByPropertyComparer(orderByPropertyComparer); - - // Identify how many items will be in the heap and the current number of items - int heapCount = 0; - int heapCapacity = Top > 0 ? Top : Bottom; - - // Identify the comparator (the value all comparisons will be made against based on whether we're - // doing a Top N or Bottom N sort) - // Note: All comparison results in the loop below are performed related to the value of the - // comparator. OrderByPropertyComparer.Compare will return -1 to indicate that the lhs is smaller - // if an ascending sort is being executed, or -1 to indicate that the lhs is larger if a descending - // sort is being executed. The comparator will be -1 if we're executing a Top N sort, or 1 if we're - // executing a Bottom N sort. These two pairs of states allow us to perform the proper comparison - // regardless of whether we're executing an ascending or descending Top N or Bottom N sort. This - // allows us to build a min-heap or max-heap for each of these sorts with the exact same logic. - // Min-heap: used for faster processing of a top N descending sort and a bottom N ascending sort - // Max-heap: used for faster processing of a top N ascending sort and a bottom N descending sort - int comparator = Top > 0 ? -1 : 1; - - // For unique sorts, use a sorted set to avoid adding unique items to the heap - SortedSet uniqueSet = _unique ? new SortedSet(orderByPropertyComparer) : null; - - // Tracking the index is necessary so that unsortable items can be output at the end, in the order - // in which they were received. - for (int dataIndex = 0, discardedDuplicates = 0; dataIndex < dataToSort.Count - discardedDuplicates; dataIndex++) - { - // Min-heap: if the heap is full and the root item is larger than the entry, discard the entry - // Max-heap: if the heap is full and the root item is smaller than the entry, discard the entry - if (heapCount == heapCapacity && comparer.Compare(dataToSort[0], dataToSort[dataIndex]) == comparator) - { - continue; - } - - // If we're doing a unique sort and the entry is not unique, discard the duplicate entry - if (_unique && !uniqueSet.Add(dataToSort[dataIndex])) - { - discardedDuplicates++; - if (dataIndex != dataToSort.Count - discardedDuplicates) - { - // When discarding duplicates, replace them with an item at the end of the list and - // adjust our counter so that we check the item we just swapped in next - dataToSort[dataIndex] = dataToSort[dataToSort.Count - discardedDuplicates]; - dataIndex--; - } - continue; - } - - // Add the current item to the heap and bubble it up into the correct position - int childIndex = dataIndex; - while (childIndex > 0) - { - int parentIndex = ((childIndex > (heapCapacity - 1) ? heapCapacity : childIndex) - 1) >> 1; - - // Min-heap: if the child item is larger than its parent, break - // Max-heap: if the child item is smaller than its parent, break - if (comparer.Compare(dataToSort[childIndex], dataToSort[parentIndex]) == comparator) - { - break; - } - - var temp = dataToSort[parentIndex]; - dataToSort[parentIndex] = dataToSort[childIndex]; - dataToSort[childIndex] = temp; - - childIndex = parentIndex; - } - heapCount++; - - // If the heap size is too large, remove the root and rearrange the heap - if (heapCount > heapCapacity) - { - // Move the last item to the root and reset the heap count (this effectively removes the last item) - dataToSort[0] = dataToSort[dataIndex]; - heapCount = heapCapacity; - - // Bubble the root item down into the correct position - int parentIndex = 0; - int parentItemCount = heapCapacity >> 1; - while (parentIndex < parentItemCount) - { - // Min-heap: use the smaller of the two children in the comparison - // Max-heap: use the larger of the two children in the comparison - int leftChildIndex = (parentIndex << 1) + 1; - int rightChildIndex = leftChildIndex + 1; - childIndex = rightChildIndex == heapCapacity || comparer.Compare(dataToSort[leftChildIndex], dataToSort[rightChildIndex]) != comparator - ? leftChildIndex - : rightChildIndex; - - // Min-heap: if the smallest child is larger than or equal to its parent, break - // Max-heap: if the largest child is smaller than or equal to its parent, break - int childComparisonResult = comparer.Compare(dataToSort[childIndex], dataToSort[parentIndex]); - if (childComparisonResult == 0 || childComparisonResult == comparator) - { - break; - } - - var temp = dataToSort[childIndex]; - dataToSort[childIndex] = dataToSort[parentIndex]; - dataToSort[parentIndex] = temp; - - parentIndex = childIndex; - } - } - } - - dataToSort.Sort(0, heapCount, comparer); - - return heapCount; - } - - /// - /// - /// - protected override void EndProcessing() - { - OrderByProperty orderByProperty = new OrderByProperty( - this, InputObjects, Property, !Descending, ConvertedCulture, CaseSensitive); - - var dataToProcess = orderByProperty.OrderMatrix; - var comparer = orderByProperty.Comparer; - if (comparer == null || dataToProcess == null || dataToProcess.Count == 0) - { - return; - } - - // Track the number of items that will be output from the data once it is sorted - int sortedItemCount = dataToProcess.Count; - - // If -Top & -Bottom were not used, or if -Top or -Bottom would return all objects, invoke - // an in-place full sort - if ((Top == 0 && Bottom == 0) || Top >= dataToProcess.Count || Bottom >= dataToProcess.Count) - { - sortedItemCount = FullSort(dataToProcess, comparer); - } - // Otherwise, use an indexed min-/max-heap to perform an in-place sort of all objects - else - { - sortedItemCount = Heapify(dataToProcess, comparer); - } - - // Write out the portion of the processed data that was sorted - for (int index = 0; index < sortedItemCount; index++) - { - WriteObject(dataToProcess[index].inputObject); - } - } - } -} diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/tee-object.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/tee-object.cs deleted file mode 100644 index 3892848769f..00000000000 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/tee-object.cs +++ /dev/null @@ -1,157 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -using System; -using System.Management.Automation; -using Microsoft.PowerShell.Commands.Internal.Format; - -namespace Microsoft.PowerShell.Commands -{ - /// - /// Class for Tee-object implementation - /// - [Cmdlet("Tee", "Object", DefaultParameterSetName = "File", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113417")] - public sealed class TeeObjectCommand : PSCmdlet, IDisposable - { - /// - /// object to process - /// - [Parameter(ValueFromPipeline = true)] - public PSObject InputObject - { - get { return _inputObject; } - set { _inputObject = value; } - } - private PSObject _inputObject; - - /// - /// FilePath parameter - /// - [Parameter(Mandatory = true, Position = 0, ParameterSetName = "File")] - public string FilePath - { - get { return _fileName; } - set { _fileName = value; } - } - private string _fileName; - - /// - /// Literal FilePath parameter - /// - [Parameter(Mandatory = true, ParameterSetName = "LiteralFile")] - [Alias("PSPath")] - public string LiteralPath - { - get - { - return _fileName; - } - set - { - _fileName = value; - } - } - - /// - /// Append switch - /// - [Parameter(ParameterSetName = "File")] - public SwitchParameter Append - { - get { return _append; } - set { _append = value; } - } - private bool _append; - - /// - /// Variable parameter - /// - [Parameter(Mandatory = true, ParameterSetName = "Variable")] - public string Variable - { - get { return _variable; } - set { _variable = value; } - } - private string _variable; - - /// - /// - /// - protected override void BeginProcessing() - { - _commandWrapper = new CommandWrapper(); - if (String.Equals(ParameterSetName, "File", StringComparison.OrdinalIgnoreCase)) - { - _commandWrapper.Initialize(Context, "out-file", typeof(OutFileCommand)); - _commandWrapper.AddNamedParameter("filepath", _fileName); - _commandWrapper.AddNamedParameter("append", _append); - } - else if (String.Equals(ParameterSetName, "LiteralFile", StringComparison.OrdinalIgnoreCase)) - { - _commandWrapper.Initialize(Context, "out-file", typeof(OutFileCommand)); - _commandWrapper.AddNamedParameter("LiteralPath", _fileName); - _commandWrapper.AddNamedParameter("append", _append); - } - else - { - // variable parameter set - _commandWrapper.Initialize(Context, "set-variable", typeof(SetVariableCommand)); - _commandWrapper.AddNamedParameter("name", _variable); - // Can't use set-var's passthru because it writes the var object to the pipeline, we want just - // the values to be written - } - } - /// - /// - /// - protected override void ProcessRecord() - { - _commandWrapper.Process(_inputObject); - WriteObject(_inputObject); - } - - /// - /// - /// - protected override void EndProcessing() - { - _commandWrapper.ShutDown(); - } - - private void Dispose(bool isDisposing) - { - if (!_alreadyDisposed) - { - _alreadyDisposed = true; - if (isDisposing && _commandWrapper != null) - { - _commandWrapper.Dispose(); - _commandWrapper = null; - } - } - } - - /// - /// Dispose method in IDisposable - /// - public void Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - /// - /// Finalizer - /// - ~TeeObjectCommand() - { - Dispose(false); - } - #region private - private CommandWrapper _commandWrapper; - private bool _alreadyDisposed; - #endregion private - } -} - diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/trace/GetTracerCommand.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/trace/GetTracerCommand.cs index 12465f07988..491aaf85361 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/trace/GetTracerCommand.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/trace/GetTracerCommand.cs @@ -1,6 +1,5 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. using System.Linq; using System.Management.Automation; @@ -8,17 +7,16 @@ namespace Microsoft.PowerShell.Commands { /// - /// A cmdlet that gets the TraceSource instances that are instantiated in the process + /// A cmdlet that gets the TraceSource instances that are instantiated in the process. /// - [Cmdlet(VerbsCommon.Get, "TraceSource", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113333")] + [Cmdlet(VerbsCommon.Get, "TraceSource", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2096707")] [OutputType(typeof(PSTraceSource))] public class GetTraceSourceCommand : TraceCommandBase { #region Parameters /// - /// Gets or sets the category parameter which determines - /// which trace switch to get. + /// Gets or sets the category parameter which determines which trace switch to get. /// /// [Parameter(Position = 0, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true)] @@ -39,7 +37,8 @@ public string[] Name _names = value; } - } // TraceSource + } + private string[] _names = new string[] { "*" }; #endregion Parameters @@ -47,14 +46,14 @@ public string[] Name #region Cmdlet code /// - /// Gets the PSTraceSource for the specified category + /// Gets the PSTraceSource for the specified category. /// protected override void ProcessRecord() { var sources = GetMatchingTraceSource(_names, true); - var result = sources.OrderBy(source => source.Name); + var result = sources.OrderBy(static source => source.Name); WriteObject(result, true); - } // ProcessRecord + } #endregion Cmdlet code } diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/trace/MshHostTraceListener.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/trace/MshHostTraceListener.cs index f366c518b63..b7e4ed279aa 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/trace/MshHostTraceListener.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/trace/MshHostTraceListener.cs @@ -1,12 +1,10 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. using System; -using System.Text; -using System.Security.Permissions; using System.Management.Automation; using System.Management.Automation.Internal.Host; +using System.Text; namespace Microsoft.PowerShell.Commands { @@ -15,26 +13,24 @@ namespace Microsoft.PowerShell.Commands /// coming from a System.Management.Automation.TraceSwitch /// to be passed to the Msh host's RawUI methods. ///
- /// /// /// This trace listener cannot be specified in the app.config file. /// It must be added through the add-tracelistener cmdlet. /// - /// - internal class PSHostTraceListener + internal sealed class PSHostTraceListener : System.Diagnostics.TraceListener { #region TraceListener constructors and disposer /// - /// Default constructor used if no. + /// Initializes a new instance of the class. /// internal PSHostTraceListener(PSCmdlet cmdlet) - : base("") + : base(string.Empty) { if (cmdlet == null) { - throw new PSArgumentNullException("cmdlet"); + throw new PSArgumentNullException(nameof(cmdlet)); } Diagnostics.Assert( @@ -50,16 +46,11 @@ internal PSHostTraceListener(PSCmdlet cmdlet) } /// - /// Closes the TraceListenerDialog so that it no longer - /// receives trace output. + /// Closes the TraceListenerDialog so that it no longer receives trace output. /// - /// /// - /// true if the TraceListener is being disposed, false - /// otherwise. + /// True if the TraceListener is being disposed, false otherwise. /// - /// - [SecurityPermission(SecurityAction.LinkDemand)] protected override void Dispose(bool disposing) { try @@ -78,13 +69,11 @@ protected override void Dispose(bool disposing) #endregion TraceListener constructors and disposer /// - /// Sends the given output string to the host for processing + /// Sends the given output string to the host for processing. /// /// - /// The trace output to be written + /// The trace output to be written. /// - /// - [SecurityPermission(SecurityAction.LinkDemand)] public override void Write(string output) { try @@ -97,20 +86,21 @@ public override void Write(string output) // We don't want tracing to bring down the process. } } - private StringBuilder _cachedWrite = new StringBuilder(); + + private readonly StringBuilder _cachedWrite = new(); /// - /// Sends the given output string to the host for processing + /// Sends the given output string to the host for processing. /// /// - /// The trace output to be written + /// The trace output to be written. /// - [SecurityPermission(SecurityAction.LinkDemand)] public override void WriteLine(string output) { try { _cachedWrite.Append(output); + _cachedWrite.Insert(0, DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss.ffff ")); _ui.WriteDebugLine(_cachedWrite.ToString()); _cachedWrite.Remove(0, _cachedWrite.Length); @@ -125,6 +115,6 @@ public override void WriteLine(string output) /// /// The host interface to write the debug line to. /// - private InternalHostUserInterface _ui; - } // class PSHostTraceListener -} // namespace System.Management.Automation + private readonly InternalHostUserInterface _ui; + } +} diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/trace/SetTracerCommand.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/trace/SetTracerCommand.cs index 405857aa651..9f8694ebd20 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/trace/SetTracerCommand.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/trace/SetTracerCommand.cs @@ -1,6 +1,5 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. using System.Collections.ObjectModel; using System.Diagnostics; @@ -9,9 +8,9 @@ namespace Microsoft.PowerShell.Commands { /// - /// A cmdlet that sets the properties of the TraceSwitch instances that are instantiated in the process + /// A cmdlet that sets the properties of the TraceSwitch instances that are instantiated in the process. /// - [Cmdlet(VerbsCommon.Set, "TraceSource", DefaultParameterSetName = "optionsSet", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113400")] + [Cmdlet(VerbsCommon.Set, "TraceSource", DefaultParameterSetName = "optionsSet", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2097129")] [OutputType(typeof(PSTraceSource))] public class SetTraceSourceCommand : TraceListenerCommandBase { @@ -21,38 +20,42 @@ public class SetTraceSourceCommand : TraceListenerCommandBase /// The TraceSource parameter determines which TraceSource categories the /// operation will take place on. ///
- /// [Parameter(Position = 0, Mandatory = true, ValueFromPipeline = true, ValueFromPipelineByPropertyName = true)] public string[] Name { get { return base.NameInternal; } + set { base.NameInternal = value; } } /// - /// The flags to be set on the TraceSource + /// The flags to be set on the TraceSource. /// - /// [Parameter(Position = 1, ValueFromPipelineByPropertyName = true, ParameterSetName = "optionsSet")] public PSTraceSourceOptions Option { - get { return base.OptionsInternal; } + get + { + return base.OptionsInternal; + } + set { base.OptionsInternal = value; } - } // Flags - + } /// - /// The parameter which determines the options for output from the - /// trace listeners. + /// The parameter which determines the options for output from the trace listeners. /// - /// [Parameter(ParameterSetName = "optionsSet")] public TraceOptions ListenerOption { - get { return base.ListenerOptionsInternal; } + get + { + return base.ListenerOptionsInternal; + } + set { base.ListenerOptionsInternal = value; @@ -60,56 +63,56 @@ public TraceOptions ListenerOption } /// - /// Adds the file trace listener using the specified file + /// Adds the file trace listener using the specified file. /// /// [Parameter(ParameterSetName = "optionsSet")] - [Alias("PSPath")] + [Alias("PSPath", "Path")] public string FilePath { get { return base.FileListener; } + set { base.FileListener = value; } - } // File + } /// - /// Force parameter to control read-only files + /// Force parameter to control read-only files. /// [Parameter(ParameterSetName = "optionsSet")] public SwitchParameter Force { get { return base.ForceWrite; } + set { base.ForceWrite = value; } } /// - /// If this parameter is specified the Debugger trace listener - /// will be added. + /// If this parameter is specified the Debugger trace listener will be added. /// /// [Parameter(ParameterSetName = "optionsSet")] public SwitchParameter Debugger { get { return base.DebuggerListener; } + set { base.DebuggerListener = value; } - } // Debugger + } /// - /// If this parameter is specified the Msh Host trace listener - /// will be added. + /// If this parameter is specified the PSHost trace listener will be added. /// /// [Parameter(ParameterSetName = "optionsSet")] public SwitchParameter PSHost { get { return base.PSHostListener; } + set { base.PSHostListener = value; } - } // PSHost + } /// - /// If set, the specified listeners will be removed regardless - /// of their type. + /// If set, the specified listeners will be removed regardless of their type. /// - /// [Parameter(ParameterSetName = "removeAllListenersSet")] [ValidateNotNullOrEmpty] public string[] RemoveListener { get; set; } = new string[] { "*" }; @@ -117,7 +120,6 @@ public SwitchParameter PSHost /// /// If set, the specified file trace listeners will be removed. /// - /// [Parameter(ParameterSetName = "removeFileListenersSet")] [ValidateNotNullOrEmpty] public string[] RemoveFileListener { get; set; } = new string[] { "*" }; @@ -131,8 +133,10 @@ public SwitchParameter PSHost public SwitchParameter PassThru { get { return _passThru; } + set { _passThru = value; } - } // Passthru + } + private bool _passThru; #endregion Parameters @@ -140,7 +144,7 @@ public SwitchParameter PassThru #region Cmdlet code /// - /// Sets the TraceSource properties + /// Sets the TraceSource properties. /// protected override void ProcessRecord() { @@ -157,6 +161,7 @@ protected override void ProcessRecord() WriteObject(matchingSources, true); WriteObject(preconfiguredTraceSources, true); } + break; case "removeAllListenersSet": @@ -169,7 +174,7 @@ protected override void ProcessRecord() RemoveListenersByName(matchingSources, RemoveFileListener, true); break; } - } // ProcessRecord + } #endregion Cmdlet code } diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/trace/TraceCommandBase.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/trace/TraceCommandBase.cs index 621c44be5a2..9118fd773fd 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/trace/TraceCommandBase.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/trace/TraceCommandBase.cs @@ -1,39 +1,30 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. -using System; using System.Collections.Generic; using System.Collections.ObjectModel; using System.Management.Automation; -using Dbg = System.Management.Automation.Diagnostics; namespace Microsoft.PowerShell.Commands { /// - /// A base class for cmdlets that has helper methods for globbing - /// trace source instances + /// A base class for cmdlets that has helper methods for globbing trace source instances. /// public class TraceCommandBase : PSCmdlet { /// - /// Gets the matching PSTraceSource instances for the - /// specified patterns. + /// Gets the matching PSTraceSource instances for the specified patterns. /// - /// /// /// The patterns used to match the PSTraceSource name. /// - /// /// /// If true and the pattern does not contain wildcard patterns and no /// match is found, then WriteError will be called. /// - /// /// /// A collection of the matching PSTraceSource instances. /// - /// internal Collection GetMatchingTraceSource( string[] patternsToMatch, bool writeErrorIfMatchNotFound) @@ -43,27 +34,21 @@ internal Collection GetMatchingTraceSource( } /// - /// Gets the matching PSTraceSource instances for the - /// specified patterns. + /// Gets the matching PSTraceSource instances for the specified patterns. /// - /// /// /// The patterns used to match the PSTraceSource name. /// - /// /// /// If true and the pattern does not contain wildcard patterns and no /// match is found, then WriteError will be called. /// - /// /// /// The patterns for which a match was not found. /// - /// /// /// A collection of the matching PSTraceSource instances. /// - /// internal Collection GetMatchingTraceSource( string[] patternsToMatch, bool writeErrorIfMatchNotFound, @@ -71,12 +56,12 @@ internal Collection GetMatchingTraceSource( { notMatched = new Collection(); - Collection results = new Collection(); + Collection results = new(); foreach (string patternToMatch in patternsToMatch) { bool matchFound = false; - if (String.IsNullOrEmpty(patternToMatch)) + if (string.IsNullOrEmpty(patternToMatch)) { notMatched.Add(patternToMatch); continue; @@ -87,7 +72,7 @@ internal Collection GetMatchingTraceSource( patternToMatch, WildcardOptions.IgnoreCase); - Dictionary traceCatalog = PSTraceSource.TraceCatalog; + Dictionary traceCatalog = PSTraceSource.TraceCatalog; foreach (PSTraceSource source in traceCatalog.Values) { @@ -117,12 +102,12 @@ internal Collection GetMatchingTraceSource( !WildcardPattern.ContainsWildcardCharacters(patternToMatch)) { ItemNotFoundException itemNotFound = - new ItemNotFoundException( + new( patternToMatch, "TraceSourceNotFound", SessionStateStrings.TraceSourceNotFound); - ErrorRecord errorRecord = new ErrorRecord(itemNotFound.ErrorRecord, itemNotFound); + ErrorRecord errorRecord = new(itemNotFound.ErrorRecord, itemNotFound); WriteError(errorRecord); } } diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/trace/TraceExpressionCommand.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/trace/TraceExpressionCommand.cs index 15ca35a3b4b..1eadd4934b3 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/trace/TraceExpressionCommand.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/trace/TraceExpressionCommand.cs @@ -1,6 +1,5 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. using System; using System.Collections.ObjectModel; @@ -10,7 +9,6 @@ using System.Management.Automation.Internal; using System.Management.Automation.Runspaces; using System.Threading; -using Dbg = System.Management.Automation.Diagnostics; namespace Microsoft.PowerShell.Commands { @@ -18,42 +16,46 @@ namespace Microsoft.PowerShell.Commands /// A cmdlet that traces the specified categories and flags for the duration of the /// specified expression. /// - [Cmdlet(VerbsDiagnostic.Trace, "Command", DefaultParameterSetName = "expressionSet", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113419")] + [Cmdlet(VerbsDiagnostic.Trace, "Command", DefaultParameterSetName = "expressionSet", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2097136")] public class TraceCommandCommand : TraceListenerCommandBase, IDisposable { #region Parameters /// - /// This parameter specifies the current pipeline object + /// This parameter specifies the current pipeline object. /// [Parameter(ValueFromPipeline = true)] - public PSObject InputObject { set; get; } = AutomationNull.Value; + public PSObject InputObject { get; set; } = AutomationNull.Value; /// /// The TraceSource parameter determines which TraceSource categories the /// operation will take place on. /// - /// [Parameter(Position = 0, Mandatory = true)] public string[] Name { get { return base.NameInternal; } + set { base.NameInternal = value; } } /// - /// The flags to be set on the TraceSource + /// The flags to be set on the TraceSource. /// /// [Parameter(Position = 2)] public PSTraceSourceOptions Option { - get { return base.OptionsInternal; } + get + { + return base.OptionsInternal; + } + set { base.OptionsInternal = value; } - } // Options + } /// /// The parameter for the expression that should be traced. @@ -71,21 +73,23 @@ public PSTraceSourceOptions Option /// /// When set, this parameter is the arguments to pass to the command specified by - /// the -Command parameter + /// the -Command parameter. /// [Parameter(ParameterSetName = "commandSet", ValueFromRemainingArguments = true)] [Alias("Args")] public object[] ArgumentList { get; set; } /// - /// The parameter which determines the options for output from the - /// trace listeners. + /// The parameter which determines the options for output from the trace listeners. /// - /// [Parameter] public TraceOptions ListenerOption { - get { return base.ListenerOptionsInternal; } + get + { + return base.ListenerOptionsInternal; + } + set { base.ListenerOptionsInternal = value; @@ -93,51 +97,52 @@ public TraceOptions ListenerOption } /// - /// Adds the file trace listener using the specified file + /// Adds the file trace listener using the specified file. /// /// [Parameter] - [Alias("PSPath")] + [Alias("PSPath", "Path")] public string FilePath { get { return base.FileListener; } + set { base.FileListener = value; } - } // File + } /// - /// Force parameter to control read-only files + /// Force parameter to control read-only files. /// [Parameter] public SwitchParameter Force { get { return base.ForceWrite; } + set { base.ForceWrite = value; } } /// - /// If this parameter is specified the Debugger trace listener - /// will be added. + /// If this parameter is specified the Debugger trace listener will be added. /// /// [Parameter] public SwitchParameter Debugger { get { return base.DebuggerListener; } + set { base.DebuggerListener = value; } - } // Debugger + } /// - /// If this parameter is specified the Msh Host trace listener - /// will be added. + /// If this parameter is specified the Msh Host trace listener will be added. /// /// [Parameter] public SwitchParameter PSHost { get { return base.PSHostListener; } - set { base.PSHostListener = value; } - } // PSHost + set { base.PSHostListener = value; } + } #endregion Parameters @@ -153,7 +158,6 @@ protected override void BeginProcessing() Collection preconfiguredSources = null; _matchingSources = ConfigureTraceSource(base.NameInternal, false, out preconfiguredSources); - TurnOnTracing(_matchingSources, false); TurnOnTracing(preconfiguredSources, true); @@ -185,13 +189,13 @@ protected override void BeginProcessing() _pipeline.ExternalErrorOutput = new TracePipelineWriter(this, true, _matchingSources); _pipeline.ExternalSuccessOutput = new TracePipelineWriter(this, false, _matchingSources); } + ResetTracing(_matchingSources); } /// /// Executes the expression. - /// - /// Note, this was taken from apply-expression + /// Note, this was taken from apply-expression. /// protected override void ProcessRecord() { @@ -208,6 +212,7 @@ protected override void ProcessRecord() result = StepCommand(); break; } + ResetTracing(_matchingSources); if (result == null) @@ -215,13 +220,11 @@ protected override void ProcessRecord() return; } - if (!LanguagePrimitives.IsNull(result)) { WriteObject(result, true); } - } // ProcessRecord - + } /// /// Finishes running the command if specified and then sets the @@ -239,20 +242,14 @@ protected override void EndProcessing() WriteObject(results, true); } + this.Dispose(); } /// /// Ensures that the sub-pipeline we created gets stopped as well. /// - /// - protected override void StopProcessing() - { - if (_pipeline != null) - { - _pipeline.Stop(); - } - } + protected override void StopProcessing() => _pipeline?.Stop(); #endregion Cmdlet code @@ -264,7 +261,7 @@ private object RunExpression() dollarUnder: InputObject, input: new object[] { InputObject }, scriptThis: AutomationNull.Value, - args: Utils.EmptyArray()); + args: Array.Empty()); } private object StepCommand() @@ -273,6 +270,7 @@ private object StepCommand() { _pipeline.Step(InputObject); } + return null; } @@ -311,9 +309,11 @@ public void Dispose() fileStream.Dispose(); } } + GC.SuppressFinalize(this); } - } // Dispose + } + private bool _disposed; #endregion IDisposable } @@ -323,23 +323,15 @@ public void Dispose() /// cmdlet. It gets attached to the sub-pipelines success or error pipeline and redirects /// all objects written to these pipelines to trace-command pipeline. /// - /// - internal class TracePipelineWriter : PipelineWriter + internal sealed class TracePipelineWriter : PipelineWriter { internal TracePipelineWriter( TraceListenerCommandBase cmdlet, bool writeError, Collection matchingSources) { - if (cmdlet == null) - { - throw new ArgumentNullException("cmdlet"); - } - - if (matchingSources == null) - { - throw new ArgumentNullException("matchingSources"); - } + ArgumentNullException.ThrowIfNull(cmdlet); + ArgumentNullException.ThrowIfNull(matchingSources); _cmdlet = cmdlet; _writeError = writeError; @@ -347,8 +339,7 @@ internal TracePipelineWriter( } /// - /// Get the wait handle signaled when buffer space is available - /// in the underlying stream. + /// Get the wait handle signaled when buffer space is available in the underlying stream. /// public override WaitHandle WaitHandle { @@ -372,7 +363,7 @@ public override bool IsOpen } /// - /// Returns the number of objects in the underlying stream + /// Returns the number of objects in the underlying stream. /// public override int Count { @@ -380,7 +371,7 @@ public override int Count } /// - /// Get the capacity of the stream + /// Get the capacity of the stream. /// /// /// The capacity of the stream. @@ -396,7 +387,7 @@ public override int MaxCapacity } /// - /// Close the stream + /// Close the stream. /// /// /// Causes subsequent calls to IsOpen to return false and calls to @@ -404,7 +395,7 @@ public override int MaxCapacity /// All calls to Close() after the first call are silently ignored. /// /// - /// The stream is already disposed + /// The stream is already disposed. /// public override void Close() { @@ -420,23 +411,23 @@ public override void Close() /// but disposed streams may not. /// /// - /// The underlying stream is disposed + /// The underlying stream is disposed. /// public override void Flush() { } /// - /// Write a single object into the underlying stream + /// Write a single object into the underlying stream. /// - /// The object to add to the stream + /// The object to add to the stream. /// /// One, if the write was successful, otherwise; /// zero if the stream was closed before the object could be written, /// or if the object was AutomationNull.Value. /// /// - /// The underlying stream is closed + /// The underlying stream is closed. /// public override int Write(object obj) { @@ -461,9 +452,9 @@ public override int Write(object obj) } /// - /// Write objects to the underlying stream + /// Write objects to the underlying stream. /// - /// object or enumeration to read from + /// Object or enumeration to read from. /// /// If enumerateCollection is true, and /// is an enumeration according to LanguagePrimitives.GetEnumerable, @@ -471,9 +462,9 @@ public override int Write(object obj) /// written separately. Otherwise, /// will be written as a single object. /// - /// The number of objects written + /// The number of objects written. /// - /// The underlying stream is closed + /// The underlying stream is closed. /// /// /// contains AutomationNull.Value @@ -521,18 +512,16 @@ public override int Write(object obj, bool enumerateCollection) private static ErrorRecord ConvertToErrorRecord(object obj) { ErrorRecord result = null; - PSObject mshobj = obj as PSObject; - if (mshobj != null) + if (obj is PSObject mshobj) { object baseObject = mshobj.BaseObject; - if (!(baseObject is PSCustomObject)) + if (baseObject is not PSCustomObject) { obj = baseObject; } } - ErrorRecord errorRecordResult = obj as ErrorRecord; - if (errorRecordResult != null) + if (obj is ErrorRecord errorRecordResult) { result = errorRecordResult; } @@ -540,9 +529,9 @@ private static ErrorRecord ConvertToErrorRecord(object obj) return result; } - private TraceListenerCommandBase _cmdlet; - private bool _writeError; + private readonly TraceListenerCommandBase _cmdlet; + private readonly bool _writeError; private bool _isOpen = true; - private Collection _matchingSources = new Collection(); + private readonly Collection _matchingSources = new(); } } diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/trace/TraceListenerCommandBase.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/trace/TraceListenerCommandBase.cs index 7c3435d502f..f1e4fe2dfa0 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/trace/TraceListenerCommandBase.cs +++ b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/trace/TraceListenerCommandBase.cs @@ -1,6 +1,5 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. using System; using System.Collections.Generic; @@ -10,13 +9,12 @@ using System.Management.Automation; using System.Management.Automation.Internal; using System.Security; -using Dbg = System.Management.Automation.Diagnostics; namespace Microsoft.PowerShell.Commands { /// /// A base class for the trace cmdlets that allow you to specify - /// which trace listeners to add to a TraceSource + /// which trace listeners to add to a TraceSource. /// public class TraceListenerCommandBase : TraceCommandBase { @@ -26,23 +24,26 @@ public class TraceListenerCommandBase : TraceCommandBase /// The TraceSource parameter determines which TraceSource categories the /// operation will take place on. /// - /// - internal string[] NameInternal { get; set; } = new string[0]; - + internal string[] NameInternal { get; set; } = Array.Empty(); /// - /// The flags to be set on the TraceSource + /// The flags to be set on the TraceSource. /// /// internal PSTraceSourceOptions OptionsInternal { - get { return _options; } + get + { + return _options; + } + set { _options = value; optionsSpecified = true; } - } // Flags + } + private PSTraceSourceOptions _options = PSTraceSourceOptions.All; /// @@ -51,31 +52,34 @@ internal PSTraceSourceOptions OptionsInternal internal bool optionsSpecified; /// - /// The parameter which determines the options for output from the - /// trace listeners. + /// The parameter which determines the options for output from the trace listeners. /// - /// internal TraceOptions ListenerOptionsInternal { - get { return _traceOptions; } + get + { + return _traceOptions; + } + set { traceOptionsSpecified = true; _traceOptions = value; } } + private TraceOptions _traceOptions = TraceOptions.None; /// - /// True if the TraceOptions parameter was specified, or false otherwise + /// True if the TraceOptions parameter was specified, or false otherwise. /// internal bool traceOptionsSpecified; /// - /// Adds the file trace listener using the specified file + /// Adds the file trace listener using the specified file. /// /// - internal string FileListener { get; set; } // File + internal string FileListener { get; set; } /// /// Property that sets force parameter. This will clear the @@ -84,25 +88,25 @@ internal TraceOptions ListenerOptionsInternal /// /// Note that we do not attempt to reset the read-only attribute. /// - public bool ForceWrite { get; set; } // Force + public bool ForceWrite { get; set; } /// - /// If this parameter is specified the Debugger trace listener - /// will be added. + /// If this parameter is specified the Debugger trace listener will be added. /// /// - internal bool DebuggerListener { get; set; } // Debugger + internal bool DebuggerListener { get; set; } /// - /// If this parameter is specified the Msh Host trace listener - /// will be added. + /// If this parameter is specified the Msh Host trace listener will be added. /// /// internal SwitchParameter PSHostListener { get { return _host; } + set { _host = value; } - } // UseHost + } + private bool _host = false; #endregion Parameters @@ -136,7 +140,7 @@ internal Collection ConfigureTraceSource( foreach (string notMatchedName in notMatched) { - if (String.IsNullOrEmpty(notMatchedName)) + if (string.IsNullOrEmpty(notMatchedName)) { continue; } @@ -149,7 +153,7 @@ internal Collection ConfigureTraceSource( PSTraceSource newTraceSource = PSTraceSource.GetNewTraceSource( notMatchedName, - String.Empty, + string.Empty, true); preconfiguredSources.Add(newTraceSource); @@ -188,10 +192,8 @@ internal Collection ConfigureTraceSource( #region AddTraceListeners /// - /// Adds the console, debugger, file, or host listener - /// if requested. + /// Adds the console, debugger, file, or host listener if requested. /// - /// internal void AddTraceListenersToSources(Collection matchingSources) { if (DebuggerListener) @@ -204,6 +206,7 @@ internal void AddTraceListenersToSources(Collection matchingSourc // Note, this is not meant to be localized. _defaultListener.Name = "Debug"; } + AddListenerToSources(matchingSources, _defaultListener); } @@ -217,6 +220,7 @@ internal void AddTraceListenersToSources(Collection matchingSourc // Note, this is not meant to be localized. _hostListener.Name = "Host"; } + AddListenerToSources(matchingSources, _hostListener); } @@ -231,7 +235,7 @@ internal void AddTraceListenersToSources(Collection matchingSourc try { - Collection resolvedPaths = new Collection(); + Collection resolvedPaths = new(); try { // Resolve the file path @@ -271,6 +275,7 @@ internal void AddTraceListenersToSources(Collection matchingSourc FileListener, provider.FullName)); } + resolvedPaths.Add(path); } @@ -288,26 +293,26 @@ internal void AddTraceListenersToSources(Collection matchingSourc if (ForceWrite && System.IO.File.Exists(resolvedPath)) { // remove readonly attributes on the file - System.IO.FileInfo fInfo = new System.IO.FileInfo(resolvedPath); + System.IO.FileInfo fInfo = new(resolvedPath); if (fInfo != null) { // Save some disk write time by checking whether file is readonly.. if ((fInfo.Attributes & FileAttributes.ReadOnly) == FileAttributes.ReadOnly) { - //Make sure the file is not read only + // Make sure the file is not read only fInfo.Attributes &= ~(FileAttributes.ReadOnly); } } } // Trace commands always append..So there is no need to set overwrite with force.. - FileStream fileStream = new FileStream(resolvedPath, FileMode.Append, FileAccess.Write, FileShare.ReadWrite); + FileStream fileStream = new(resolvedPath, FileMode.Append, FileAccess.Write, FileShare.ReadWrite); FileStreams.Add(fileStream); // Open the file stream TextWriterTraceListener fileListener = - new TextWriterTraceListener(fileStream, resolvedPath); + new(fileStream, resolvedPath); fileListener.Name = FileListener; @@ -329,7 +334,7 @@ internal void AddTraceListenersToSources(Collection matchingSourc if (fileOpenError != null) { ErrorRecord errorRecord = - new ErrorRecord( + new( fileOpenError, "FileListenerPathResolutionFailed", ErrorCategory.OpenError, @@ -354,7 +359,7 @@ internal void AddTraceListenersToSources(Collection matchingSourc if (error != null) { ErrorRecord errorRecord = - new ErrorRecord( + new( error, "FileListenerPathResolutionFailed", ErrorCategory.InvalidArgument, @@ -370,14 +375,14 @@ internal void AddTraceListenersToSources(Collection matchingSourc } } } + private DefaultTraceListener _defaultListener; private PSHostTraceListener _hostListener; private Collection _fileListeners; /// - /// The file streams that were open by this command + /// The file streams that were open by this command. /// - /// internal Collection FileStreams { get; private set; } private static void AddListenerToSources(Collection matchingSources, TraceListener listener) @@ -394,9 +399,8 @@ private static void AddListenerToSources(Collection matchingSourc #region RemoveTraceListeners /// - /// Removes the tracelisteners from the specified trace sources + /// Removes the tracelisteners from the specified trace sources. /// - /// internal static void RemoveListenersByName( Collection matchingSources, string[] listenerNames, @@ -419,7 +423,7 @@ internal static void RemoveListenersByName( { TraceListener listenerToRemove = source.Listeners[index]; - if (fileListenersOnly && !(listenerToRemove is TextWriterTraceListener)) + if (fileListenersOnly && listenerToRemove is not TextWriterTraceListener) { // Since we only want to remove file listeners, skip any that // aren't file listeners @@ -439,15 +443,14 @@ internal static void RemoveListenersByName( } } } - } // RemoveAllTraceListenersFromSource - + } #endregion RemoveTraceListeners #region SetTraceListenerOptions /// - /// Sets the trace listener options based on the ListenerOptions parameter + /// Sets the trace listener options based on the ListenerOptions parameter. /// internal void SetTraceListenerOptions(Collection matchingSources) { @@ -469,9 +472,8 @@ internal void SetTraceListenerOptions(Collection matchingSources) #region SetFlags /// - /// Sets the flags for all the specified TraceSources + /// Sets the flags for all the specified TraceSources. /// - /// internal void SetFlags(Collection matchingSources) { foreach (PSTraceSource structuredSource in matchingSources) @@ -484,8 +486,7 @@ internal void SetFlags(Collection matchingSources) #region TurnOnTracing /// - /// Turns on tracing for the TraceSources, flags, and listeners defined by - /// the parameters + /// Turns on tracing for the TraceSources, flags, and listeners defined by the parameters. /// internal void TurnOnTracing(Collection matchingSources, bool preConfigured) { @@ -496,7 +497,7 @@ internal void TurnOnTracing(Collection matchingSources, bool preC { // Copy the listeners into a different collection - Collection listenerCollection = new Collection(); + Collection listenerCollection = new(); foreach (TraceListener listener in source.Listeners) { listenerCollection.Add(listener); @@ -596,11 +597,12 @@ protected void ClearStoredState() listener.Dispose(); } } + _storedTraceSourceState.Clear(); } - private Dictionary>> _storedTraceSourceState = - new Dictionary>>(); + private readonly Dictionary>> _storedTraceSourceState = + new(); #endregion stored state } diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/update-list.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/update-list.cs deleted file mode 100644 index 55e4c855232..00000000000 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/update-list.cs +++ /dev/null @@ -1,191 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -using System; -using System.Collections; -using System.Management.Automation; -using System.Diagnostics.CodeAnalysis; - -namespace Microsoft.PowerShell.Commands -{ - /// - /// This cmdlet updates the property of incoming objects and passes them to the - /// pipeline. This cmdlet also returns a .NET object with properties that - /// defines the update action on a list. - /// - /// This cmdlet is most helpful when the cmdlet author wants the user to do - /// update action on object list that are not directly exposed through - /// cmdlet parameter. One wants to update a property value which is a list - /// (multi-valued parameter for a cmdlet), without exposing the list. - /// - [Cmdlet(VerbsData.Update, "List", DefaultParameterSetName = "AddRemoveSet", - HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113447", RemotingCapability = RemotingCapability.None)] - public class UpdateListCommand : PSCmdlet - { - /// - /// The following is the definition of the input parameter "Add". - /// Objects to be add to the list - /// - [Parameter(ParameterSetName = "AddRemoveSet")] - [ValidateNotNullOrEmpty()] - [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Justification = "Cmdlets use arrays for parameters.")] - public object[] Add { get; set; } - - /// - /// The following is the definition of the input parameter "Remove". - /// Objects to be removed from the list - /// - [Parameter(ParameterSetName = "AddRemoveSet")] - [ValidateNotNullOrEmpty()] - [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Justification = "Cmdlets use arrays for parameters.")] - public object[] Remove { get; set; } - - /// - /// The following is the definition of the input parameter "Replace". - /// Objects in this list replace the objects in the target list. - /// - [Parameter(Mandatory = true, ParameterSetName = "ReplaceSet")] - [ValidateNotNullOrEmpty()] - [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays", Justification = "Cmdlets use arrays for parameters.")] - public object[] Replace { get; set; } - - /// - /// The following is the definition of the input parameter "InputObject". - /// List of InputObjects where the updates needs to applied to the - /// specific property - /// - //[Parameter(ValueFromPipeline = true, ParameterSetName = "AddRemoveSet")] - //[Parameter(ValueFromPipeline = true, ParameterSetName = "ReplaceSet")] - [Parameter(ValueFromPipeline = true)] - [ValidateNotNullOrEmpty()] - public PSObject InputObject { get; set; } - - /// - /// The following is the definition of the input parameter "Property". - /// Defines which property of the input object should be updated with Add and - /// Remove actions - /// - //[Parameter(Position = 0, ParameterSetName = "AddRemoveSet")] - //[Parameter(Position = 0, ParameterSetName = "ReplaceSet")] - [Parameter(Position = 0)] - [ValidateNotNullOrEmpty()] - public string Property { get; set; } - - private PSListModifier _listModifier; - - /// - /// ProcessRecord method. - /// - protected override void ProcessRecord() - { - if (Property != null) - { - if (InputObject == null) - { - WriteError(NewError("MissingInputObjectParameter", "MissingInputObjectParameter", null)); - } - else - { - if (_listModifier == null) - { - _listModifier = CreatePSListModifier(); - } - - PSMemberInfo memberInfo = InputObject.Members[Property]; - if (memberInfo != null) - { - try - { - _listModifier.ApplyTo(memberInfo.Value); - WriteObject(InputObject); - } - catch (PSInvalidOperationException e) - { - WriteError(new ErrorRecord(e, "ApplyFailed", ErrorCategory.InvalidOperation, null)); - } - } - else - { - WriteError(NewError("MemberDoesntExist", "MemberDoesntExist", InputObject, Property)); - } - } - } - } - - - /// - /// EndProcessing method. - /// - protected override void EndProcessing() - { - if (Property == null) - { - if (InputObject != null) - { - ThrowTerminatingError(NewError("MissingPropertyParameter", "MissingPropertyParameter", null)); - } - else - { - WriteObject(CreateHashtable()); - } - } - } - - private Hashtable CreateHashtable() - { - Hashtable hash = new Hashtable(2); - if (Add != null) - { - hash.Add("Add", Add); - } - if (Remove != null) - { - hash.Add("Remove", Remove); - } - if (Replace != null) - { - hash.Add("Replace", Replace); - } - return hash; - } - - private PSListModifier CreatePSListModifier() - { - PSListModifier listModifier = new PSListModifier(); - if (Add != null) - { - foreach (object obj in Add) - { - listModifier.Add.Add(obj); - } - } - if (Remove != null) - { - foreach (object obj in Remove) - { - listModifier.Remove.Add(obj); - } - } - if (Replace != null) - { - foreach (object obj in Replace) - { - listModifier.Replace.Add(obj); - } - } - return listModifier; - } - - private ErrorRecord NewError(string errorId, string resourceId, object targetObject, params object[] args) - { - ErrorDetails details = new ErrorDetails(this.GetType().Assembly, "UpdateListStrings", resourceId, args); - ErrorRecord errorRecord = new ErrorRecord( - new InvalidOperationException(details.Message), - errorId, - ErrorCategory.InvalidOperation, - targetObject); - return errorRecord; - } - } -} diff --git a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/write.cs b/src/Microsoft.PowerShell.Commands.Utility/commands/utility/write.cs deleted file mode 100644 index 99d4c8b2403..00000000000 --- a/src/Microsoft.PowerShell.Commands.Utility/commands/utility/write.cs +++ /dev/null @@ -1,497 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -using System; -using System.Management.Automation; -using System.Management.Automation.Internal; -using System.Runtime.Serialization; -using System.Diagnostics.CodeAnalysis; - -namespace Microsoft.PowerShell.Commands -{ - #region WriteDebugCommand - /// - /// This class implements Write-Debug command - /// - /// - [Cmdlet(VerbsCommunications.Write, "Debug", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113424", RemotingCapability = RemotingCapability.None)] - public sealed class WriteDebugCommand : PSCmdlet - { - /// - /// Message to be sent and processed if debug mode is on. - /// - [Parameter(Position = 0, Mandatory = true, ValueFromPipeline = true)] - [AllowEmptyString] - [Alias("Msg")] - public string Message { get; set; } = null; - - - /// - /// This method implements the ProcessRecord method for Write-Debug command - /// - protected override void ProcessRecord() - { - // - // The write-debug command must use the script's InvocationInfo rather than its own, - // so we create the DebugRecord here and fill it up with the appropriate InvocationInfo; - // then, we call the command runtime directly and pass this record to WriteDebug(). - // - MshCommandRuntime mshCommandRuntime = this.CommandRuntime as MshCommandRuntime; - - if (mshCommandRuntime != null) - { - DebugRecord record = new DebugRecord(Message); - - InvocationInfo invocationInfo = GetVariableValue(SpecialVariables.MyInvocation) as InvocationInfo; - - if (invocationInfo != null) - { - record.SetInvocationInfo(invocationInfo); - } - - mshCommandRuntime.WriteDebug(record); - } - else - { - WriteDebug(Message); - } - }//processrecord - }//WriteDebugCommand - #endregion WriteDebugCommand - - #region WriteVerboseCommand - /// - /// This class implements Write-Verbose command - /// - /// - [Cmdlet(VerbsCommunications.Write, "Verbose", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113429", RemotingCapability = RemotingCapability.None)] - public sealed class WriteVerboseCommand : PSCmdlet - { - /// - /// Message to be sent if verbose messages are being shown. - /// - [Parameter(Position = 0, Mandatory = true, ValueFromPipeline = true)] - [AllowEmptyString] - [Alias("Msg")] - public string Message { get; set; } = null; - - - /// - /// This method implements the ProcessRecord method for Write-verbose command - /// - protected override void ProcessRecord() - { - // - // The write-verbose command must use the script's InvocationInfo rather than its own, - // so we create the VerboseRecord here and fill it up with the appropriate InvocationInfo; - // then, we call the command runtime directly and pass this record to WriteVerbose(). - // - MshCommandRuntime mshCommandRuntime = this.CommandRuntime as MshCommandRuntime; - - if (mshCommandRuntime != null) - { - VerboseRecord record = new VerboseRecord(Message); - - InvocationInfo invocationInfo = GetVariableValue(SpecialVariables.MyInvocation) as InvocationInfo; - - if (invocationInfo != null) - { - record.SetInvocationInfo(invocationInfo); - } - - mshCommandRuntime.WriteVerbose(record); - } - else - { - WriteVerbose(Message); - } - }//processrecord - }//WriteVerboseCommand - #endregion WriteVerboseCommand - - #region WriteWarningCommand - /// - /// This class implements Write-Warning command - /// - /// - [Cmdlet(VerbsCommunications.Write, "Warning", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113430", RemotingCapability = RemotingCapability.None)] - public sealed class WriteWarningCommand : PSCmdlet - { - /// - /// Message to be sent if warning messages are being shown. - /// - [Parameter(Position = 0, Mandatory = true, ValueFromPipeline = true)] - [AllowEmptyString] - [Alias("Msg")] - public string Message { get; set; } = null; - - - /// - /// This method implements the ProcessRecord method for Write-Warning command - /// - protected override void ProcessRecord() - { - // - // The write-warning command must use the script's InvocationInfo rather than its own, - // so we create the WarningRecord here and fill it up with the appropriate InvocationInfo; - // then, we call the command runtime directly and pass this record to WriteWarning(). - // - MshCommandRuntime mshCommandRuntime = this.CommandRuntime as MshCommandRuntime; - - if (mshCommandRuntime != null) - { - WarningRecord record = new WarningRecord(Message); - - InvocationInfo invocationInfo = GetVariableValue(SpecialVariables.MyInvocation) as InvocationInfo; - - if (invocationInfo != null) - { - record.SetInvocationInfo(invocationInfo); - } - - mshCommandRuntime.WriteWarning(record); - } - else - { - WriteWarning(Message); - } - }//processrecord - }//WriteWarningCommand - #endregion WriteWarningCommand - - #region WriteInformationCommand - /// - /// This class implements Write-Information command - /// - /// - [Cmdlet(VerbsCommunications.Write, "Information", HelpUri = "https://go.microsoft.com/fwlink/?LinkId=525909", RemotingCapability = RemotingCapability.None)] - public sealed class WriteInformationCommand : PSCmdlet - { - /// - /// Object to be sent to the Information stream. - /// - [Parameter(Position = 0, Mandatory = true, ValueFromPipeline = true)] - [Alias("Msg")] - public Object MessageData { get; set; } - - /// - /// Any tags to be associated with this information - /// - [Parameter(Position = 1)] - [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] - public string[] Tags { get; set; } - - /// - /// This method implements the processing of the Write-Information command - /// - protected override void BeginProcessing() - { - if (Tags != null) - { - foreach (string tag in Tags) - { - if (tag.StartsWith("PS", StringComparison.OrdinalIgnoreCase)) - { - ErrorRecord er = new ErrorRecord( - new InvalidOperationException(StringUtil.Format(UtilityCommonStrings.PSPrefixReservedInInformationTag, tag)), - "PSPrefixReservedInInformationTag", ErrorCategory.InvalidArgument, tag); - ThrowTerminatingError(er); - } - } - } - } - - /// - /// This method implements the ProcessRecord method for Write-Information command - /// - protected override void ProcessRecord() - { - WriteInformation(MessageData, Tags); - } - - }//WriteInformationCommand - - #endregion WriteInformationCommand - - - #region WriteOrThrowErrorCommand - - /// - /// This class implements the Write-Error command - /// - public class WriteOrThrowErrorCommand : PSCmdlet - { - /// - /// ErrorRecord.Exception -- if not specified, ErrorRecord.Exception is - /// System.Exception. - /// - [Parameter(ParameterSetName = "WithException", Mandatory = true)] - public Exception Exception { get; set; } = null; - - /// - /// If Exception is specified, this is ErrorRecord.ErrorDetails.Message. - /// Otherwise, the Exception is System.Exception, and this is - /// Exception.Message. - /// - [Parameter(Position = 0, ParameterSetName = "NoException", Mandatory = true, ValueFromPipeline = true)] - [Parameter(ParameterSetName = "WithException")] - [AllowNull] - [AllowEmptyString] - [Alias("Msg")] - public string Message { get; set; } = null; - - /// - /// If Exception is specified, this is ErrorRecord.ErrorDetails.Message. - /// Otherwise, the Exception is System.Exception, and this is - /// Exception.Message. - /// - [Parameter(ParameterSetName = "ErrorRecord", Mandatory = true)] - public ErrorRecord ErrorRecord { get; set; } = null; - - /// - /// ErrorRecord.CategoryInfo.Category - /// - [Parameter(ParameterSetName = "NoException")] - [Parameter(ParameterSetName = "WithException")] - public ErrorCategory Category { get; set; } = ErrorCategory.NotSpecified; - - /// - /// ErrorRecord.ErrorId - /// - [Parameter(ParameterSetName = "NoException")] - [Parameter(ParameterSetName = "WithException")] - public string ErrorId { get; set; } = ""; - - /// - /// ErrorRecord.TargetObject - /// - [Parameter(ParameterSetName = "NoException")] - [Parameter(ParameterSetName = "WithException")] - public object TargetObject { get; set; } = null; - - /// - /// ErrorRecord.ErrorDetails.RecommendedAction - /// - [Parameter] - public string RecommendedAction { get; set; } = ""; - - /* 2005/01/25 removing throw-error - /// - /// If true, this is throw-error. Otherwise, this is write-error. - /// - internal bool _terminating = false; - */ - - /// - /// ErrorRecord.CategoryInfo.Activity - /// - [Parameter] - [Alias("Activity")] - public string CategoryActivity { get; set; } = ""; - - /// - /// ErrorRecord.CategoryInfo.Reason - /// - [Parameter] - [Alias("Reason")] - public string CategoryReason { get; set; } = ""; - - /// - /// ErrorRecord.CategoryInfo.TargetName - /// - [Parameter] - [Alias("TargetName")] - public string CategoryTargetName { get; set; } = ""; - - /// - /// ErrorRecord.CategoryInfo.TargetType - /// - [Parameter] - [Alias("TargetType")] - public string CategoryTargetType { get; set; } = ""; - - - /// - /// Write an error to the output pipe, or throw a terminating error. - /// - protected override void ProcessRecord() - { - ErrorRecord errorRecord = this.ErrorRecord; - if (null != errorRecord) - { - // copy constructor - errorRecord = new ErrorRecord(errorRecord, null); - } - else - { - Exception e = this.Exception; - string msg = Message; - if (null == e) - { - e = new WriteErrorException(msg); - } - string errid = ErrorId; - if (String.IsNullOrEmpty(errid)) - { - errid = e.GetType().FullName; - } - errorRecord = new ErrorRecord( - e, - errid, - Category, - TargetObject - ); - - if ((null != this.Exception && !String.IsNullOrEmpty(msg))) - { - errorRecord.ErrorDetails = new ErrorDetails(msg); - } - } - - string recact = RecommendedAction; - if (!String.IsNullOrEmpty(recact)) - { - if (null == errorRecord.ErrorDetails) - { - errorRecord.ErrorDetails = new ErrorDetails(errorRecord.ToString()); - } - errorRecord.ErrorDetails.RecommendedAction = recact; - } - - if (!String.IsNullOrEmpty(CategoryActivity)) - errorRecord.CategoryInfo.Activity = CategoryActivity; - if (!String.IsNullOrEmpty(CategoryReason)) - errorRecord.CategoryInfo.Reason = CategoryReason; - if (!String.IsNullOrEmpty(CategoryTargetName)) - errorRecord.CategoryInfo.TargetName = CategoryTargetName; - if (!String.IsNullOrEmpty(CategoryTargetType)) - errorRecord.CategoryInfo.TargetType = CategoryTargetType; - - /* 2005/01/25 removing throw-error - if (_terminating) - { - ThrowTerminatingError(errorRecord); - } - else - { - */ - - // 2005/07/14-913791 "write-error output is confusing and misleading" - // set InvocationInfo to the script not the command - InvocationInfo myInvocation = GetVariableValue(SpecialVariables.MyInvocation) as InvocationInfo; - if (null != myInvocation) - { - errorRecord.SetInvocationInfo(myInvocation); - errorRecord.PreserveInvocationInfoOnce = true; - if (!String.IsNullOrEmpty(CategoryActivity)) - errorRecord.CategoryInfo.Activity = CategoryActivity; - else - errorRecord.CategoryInfo.Activity = "Write-Error"; - } - - WriteError(errorRecord); - /* - } - */ - }//processrecord - }//WriteOrThrowErrorCommand - - /// - /// This class implements Write-Error command - /// - [Cmdlet(VerbsCommunications.Write, "Error", DefaultParameterSetName = "NoException", - HelpUri = "https://go.microsoft.com/fwlink/?LinkID=113425", RemotingCapability = RemotingCapability.None)] - public sealed class WriteErrorCommand : WriteOrThrowErrorCommand - { - /// - /// constructor - /// - public WriteErrorCommand() - { - } - } - - /* 2005/01/25 removing throw-error - /// - /// This class implements Write-Error command - /// - [Cmdlet("Throw", "Error", DefaultParameterSetName = "NoException")] - public sealed class ThrowErrorCommand : WriteOrThrowErrorCommand - { - /// - /// constructor - /// - public ThrowErrorCommand() - { - using (tracer.TraceConstructor(this)) - { - _terminating = true; - } - } - } - */ - - #endregion WriteOrThrowErrorCommand - - #region WriteErrorException - /// - /// The write-error cmdlet uses WriteErrorException - /// when the user only specifies a string and not - /// an Exception or ErrorRecord. - /// - [Serializable] - public class WriteErrorException : SystemException - { - #region ctor - /// - /// Constructor for class WriteErrorException - /// - /// constructed object - public WriteErrorException() - : base(StringUtil.Format(WriteErrorStrings.WriteErrorException)) - { - } - - /// - /// Constructor for class WriteErrorException - /// - /// - /// constructed object - public WriteErrorException(string message) - : base(message) - { - } - - /// - /// Constructor for class WriteErrorException - /// - /// - /// - /// constructed object - public WriteErrorException(string message, - Exception innerException) - : base(message, innerException) - { - } - #endregion ctor - - #region Serialization - /// - /// Serialization constructor for class WriteErrorException - /// - /// serialization information - /// streaming context - /// constructed object - protected WriteErrorException(SerializationInfo info, - StreamingContext context) - : base(info, context) - { - } - #endregion Serialization - } // WriteErrorException - #endregion WriteErrorException -} //namespace - - - diff --git a/src/Microsoft.PowerShell.Commands.Utility/resources/AddTypeStrings.resx b/src/Microsoft.PowerShell.Commands.Utility/resources/AddTypeStrings.resx index 51762730bac..6b178fb85eb 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/resources/AddTypeStrings.resx +++ b/src/Microsoft.PowerShell.Commands.Utility/resources/AddTypeStrings.resx @@ -117,17 +117,8 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - {0}({1}) : {2} - - - The generated type defines no public methods or properties. - - - The generated type is not public. - - - Cannot add type. The -MemberDefinition parameter is not supported for this language. + + The source code was already compiled and loaded. Cannot add type. The "{0}" extension is not supported. @@ -135,15 +126,6 @@ Cannot add type. Input files must all have the same file extension. - - Cannot add type. Specify only the Language or CodeDomProvider parameters. - - - Cannot add type. The '{0}' language requires Microsoft .NET Framework {1}. - - - Cannot add type. The assembly name {0} matches both {1} and {2}. - Cannot add type. The assembly '{0}' could not be found. @@ -156,22 +138,22 @@ Cannot add type. Compilation errors occurred. - - Cannot add type. One or more required assemblies are missing. - Cannot add type. The OutputType parameter requires that the OutputAssembly parameter be specified. - - Cannot add type due to the following exception: {0}. Verify that Microsoft .NET Framework {1} is installed. On x64-based versions of Windows, you must also install the WOW64 feature. - - - Cannot add type. The '{0}' parameter and the '{1}' parameter cannot both be specified. - Cannot add type. Definition of new types is not supported in this language mode. The specified reference assembly '{0}' is unnecessary and ignored. + + Both the assembly types 'ConsoleApplication' and 'WindowsApplication' are not currently supported. + + + Add-Type Cmdlet + + + Add-Type cmdlet will not be allowed in ConstrainedLanguage mode. + diff --git a/src/Microsoft.PowerShell.Commands.Utility/resources/AliasCommandStrings.resx b/src/Microsoft.PowerShell.Commands.Utility/resources/AliasCommandStrings.resx index 1c3b0e52953..f10c2e64236 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/resources/AliasCommandStrings.resx +++ b/src/Microsoft.PowerShell.Commands.Utility/resources/AliasCommandStrings.resx @@ -135,12 +135,6 @@ Name: {0} Value: {1} - - Cannot export the aliases because path '{0}' referred to a '{1}' provider path. Change the Path parameter to a file system path. - - - Cannot export the aliases because path '{0}' contains wildcard characters that resolved to multiple paths. Aliases can be exported to only one file. Change the value of the Path parameter to a path that resolves to a single file. - Cannot open file {0} to export the alias. {1} diff --git a/src/Microsoft.PowerShell.Commands.Utility/resources/ConvertFromStringData.resx b/src/Microsoft.PowerShell.Commands.Utility/resources/ConvertFromStringData.resx index cf223fb649f..285dea7301e 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/resources/ConvertFromStringData.resx +++ b/src/Microsoft.PowerShell.Commands.Utility/resources/ConvertFromStringData.resx @@ -1,4 +1,4 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Accepted meta properties are content-type, default-style, application-name, author, description, generator, keywords, x-ua-compatible, and viewport. The meta pair: {0} and {1} may not function correctly. + + diff --git a/src/Microsoft.PowerShell.Commands.Utility/resources/ConvertMarkdownStrings.resx b/src/Microsoft.PowerShell.Commands.Utility/resources/ConvertMarkdownStrings.resx new file mode 100644 index 00000000000..d77c7422abe --- /dev/null +++ b/src/Microsoft.PowerShell.Commands.Utility/resources/ConvertMarkdownStrings.resx @@ -0,0 +1,132 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + The type of the input object '{0}' is invalid. + + + Only FileSystem Provider paths are supported. The file path is not supported: '{0}'. + + + The property {0} of the given object is null or empty. + + + Invalid parameter set name: {0}. + + diff --git a/src/Microsoft.PowerShell.Commands.Utility/resources/ConvertStringResources.resx b/src/Microsoft.PowerShell.Commands.Utility/resources/ConvertStringResources.resx index f4994c36d50..9e65acdc23c 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/resources/ConvertStringResources.resx +++ b/src/Microsoft.PowerShell.Commands.Utility/resources/ConvertStringResources.resx @@ -1,4 +1,4 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Processing view defintion '{0}' + + diff --git a/src/Microsoft.PowerShell.Commands.Utility/resources/GetUptimeStrings.resx b/src/Microsoft.PowerShell.Commands.Utility/resources/GetUptimeStrings.resx index 89b82e007c6..f59439da714 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/resources/GetUptimeStrings.resx +++ b/src/Microsoft.PowerShell.Commands.Utility/resources/GetUptimeStrings.resx @@ -1,4 +1,4 @@ - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + The '-Duration' parameter value must not exceed '{0}', provided value was '{1}'. + + diff --git a/src/Microsoft.PowerShell.Commands.Utility/resources/TestJsonCmdletStrings.resx b/src/Microsoft.PowerShell.Commands.Utility/resources/TestJsonCmdletStrings.resx new file mode 100644 index 00000000000..cc607eda418 --- /dev/null +++ b/src/Microsoft.PowerShell.Commands.Utility/resources/TestJsonCmdletStrings.resx @@ -0,0 +1,135 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Cannot parse the JSON schema. + + + Cannot parse the JSON. + + + The JSON is not valid with the schema: {0} at '{1}' + + + Can not open JSON schema file: {0} + + + URI scheme '{0}' is not supported. Only HTTP(S) and local file system URIs are allowed. + + diff --git a/src/Microsoft.PowerShell.Commands.Utility/resources/TraceCommandStrings.resx b/src/Microsoft.PowerShell.Commands.Utility/resources/TraceCommandStrings.resx index 0eb1e1e9466..c09ba1ea796 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/resources/TraceCommandStrings.resx +++ b/src/Microsoft.PowerShell.Commands.Utility/resources/TraceCommandStrings.resx @@ -117,9 +117,6 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - A file listener with name '{0}' was not found. - Trace output can only be written to the file system. The path '{0}' referred to a '{1}' provider path. diff --git a/src/Microsoft.PowerShell.Commands.Utility/resources/UnblockFileStrings.resx b/src/Microsoft.PowerShell.Commands.Utility/resources/UnblockFileStrings.resx new file mode 100644 index 00000000000..b2af7eba67e --- /dev/null +++ b/src/Microsoft.PowerShell.Commands.Utility/resources/UnblockFileStrings.resx @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + The cmdlet does not support Linux. + + + There was an error unblocking {0}. + + diff --git a/src/Microsoft.PowerShell.Commands.Utility/resources/UpdateDataStrings.resx b/src/Microsoft.PowerShell.Commands.Utility/resources/UpdateDataStrings.resx index 9d2ccaced94..ba1bdfd6174 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/resources/UpdateDataStrings.resx +++ b/src/Microsoft.PowerShell.Commands.Utility/resources/UpdateDataStrings.resx @@ -135,9 +135,6 @@ Cannot update a member with type "{0}". Specify a different type for the MemberType parameter. - - The value of the SerializationDepth property should not be negative. - The {0} parameter is required for the type "{1}". Please specify the {0} parameter. diff --git a/src/Microsoft.PowerShell.Commands.Utility/resources/UtilityCommonStrings.resx b/src/Microsoft.PowerShell.Commands.Utility/resources/UtilityCommonStrings.resx index f8e5623d8fc..0eedd2d38d2 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/resources/UtilityCommonStrings.resx +++ b/src/Microsoft.PowerShell.Commands.Utility/resources/UtilityCommonStrings.resx @@ -117,9 +117,6 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - There are no matching results found for {2}. - {2} has one or more exceptions that are not valid. @@ -165,13 +162,16 @@ Cannot use tag '{0}'. The 'PS' prefix is reserved. - - Algorithm '{0}' is not supported in this system. - The file '{0}' could not be parsed as a PowerShell Data File. - - '{0}' is not supported in this system. + + Cannot construct a security descriptor from the given SDDL due to the following error: {0} + + + Invoke-Expression Cmdlet + + + Invoke-Expression cmdlet script block will be run in ConstrainedLanguage mode. diff --git a/src/Microsoft.PowerShell.Commands.Utility/resources/UtilityMshSnapinResources.resx b/src/Microsoft.PowerShell.Commands.Utility/resources/UtilityMshSnapinResources.resx index 387a0ebf0b9..3bca38847db 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/resources/UtilityMshSnapinResources.resx +++ b/src/Microsoft.PowerShell.Commands.Utility/resources/UtilityMshSnapinResources.resx @@ -118,12 +118,12 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - This Windows PowerShell snap-in contains utility cmdlets that are used to view and organize data in different ways. + This PowerShell snap-in contains utility cmdlets that are used to view and organize data in different ways. Microsoft Corporation - Windows PowerShell utility snap-in + PowerShell utility snap-in diff --git a/src/Microsoft.PowerShell.Commands.Utility/resources/VariableCommandStrings.resx b/src/Microsoft.PowerShell.Commands.Utility/resources/VariableCommandStrings.resx index bff7eee52af..d385adda038 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/resources/VariableCommandStrings.resx +++ b/src/Microsoft.PowerShell.Commands.Utility/resources/VariableCommandStrings.resx @@ -123,18 +123,15 @@ Name: {0} Value: {1} + + Use a single variable rather than a collection + New variable Name: {0} Value: {1} - - Add variable - - - Name: {0} - Remove variable diff --git a/src/Microsoft.PowerShell.Commands.Utility/resources/WebCmdletStrings.resx b/src/Microsoft.PowerShell.Commands.Utility/resources/WebCmdletStrings.resx index d1fde8e4cfa..cb080d37012 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/resources/WebCmdletStrings.resx +++ b/src/Microsoft.PowerShell.Commands.Utility/resources/WebCmdletStrings.resx @@ -120,26 +120,53 @@ Access to the path '{0}' is denied. + + The cmdlet cannot protect plain text secrets sent over unencrypted connections. To suppress this warning and send plain text secrets over unencrypted networks, reissue the command specifying the AllowUnencryptedAuthentication parameter. + + + The cmdlet cannot run because the following conflicting parameters are specified: Authentication and UseDefaultCredentials. Authentication does not support Default Credentials. Specify either Authentication or UseDefaultCredentials, then retry. + + + The cmdlet cannot run because the following parameter is not specified: Credential. The supplied Authentication type requires a Credential. Specify Credential, then retry. + + + The cmdlet cannot run because the following parameter is not specified: Token. The supplied Authentication type requires a Token. Specify Token, then retry. + + + The cmdlet cannot run because the following conflicting parameters are specified: Credential and Token. Specify either Credential or Token, then retry. + The cmdlet cannot run because the following conflicting parameters are specified: Body and InFile. Specify either Body or Infile, then retry. + + The cmdlet cannot run because the following conflicting parameters are specified: Body and Form. Specify either Body or Form, then retry. + + + The cmdlet cannot run because the following conflicting parameters are specified: InFile and Form. Specify either InFile or Form, then retry. + + + The cmdlet cannot run because the -ContentType parameter is not a valid Content-Type header. Specify a valid Content-Type for -ContentType, then retry. To suppress header validation, supply the -SkipHeaderValidation parameter. + The cmdlet cannot run because the following conflicting parameters are specified: Credential and UseDefaultCredentials. Specify either Credential or UseDefaultCredentials, then retry. - + Path '{0}' resolves to a directory. Specify a path including a file name, and then retry the command. - - Cannot convert the JSON string because a dictionary that was converted from the string contains the duplicated keys '{0}' and '{1}'. + + The provided JSON includes a property whose name is an empty string, this is only supported using the -AsHashTable switch. - - The ConvertTo-Json and ConvertFrom-Json cmdlets require the installation of the .NET Client Profile, sometimes called the .NET extended profile. + + Cannot convert the JSON string because a dictionary that was converted from the string contains the duplicated key '{0}'. The response content cannot be parsed because the Internet Explorer engine is not available, or Internet Explorer's first-launch configuration is not complete. Specify the UseBasicParsing parameter and try again. - - The converted JSON string is in bad format. + + Cannot follow an insecure redirection by default. Reissue the command specifying the -AllowInsecureRedirect switch. + + + Cannot convert the JSON string because it contains keys with different casing. Please use the -AsHashTable switch instead. The key that was attempted to be added to the existing key '{0}' was '{1}'. The maximum redirection count has been exceeded. To increase the number of redirections allowed, supply a higher value to the -MaximumRedirection parameter. @@ -157,25 +184,28 @@ Path '{0}' is not a file system path. Please specify the path to a file in the file system. - The cmdlet cannot run because the following parameter is missing: OutFile. Provide a valid OutFile parameter value when using the PassThru parameter, then retry. + The cmdlet cannot run because the following parameter is missing: OutFile. Provide a valid OutFile parameter value when using the {0} parameter, then retry. + + + The file will not be re-downloaded because the remote file is the same size as the OutFile: {0} The cmdlet cannot run because the following conflicting parameters are specified: ProxyCredential and ProxyUseDefaultCredentials. Specify either ProxyCredential or ProxyUseDefaultCredentials, then retry. - The cmdlet cannot run because the following parameter is missing: Proxy. Provide a valid proxy URI for the Proxy parameter when using the ProxyCredential or UseDefaultProxyCredentials parameters, then retry. + The cmdlet cannot run because the following parameter is missing: Proxy. Provide a valid proxy URI for the Proxy parameter when using the ProxyCredential or ProxyUseDefaultCredentials parameters, then retry. - Reading web response completed. (Number of bytes read: {0}) + Reading web response stream completed. Bytes downloaded: {0} - Reading web response + Reading web response stream - Reading response stream... (Number of bytes read: {0}) + Downloaded: {0} of {1} - - The operation has timed out. + + The Resume switch can only be used if OutFile targets a file but it resolves to a directory: {0}. The cmdlet cannot run because the following conflicting parameters are specified: Session and SessionVariable. Specify either Session or SessionVariable, then retry. @@ -184,28 +214,16 @@ Unable to retrieve certificates because the thumbprint is not valid. Verify the thumbprint and retry. - Writing web request completed. (Number of bytes remaining: {0}) + Web request completed. (Number of bytes processed: {0}) + + + Web request cancelled. (Number of bytes processed: {0}) - Writing web request + Web request status - Writing request stream... (Number of bytes written: {0}) - - - The ConvertTo-Json and ConvertFrom-Json cmdlets require the 'Json.Net' module. {0} - - - The cmdlet cannot run because the 'Json.Net' module cannot be loaded. Import the module manually or set the $PSModuleAutoLoadingPreference variable to enable module auto loading. For more information, see 'get-help about_Preference_Variables'. - - - However, the 'Json.Net' module could not be loaded. For more information, run 'Import-Module Json.Net'. - - - Ensure 'Json.Net.psd1' and 'Newtonsoft.Json.dll' are available in a versioned subdirectory of '{0}'. - - - The maximum depth allowed for serialization is {0}. + Downloaded: {0} of {1} Conversion from JSON failed with error: {0} @@ -216,10 +234,19 @@ Following rel link {0} - - {0} {1} with {2}-byte payload + + The remote server indicated it could not resume downloading. The local file will be overwritten. + + + Received HTTP/{0} response of content type {1} of unknown size + + + Retrying after interval of {0} seconds. Status code for previous attempt: {1} + + + Resulting JSON is truncated as serialization has exceeded the set depth of {0}. - - received {0}-byte response of content type {1} + + The WebSession properties were changed between requests forcing all HTTP connections in the session to be recreated. diff --git a/src/Microsoft.PowerShell.Commands.Utility/resources/WriteErrorStrings.resx b/src/Microsoft.PowerShell.Commands.Utility/resources/WriteErrorStrings.resx index 435fef2b837..13d2058e7b2 100644 --- a/src/Microsoft.PowerShell.Commands.Utility/resources/WriteErrorStrings.resx +++ b/src/Microsoft.PowerShell.Commands.Utility/resources/WriteErrorStrings.resx @@ -1,4 +1,4 @@ - + + diff --git a/src/Microsoft.PowerShell.GlobalTool.Shim/GlobalToolShim.cs b/src/Microsoft.PowerShell.GlobalTool.Shim/GlobalToolShim.cs new file mode 100644 index 00000000000..356cde68152 --- /dev/null +++ b/src/Microsoft.PowerShell.GlobalTool.Shim/GlobalToolShim.cs @@ -0,0 +1,56 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Runtime.InteropServices; + +namespace Microsoft.PowerShell.GlobalTool.Shim +{ + /// + /// Shim layer to chose the appropriate runtime for PowerShell DotNet Global tool. + /// + public static class EntryPoint + { + private const string PwshDllName = "pwsh.dll"; + + private const string WinFolderName = "win"; + + private const string UnixFolderName = "unix"; + + /// + /// Entry point for the global tool. + /// + /// Arguments passed to the global tool.' + /// Exit code returned by pwsh. + public static int Main(string[] args) + { + var currentPath = new FileInfo(System.Reflection.Assembly.GetEntryAssembly().Location).Directory.FullName; + var isWindows = OperatingSystem.IsWindows(); + + string platformFolder = isWindows ? WinFolderName : UnixFolderName; + + var arguments = new List(args.Length + 1); + var pwshPath = Path.Combine(currentPath, platformFolder, PwshDllName); + arguments.Add(pwshPath); + arguments.AddRange(args); + + if (File.Exists(pwshPath)) + { + Console.CancelKeyPress += (sender, e) => + { + e.Cancel = true; + }; + + var process = System.Diagnostics.Process.Start("dotnet", arguments); + process.WaitForExit(); + return process.ExitCode; + } + else + { + throw new FileNotFoundException(pwshPath); + } + } + } +} diff --git a/src/Microsoft.PowerShell.GlobalTool.Shim/Microsoft.PowerShell.GlobalTool.Shim.csproj b/src/Microsoft.PowerShell.GlobalTool.Shim/Microsoft.PowerShell.GlobalTool.Shim.csproj new file mode 100644 index 00000000000..d0203344cc2 --- /dev/null +++ b/src/Microsoft.PowerShell.GlobalTool.Shim/Microsoft.PowerShell.GlobalTool.Shim.csproj @@ -0,0 +1,18 @@ + + + + + Shim for global tool to select appropriate runtime + Microsoft.PowerShell.GlobalTool.Shim + EXE + Microsoft.PowerShell.GlobalTool.Shim + False + + + + + + + + + diff --git a/src/Microsoft.PowerShell.GlobalTool.Shim/runtimeconfig.template.json b/src/Microsoft.PowerShell.GlobalTool.Shim/runtimeconfig.template.json new file mode 100644 index 00000000000..4a5e3e367ec --- /dev/null +++ b/src/Microsoft.PowerShell.GlobalTool.Shim/runtimeconfig.template.json @@ -0,0 +1,4 @@ +// This is required to roll forward to supported minor.patch versions of the runtime. +{ + "rollForwardOnNoCandidateFx": 1 +} diff --git a/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/Commands/AddLocalGroupMemberCommand.cs b/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/Commands/AddLocalGroupMemberCommand.cs index 65540314dcc..59cb3067efc 100644 --- a/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/Commands/AddLocalGroupMemberCommand.cs +++ b/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/Commands/AddLocalGroupMemberCommand.cs @@ -1,3 +1,6 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + #region Using directives using System; using System.Collections.Generic; @@ -11,7 +14,6 @@ using System.Diagnostics.CodeAnalysis; #endregion - namespace Microsoft.PowerShell.Commands { /// @@ -40,8 +42,10 @@ public class AddLocalGroupMemberCommand : PSCmdlet public Microsoft.PowerShell.Commands.LocalGroup Group { get { return this.group;} + set { this.group = value; } } + private Microsoft.PowerShell.Commands.LocalGroup group; /// @@ -59,8 +63,10 @@ public Microsoft.PowerShell.Commands.LocalGroup Group public Microsoft.PowerShell.Commands.LocalPrincipal[] Member { get { return this.member;} + set { this.member = value; } } + private Microsoft.PowerShell.Commands.LocalPrincipal[] member; /// @@ -74,8 +80,10 @@ public Microsoft.PowerShell.Commands.LocalPrincipal[] Member public string Name { get { return this.name;} + set { this.name = value; } } + private string name; /// @@ -89,12 +97,13 @@ public string Name public System.Security.Principal.SecurityIdentifier SID { get { return this.sid;} + set { this.sid = value; } } + private System.Security.Principal.SecurityIdentifier sid; #endregion Parameter Properties - #region Cmdlet Overrides /// /// BeginProcessing method. @@ -104,7 +113,6 @@ protected override void BeginProcessing() sam = new Sam(); } - /// /// ProcessRecord method. /// @@ -125,7 +133,6 @@ protected override void ProcessRecord() } } - /// /// EndProcessing method. /// @@ -203,13 +210,13 @@ private LocalPrincipal MakePrincipal(string groupId, LocalPrincipal member) } } } + if (CheckShouldProcess(principal, groupId)) return principal; return null; } - /// /// Determine if a principal should be processed. /// Just a wrapper around Cmdlet.ShouldProcess, with localized string @@ -245,10 +252,10 @@ private void ProcessGroup(LocalGroup group) foreach (var member in this.Member) { LocalPrincipal principal = MakePrincipal(groupId, member); - if (null != principal) + if (principal != null) { var ex = sam.AddLocalGroupMember(group, principal); - if (null != ex) + if (ex != null) { WriteError(ex.MakeErrorRecord()); } @@ -279,10 +286,10 @@ private void ProcessSid(SecurityIdentifier groupSid) foreach (var member in this.Member) { LocalPrincipal principal = MakePrincipal(groupSid.ToString(), member); - if (null != principal) + if (principal != null) { var ex = sam.AddLocalGroupMember(groupSid, principal); - if (null != ex) + if (ex != null) { WriteError(ex.MakeErrorRecord()); } @@ -291,7 +298,6 @@ private void ProcessSid(SecurityIdentifier groupSid) } #endregion Private Methods - }//End Class - -}//End namespace + } +} diff --git a/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/Commands/DisableLocalUserCommand.cs b/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/Commands/DisableLocalUserCommand.cs index 9d7d4c5ca3c..592c77726fe 100644 --- a/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/Commands/DisableLocalUserCommand.cs +++ b/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/Commands/DisableLocalUserCommand.cs @@ -1,3 +1,6 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + #region Using directives using System; using System.Collections.Generic; @@ -10,7 +13,6 @@ using System.Diagnostics.CodeAnalysis; #endregion - namespace Microsoft.PowerShell.Commands { /// @@ -48,8 +50,10 @@ public class DisableLocalUserCommand : Cmdlet public Microsoft.PowerShell.Commands.LocalUser[] InputObject { get { return this.inputobject; } + set { this.inputobject = value; } } + private Microsoft.PowerShell.Commands.LocalUser[] inputobject; /// @@ -67,8 +71,10 @@ public Microsoft.PowerShell.Commands.LocalUser[] InputObject public string[] Name { get { return this.name; } + set { this.name = value; } } + private string[] name; /// @@ -86,12 +92,13 @@ public string[] Name public System.Security.Principal.SecurityIdentifier[] SID { get { return this.sid;} + set { this.sid = value; } } + private System.Security.Principal.SecurityIdentifier[] sid; #endregion Parameter Properties - #region Cmdlet Overrides /// /// BeginProcessing method. @@ -101,7 +108,6 @@ protected override void BeginProcessing() sam = new Sam(); } - /// /// ProcessRecord method. /// @@ -119,7 +125,6 @@ protected override void ProcessRecord() } } - /// /// EndProcessing method. /// @@ -135,7 +140,7 @@ protected override void EndProcessing() #region Private Methods /// - /// Process users requested by -Name + /// Process users requested by -Name. /// /// /// All arguments to -Name will be treated as names, @@ -161,7 +166,7 @@ private void ProcessNames() } /// - /// Process users requested by -SID + /// Process users requested by -SID. /// private void ProcessSids() { @@ -183,7 +188,7 @@ private void ProcessSids() } /// - /// Process users requested by -InputObject + /// Process users requested by -InputObject. /// private void ProcessUsers() { @@ -209,7 +214,6 @@ private bool CheckShouldProcess(string target) return ShouldProcess(target, Strings.ActionDisableUser); } #endregion Private Methods - }//End Class - -}//End namespace + } +} diff --git a/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/Commands/EnableLocalUserCommand.cs b/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/Commands/EnableLocalUserCommand.cs index cf0562f23f5..88627ba2e33 100644 --- a/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/Commands/EnableLocalUserCommand.cs +++ b/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/Commands/EnableLocalUserCommand.cs @@ -1,3 +1,6 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + #region Using directives using System; using System.Collections.Generic; @@ -10,7 +13,6 @@ using System.Diagnostics.CodeAnalysis; #endregion - namespace Microsoft.PowerShell.Commands { /// @@ -48,8 +50,10 @@ public class EnableLocalUserCommand : Cmdlet public Microsoft.PowerShell.Commands.LocalUser[] InputObject { get { return this.inputobject; } + set { this.inputobject = value; } } + private Microsoft.PowerShell.Commands.LocalUser[] inputobject; /// @@ -67,8 +71,10 @@ public Microsoft.PowerShell.Commands.LocalUser[] InputObject public string[] Name { get { return this.name; } + set { this.name = value; } } + private string[] name; /// @@ -86,14 +92,13 @@ public string[] Name public System.Security.Principal.SecurityIdentifier[] SID { get { return this.sid;} + set { this.sid = value; } } + private System.Security.Principal.SecurityIdentifier[] sid; #endregion Parameter Properties - - - #region Cmdlet Overrides /// /// BeginProcessing method. @@ -103,7 +108,6 @@ protected override void BeginProcessing() sam = new Sam(); } - /// /// ProcessRecord method. /// @@ -121,7 +125,6 @@ protected override void ProcessRecord() } } - /// /// EndProcessing method. /// @@ -137,7 +140,7 @@ protected override void EndProcessing() #region Private Methods /// - /// Process users requested by -Name + /// Process users requested by -Name. /// /// /// All arguments to -Name will be treated as names, @@ -163,7 +166,7 @@ private void ProcessNames() } /// - /// Process users requested by -SID + /// Process users requested by -SID. /// private void ProcessSids() { @@ -185,7 +188,7 @@ private void ProcessSids() } /// - /// Process users requested by -InputObject + /// Process users requested by -InputObject. /// private void ProcessUsers() { @@ -211,7 +214,6 @@ private bool CheckShouldProcess(string target) return ShouldProcess(target, Strings.ActionEnableUser); } #endregion Private Methods - }//End Class - -}//End namespace + } +} diff --git a/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/Commands/GetLocalGroupCommand.cs b/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/Commands/GetLocalGroupCommand.cs index bd93d56fa6e..4c11a3c4c36 100644 --- a/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/Commands/GetLocalGroupCommand.cs +++ b/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/Commands/GetLocalGroupCommand.cs @@ -1,3 +1,6 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + #region Using directives using System; using System.Management.Automation; @@ -8,7 +11,6 @@ using System.Diagnostics.CodeAnalysis; #endregion - namespace Microsoft.PowerShell.Commands { /// @@ -39,8 +41,10 @@ public class GetLocalGroupCommand : Cmdlet public string[] Name { get { return this.name; } + set { this.name = value; } } + private string[] name; /// @@ -56,14 +60,13 @@ public string[] Name public System.Security.Principal.SecurityIdentifier[] SID { get { return this.sid;} + set { this.sid = value; } } + private System.Security.Principal.SecurityIdentifier[] sid; #endregion Parameter Properties - - - #region Cmdlet Overrides /// /// BeginProcessing method. @@ -73,7 +76,6 @@ protected override void BeginProcessing() sam = new Sam(); } - /// /// ProcessRecord method. /// @@ -91,7 +93,6 @@ protected override void ProcessRecord() ProcessSids(); } - /// /// EndProcessing method. /// @@ -107,7 +108,7 @@ protected override void EndProcessing() #region Private Methods /// - /// Process groups requested by -Name + /// Process groups requested by -Name. /// /// /// All arguments to -Name will be treated as names, @@ -144,7 +145,7 @@ private void ProcessNames() } /// - /// Process groups requested by -SID + /// Process groups requested by -SID. /// private void ProcessSids() { @@ -164,7 +165,6 @@ private void ProcessSids() } } #endregion Private Methods - }//End Class - -}//End namespace + } +} diff --git a/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/Commands/GetLocalGroupMemberCommand.cs b/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/Commands/GetLocalGroupMemberCommand.cs index 0acb4d03ded..0b3625f6ef7 100644 --- a/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/Commands/GetLocalGroupMemberCommand.cs +++ b/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/Commands/GetLocalGroupMemberCommand.cs @@ -1,3 +1,6 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + #region Using directives using System; using System.Collections.Generic; @@ -8,7 +11,6 @@ using System.Management.Automation.SecurityAccountsManager.Extensions; #endregion - namespace Microsoft.PowerShell.Commands { /// @@ -38,8 +40,10 @@ public class GetLocalGroupMemberCommand : Cmdlet public Microsoft.PowerShell.Commands.LocalGroup Group { get { return this.group;} + set { this.group = value; } } + private Microsoft.PowerShell.Commands.LocalGroup group; /// @@ -53,8 +57,10 @@ public Microsoft.PowerShell.Commands.LocalGroup Group public string Member { get { return this.member;} + set { this.member = value; } } + private string member; /// @@ -70,8 +76,10 @@ public string Member public string Name { get { return this.name;} + set { this.name = value; } } + private string name; /// @@ -87,12 +95,13 @@ public string Name public System.Security.Principal.SecurityIdentifier SID { get { return this.sid;} + set { this.sid = value; } } + private System.Security.Principal.SecurityIdentifier sid; #endregion Parameter Properties - #region Cmdlet Overrides /// /// BeginProcessing method. @@ -102,7 +111,6 @@ protected override void BeginProcessing() sam = new Sam(); } - /// /// ProcessRecord method. /// @@ -128,7 +136,6 @@ protected override void ProcessRecord() } } - /// /// EndProcessing method. /// @@ -150,12 +157,12 @@ private IEnumerable ProcessesMembership(IEnumerable(membership); } else { - //var rv = new List(); + // var rv = new List(); rv = new List(); if (WildcardPattern.ContainsWildcardCharacters(Member)) @@ -203,7 +210,7 @@ private IEnumerable ProcessesMembership(IEnumerable string.Compare(p1.Name, p2.Name, StringComparison.CurrentCultureIgnoreCase)); + rv.Sort(static (p1, p2) => string.Compare(p1.Name, p2.Name, StringComparison.CurrentCultureIgnoreCase)); return rv; } @@ -223,7 +230,6 @@ private IEnumerable ProcessSid(SecurityIdentifier groupSid) return ProcessesMembership(sam.GetLocalGroupMembers(groupSid)); } #endregion Private Methods - }//End Class - -}//End namespace + } +} diff --git a/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/Commands/GetLocalUserCommand.cs b/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/Commands/GetLocalUserCommand.cs index e37acc70611..9f8d3b9311b 100644 --- a/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/Commands/GetLocalUserCommand.cs +++ b/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/Commands/GetLocalUserCommand.cs @@ -1,3 +1,6 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + #region Using directives using System; using System.Collections.Generic; @@ -8,7 +11,6 @@ using System.Management.Automation.SecurityAccountsManager.Extensions; #endregion - namespace Microsoft.PowerShell.Commands { /// @@ -44,6 +46,7 @@ public string[] Name set { this.name = value; } } + private string[] name; /// @@ -59,13 +62,13 @@ public string[] Name public System.Security.Principal.SecurityIdentifier[] SID { get { return this.sid; } + set { this.sid = value; } } + private System.Security.Principal.SecurityIdentifier[] sid; #endregion Parameter Properties - - #region Cmdlet Overrides /// /// BeginProcessing method. @@ -75,7 +78,6 @@ protected override void BeginProcessing() sam = new Sam(); } - /// /// ProcessRecord method. /// @@ -93,7 +95,6 @@ protected override void ProcessRecord() ProcessSids(); } - /// /// EndProcessing method. /// @@ -109,7 +110,7 @@ protected override void EndProcessing() #region Private Methods /// - /// Process users requested by -Name + /// Process users requested by -Name. /// /// /// All arguments to -Name will be treated as names, @@ -146,7 +147,7 @@ private void ProcessNames() } /// - /// Process users requested by -SID + /// Process users requested by -SID. /// private void ProcessSids() { @@ -166,7 +167,6 @@ private void ProcessSids() } } #endregion Private Methods - }//End Class - -}//End namespace + } +} diff --git a/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/Commands/NewLocalGroupCommand.cs b/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/Commands/NewLocalGroupCommand.cs index 35a6272de96..b709d22a485 100644 --- a/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/Commands/NewLocalGroupCommand.cs +++ b/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/Commands/NewLocalGroupCommand.cs @@ -1,3 +1,6 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + #region Using directives using System; using System.Management.Automation; @@ -8,7 +11,6 @@ using Microsoft.PowerShell.LocalAccounts; #endregion - namespace Microsoft.PowerShell.Commands { /// @@ -35,8 +37,10 @@ public class NewLocalGroupCommand : Cmdlet public string Description { get { return this.description;} + set { this.description = value; } } + private string description; /// @@ -52,13 +56,13 @@ public string Description public string Name { get { return this.name;} + set { this.name = value; } } + private string name; #endregion Parameter Properties - - #region Cmdlet Overrides /// /// BeginProcessing method. @@ -68,7 +72,6 @@ protected override void BeginProcessing() sam = new Sam(); } - /// /// ProcessRecord method. /// @@ -93,7 +96,6 @@ protected override void ProcessRecord() } } - /// /// EndProcessing method. /// @@ -113,7 +115,6 @@ private bool CheckShouldProcess(string target) return ShouldProcess(target, Strings.ActionNewGroup); } #endregion Private Methods - }//End Class - -}//End namespace + } +} diff --git a/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/Commands/NewLocalUserCommand.cs b/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/Commands/NewLocalUserCommand.cs index 70690eb8584..d3402b5a4c9 100644 --- a/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/Commands/NewLocalUserCommand.cs +++ b/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/Commands/NewLocalUserCommand.cs @@ -1,3 +1,6 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + #region Using directives using System; using System.Management.Automation; @@ -8,7 +11,6 @@ using Microsoft.PowerShell.LocalAccounts; #endregion - namespace Microsoft.PowerShell.Commands { /// @@ -48,8 +50,10 @@ public class NewLocalUserCommand : PSCmdlet public System.DateTime AccountExpires { get { return this.accountexpires;} + set { this.accountexpires = value; } } + private System.DateTime accountexpires; // This parameter added by hand (copied from SetLocalUserCommand), not by Cmdlet Designer @@ -61,8 +65,10 @@ public System.DateTime AccountExpires public System.Management.Automation.SwitchParameter AccountNeverExpires { get { return this.accountneverexpires;} + set { this.accountneverexpires = value; } } + private System.Management.Automation.SwitchParameter accountneverexpires; /// @@ -74,8 +80,10 @@ public System.Management.Automation.SwitchParameter AccountNeverExpires public string Description { get { return this.description;} + set { this.description = value; } } + private string description; /// @@ -86,8 +94,10 @@ public string Description public System.Management.Automation.SwitchParameter Disabled { get { return this.disabled;} + set { this.disabled = value; } } + private System.Management.Automation.SwitchParameter disabled; /// @@ -100,8 +110,10 @@ public System.Management.Automation.SwitchParameter Disabled public string FullName { get { return this.fullname;} + set { this.fullname = value; } } + private string fullname; /// @@ -118,8 +130,10 @@ public string FullName public string Name { get { return this.name;} + set { this.name = value; } } + private string name; /// @@ -134,8 +148,10 @@ public string Name public System.Security.SecureString Password { get { return this.password;} + set { this.password = value; } } + private System.Security.SecureString password; /// @@ -148,8 +164,10 @@ public System.Security.SecureString Password public System.Management.Automation.SwitchParameter NoPassword { get { return this.nopassword; } + set { this.nopassword = value; } } + private System.Management.Automation.SwitchParameter nopassword; /// @@ -161,8 +179,10 @@ public System.Management.Automation.SwitchParameter NoPassword public System.Management.Automation.SwitchParameter PasswordNeverExpires { get { return this.passwordneverexpires; } + set { this.passwordneverexpires = value; } } + private System.Management.Automation.SwitchParameter passwordneverexpires; /// @@ -174,13 +194,13 @@ public System.Management.Automation.SwitchParameter PasswordNeverExpires public System.Management.Automation.SwitchParameter UserMayNotChangePassword { get { return this.usermaynotchangepassword;} + set { this.usermaynotchangepassword = value; } } + private System.Management.Automation.SwitchParameter usermaynotchangepassword; #endregion Parameter Properties - - #region Cmdlet Overrides /// /// BeginProcessing method. @@ -192,10 +212,10 @@ protected override void BeginProcessing() InvalidParametersException ex = new InvalidParametersException("AccountExpires", "AccountNeverExpires"); ThrowTerminatingError(ex.MakeErrorRecord()); } + sam = new Sam(); } - /// /// ProcessRecord method. /// @@ -250,7 +270,6 @@ protected override void ProcessRecord() } } - /// /// EndProcessing method. /// @@ -270,7 +289,6 @@ private bool CheckShouldProcess(string target) return ShouldProcess(target, Strings.ActionNewUser); } #endregion Private Methods - }//End Class - -}//End namespace + } +} diff --git a/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/Commands/RemoveLocalGroupCommand.cs b/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/Commands/RemoveLocalGroupCommand.cs index cb93071ca5d..981643cf04c 100644 --- a/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/Commands/RemoveLocalGroupCommand.cs +++ b/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/Commands/RemoveLocalGroupCommand.cs @@ -1,3 +1,6 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + #region Using directives using System; using System.Management.Automation; @@ -9,7 +12,6 @@ using System.Diagnostics.CodeAnalysis; #endregion - namespace Microsoft.PowerShell.Commands { /// @@ -41,8 +43,10 @@ public class RemoveLocalGroupCommand : Cmdlet public Microsoft.PowerShell.Commands.LocalGroup[] InputObject { get { return this.inputobject; } + set { this.inputobject = value; } } + private Microsoft.PowerShell.Commands.LocalGroup[] inputobject; /// @@ -60,8 +64,10 @@ public Microsoft.PowerShell.Commands.LocalGroup[] InputObject public string[] Name { get { return this.name; } + set { this.name = value; } } + private string[] name; /// @@ -79,12 +85,13 @@ public string[] Name public System.Security.Principal.SecurityIdentifier[] SID { get { return this.sid; } + set { this.sid = value; } } + private System.Security.Principal.SecurityIdentifier[] sid; #endregion Parameter Properties - #region Cmdlet Overrides /// /// BeginProcessing method. @@ -94,7 +101,6 @@ protected override void BeginProcessing() sam = new Sam(); } - /// /// ProcessRecord method. /// @@ -112,7 +118,6 @@ protected override void ProcessRecord() } } - /// /// EndProcessing method. /// @@ -128,7 +133,7 @@ protected override void EndProcessing() #region Private Methods /// - /// Process groups requested by -Name + /// Process groups requested by -Name. /// /// /// All arguments to -Name will be treated as names, @@ -154,7 +159,7 @@ private void ProcessNames() } /// - /// Process groups requested by -SID + /// Process groups requested by -SID. /// private void ProcessSids() { @@ -176,7 +181,7 @@ private void ProcessSids() } /// - /// Process groups given through -InputObject + /// Process groups given through -InputObject. /// private void ProcessGroups() { @@ -202,7 +207,6 @@ private bool CheckShouldProcess(string target) return ShouldProcess(target, Strings.ActionRemoveGroup); } #endregion Private Methods - }//End Class - -}//End namespace + } +} diff --git a/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/Commands/RemoveLocalGroupMemberCommand.cs b/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/Commands/RemoveLocalGroupMemberCommand.cs index b8c87bef98a..47d0a77784e 100644 --- a/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/Commands/RemoveLocalGroupMemberCommand.cs +++ b/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/Commands/RemoveLocalGroupMemberCommand.cs @@ -1,3 +1,6 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + #region Using directives using System; using System.Collections.Generic; @@ -11,7 +14,6 @@ using System.Diagnostics.CodeAnalysis; #endregion - namespace Microsoft.PowerShell.Commands { /// @@ -40,8 +42,10 @@ public class RemoveLocalGroupMemberCommand : PSCmdlet public Microsoft.PowerShell.Commands.LocalGroup Group { get { return this.group;} + set { this.group = value; } } + private Microsoft.PowerShell.Commands.LocalGroup group; /// @@ -59,8 +63,10 @@ public Microsoft.PowerShell.Commands.LocalGroup Group public Microsoft.PowerShell.Commands.LocalPrincipal[] Member { get { return this.member;} + set { this.member = value; } } + private Microsoft.PowerShell.Commands.LocalPrincipal[] member; /// @@ -74,8 +80,10 @@ public Microsoft.PowerShell.Commands.LocalPrincipal[] Member public string Name { get { return this.name;} + set { this.name = value; } } + private string name; /// @@ -89,12 +97,13 @@ public string Name public System.Security.Principal.SecurityIdentifier SID { get { return this.sid;} + set { this.sid = value; } } + private System.Security.Principal.SecurityIdentifier sid; #endregion Parameter Properties - #region Cmdlet Overrides /// /// BeginProcessing method. @@ -104,7 +113,6 @@ protected override void BeginProcessing() sam = new Sam(); } - /// /// ProcessRecord method. /// @@ -125,7 +133,6 @@ protected override void ProcessRecord() } } - /// /// EndProcessing method. /// @@ -244,10 +251,10 @@ private void ProcessGroup(LocalGroup group) foreach (var member in this.Member) { LocalPrincipal principal = MakePrincipal(groupId, member); - if (null != principal) + if (principal != null) { var ex = sam.RemoveLocalGroupMember(group, principal); - if (null != ex) + if (ex != null) { WriteError(ex.MakeErrorRecord()); } @@ -278,10 +285,10 @@ private void ProcessSid(SecurityIdentifier groupSid) foreach (var member in this.Member) { LocalPrincipal principal = MakePrincipal(groupSid.ToString(), member); - if (null != principal) + if (principal != null) { var ex = sam.RemoveLocalGroupMember(groupSid, principal); - if (null != ex) + if (ex != null) { WriteError(ex.MakeErrorRecord()); } @@ -289,7 +296,6 @@ private void ProcessSid(SecurityIdentifier groupSid) } } #endregion Private Methods - }//End Class - -}//End namespace + } +} diff --git a/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/Commands/RemoveLocalUserCommand.cs b/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/Commands/RemoveLocalUserCommand.cs index cbcef6ef48f..6d6081fef7e 100644 --- a/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/Commands/RemoveLocalUserCommand.cs +++ b/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/Commands/RemoveLocalUserCommand.cs @@ -1,3 +1,6 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + #region Using directives using System; using System.Management.Automation; @@ -9,7 +12,6 @@ using System.Diagnostics.CodeAnalysis; #endregion - namespace Microsoft.PowerShell.Commands { /// @@ -42,8 +44,10 @@ public class RemoveLocalUserCommand : Cmdlet public Microsoft.PowerShell.Commands.LocalUser[] InputObject { get { return this.inputobject;} + set { this.inputobject = value; } } + private Microsoft.PowerShell.Commands.LocalUser[] inputobject; /// @@ -61,8 +65,10 @@ public Microsoft.PowerShell.Commands.LocalUser[] InputObject public string[] Name { get { return this.name; } + set { this.name = value; } } + private string[] name; /// @@ -80,13 +86,13 @@ public string[] Name public System.Security.Principal.SecurityIdentifier[] SID { get { return this.sid; } + set { this.sid = value; } } + private System.Security.Principal.SecurityIdentifier[] sid; #endregion Parameter Properties - - #region Cmdlet Overrides /// /// BeginProcessing method. @@ -96,7 +102,6 @@ protected override void BeginProcessing() sam = new Sam(); } - /// /// ProcessRecord method. /// @@ -114,7 +119,6 @@ protected override void ProcessRecord() } } - /// /// EndProcessing method. /// @@ -130,7 +134,7 @@ protected override void EndProcessing() #region Private Methods /// - /// Process users requested by -Name + /// Process users requested by -Name. /// /// /// All arguments to -Name will be treated as names, @@ -156,7 +160,7 @@ private void ProcessNames() } /// - /// Process users requested by -SID + /// Process users requested by -SID. /// private void ProcessSids() { @@ -178,7 +182,7 @@ private void ProcessSids() } /// - /// Process users given through -InputObject + /// Process users given through -InputObject. /// private void ProcessUsers() { @@ -204,7 +208,6 @@ private bool CheckShouldProcess(string target) return ShouldProcess(target, Strings.ActionRemoveUser); } #endregion Private Methods - }//End Class - -}//End namespace + } +} diff --git a/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/Commands/RenameLocalGroupCommand.cs b/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/Commands/RenameLocalGroupCommand.cs index 25254cf05fc..c594fccb889 100644 --- a/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/Commands/RenameLocalGroupCommand.cs +++ b/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/Commands/RenameLocalGroupCommand.cs @@ -1,3 +1,6 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + #region Using directives using System; using System.Management.Automation; @@ -8,7 +11,6 @@ using Microsoft.PowerShell.LocalAccounts; #endregion - namespace Microsoft.PowerShell.Commands { /// @@ -40,8 +42,10 @@ public class RenameLocalGroupCommand : Cmdlet public Microsoft.PowerShell.Commands.LocalGroup InputObject { get { return this.inputobject;} + set { this.inputobject = value; } } + private Microsoft.PowerShell.Commands.LocalGroup inputobject; /// @@ -58,8 +62,10 @@ public Microsoft.PowerShell.Commands.LocalGroup InputObject public string Name { get { return this.name;} + set { this.name = value; } } + private string name; /// @@ -73,8 +79,10 @@ public string Name public string NewName { get { return this.newname;} + set { this.newname = value; } } + private string newname; /// @@ -90,12 +98,13 @@ public string NewName public System.Security.Principal.SecurityIdentifier SID { get { return this.sid;} + set { this.sid = value; } } + private System.Security.Principal.SecurityIdentifier sid; #endregion Parameter Properties - #region Cmdlet Overrides /// /// BeginProcessing method. @@ -105,7 +114,6 @@ protected override void BeginProcessing() sam = new Sam(); } - /// /// ProcessRecord method. /// @@ -123,7 +131,6 @@ protected override void ProcessRecord() } } - /// /// EndProcessing method. /// @@ -139,7 +146,7 @@ protected override void EndProcessing() #region Private Methods /// - /// Process group requested by -Name + /// Process group requested by -Name. /// /// /// Arguments to -Name will be treated as names, @@ -162,7 +169,7 @@ private void ProcessName() } /// - /// Process group requested by -SID + /// Process group requested by -SID. /// private void ProcessSid() { @@ -181,7 +188,7 @@ private void ProcessSid() } /// - /// Process group given through -InputObject + /// Process group given through -InputObject. /// private void ProcessGroup() { @@ -220,7 +227,6 @@ private bool CheckShouldProcess(string groupName, string newName) return ShouldProcess(groupName, msg); } #endregion Private Methods - }//End Class - -}//End namespace + } +} diff --git a/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/Commands/RenameLocalUserCommand.cs b/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/Commands/RenameLocalUserCommand.cs index 4a48b659e62..c23cf41ac61 100644 --- a/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/Commands/RenameLocalUserCommand.cs +++ b/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/Commands/RenameLocalUserCommand.cs @@ -1,3 +1,6 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + #region Using directives using System; using System.Management.Automation; @@ -8,7 +11,6 @@ using Microsoft.PowerShell.LocalAccounts; #endregion - namespace Microsoft.PowerShell.Commands { /// @@ -40,8 +42,10 @@ public class RenameLocalUserCommand : Cmdlet public Microsoft.PowerShell.Commands.LocalUser InputObject { get { return this.inputobject;} + set { this.inputobject = value; } } + private Microsoft.PowerShell.Commands.LocalUser inputobject; /// @@ -58,8 +62,10 @@ public Microsoft.PowerShell.Commands.LocalUser InputObject public string Name { get { return this.name;} + set { this.name = value; } } + private string name; /// @@ -73,8 +79,10 @@ public string Name public string NewName { get { return this.newname;} + set { this.newname = value; } } + private string newname; /// @@ -90,13 +98,13 @@ public string NewName public System.Security.Principal.SecurityIdentifier SID { get { return this.sid;} + set { this.sid = value; } } + private System.Security.Principal.SecurityIdentifier sid; #endregion Parameter Properties - - #region Cmdlet Overrides /// /// BeginProcessing method. @@ -106,7 +114,6 @@ protected override void BeginProcessing() sam = new Sam(); } - /// /// ProcessRecord method. /// @@ -124,7 +131,6 @@ protected override void ProcessRecord() } } - /// /// EndProcessing method. /// @@ -140,7 +146,7 @@ protected override void EndProcessing() #region Private Methods /// - /// Process user requested by -Name + /// Process user requested by -Name. /// /// /// Arguments to -Name will be treated as names, @@ -163,7 +169,7 @@ private void ProcessName() } /// - /// Process user requested by -SID + /// Process user requested by -SID. /// private void ProcessSid() { @@ -182,7 +188,7 @@ private void ProcessSid() } /// - /// Process group given through -InputObject + /// Process group given through -InputObject. /// private void ProcessUser() { @@ -221,7 +227,6 @@ private bool CheckShouldProcess(string userName, string newName) return ShouldProcess(userName, msg); } #endregion Private Methods - }//End Class - -}//End namespace + } +} diff --git a/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/Commands/SetLocalGroupCommand.cs b/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/Commands/SetLocalGroupCommand.cs index eeaf701bffd..49ae31dacc7 100644 --- a/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/Commands/SetLocalGroupCommand.cs +++ b/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/Commands/SetLocalGroupCommand.cs @@ -1,3 +1,6 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + #region Using directives using System; using System.Management.Automation; @@ -8,7 +11,6 @@ using Microsoft.PowerShell.LocalAccounts; #endregion - namespace Microsoft.PowerShell.Commands { /// @@ -35,8 +37,10 @@ public class SetLocalGroupCommand : Cmdlet public string Description { get { return this.description;} + set { this.description = value; } } + private string description; /// @@ -53,8 +57,10 @@ public string Description public Microsoft.PowerShell.Commands.LocalGroup InputObject { get { return this.inputobject;} + set { this.inputobject = value; } } + private Microsoft.PowerShell.Commands.LocalGroup inputobject; /// @@ -71,8 +77,10 @@ public Microsoft.PowerShell.Commands.LocalGroup InputObject public string Name { get { return this.name;} + set { this.name = value; } } + private string name; /// @@ -88,13 +96,13 @@ public string Name public System.Security.Principal.SecurityIdentifier SID { get { return this.sid;} + set { this.sid = value; } } + private System.Security.Principal.SecurityIdentifier sid; #endregion Parameter Properties - - #region Cmdlet Overrides /// /// BeginProcessing method. @@ -104,7 +112,6 @@ protected override void BeginProcessing() sam = new Sam(); } - /// /// ProcessRecord method. /// @@ -148,7 +155,6 @@ protected override void ProcessRecord() } } - /// /// EndProcessing method. /// @@ -168,7 +174,6 @@ private bool CheckShouldProcess(string target) return ShouldProcess(target, Strings.ActionSetGroup); } #endregion Private Methods - }//End Class - -}//End namespace + } +} diff --git a/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/Commands/SetLocalUserCommand.cs b/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/Commands/SetLocalUserCommand.cs index 4ae4e422d39..3bfdc24ac03 100644 --- a/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/Commands/SetLocalUserCommand.cs +++ b/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/Commands/SetLocalUserCommand.cs @@ -1,3 +1,6 @@ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + #region Using directives using System; using System.Management.Automation; @@ -8,7 +11,6 @@ using Microsoft.PowerShell.LocalAccounts; #endregion - namespace Microsoft.PowerShell.Commands { /// @@ -52,8 +54,10 @@ public class SetLocalUserCommand : PSCmdlet public System.DateTime AccountExpires { get { return this.accountexpires;} + set { this.accountexpires = value; } } + private System.DateTime accountexpires; /// @@ -64,8 +68,10 @@ public System.DateTime AccountExpires public System.Management.Automation.SwitchParameter AccountNeverExpires { get { return this.accountneverexpires;} + set { this.accountneverexpires = value; } } + private System.Management.Automation.SwitchParameter accountneverexpires; /// @@ -77,8 +83,10 @@ public System.Management.Automation.SwitchParameter AccountNeverExpires public string Description { get { return this.description;} + set { this.description = value; } } + private string description; /// @@ -91,8 +99,10 @@ public string Description public string FullName { get { return this.fullname;} + set { this.fullname = value; } } + private string fullname; /// /// The following is the definition of the input parameter "InputObject". @@ -108,8 +118,10 @@ public string FullName public Microsoft.PowerShell.Commands.LocalUser InputObject { get { return this.inputobject;} + set { this.inputobject = value; } } + private Microsoft.PowerShell.Commands.LocalUser inputobject; /// @@ -125,8 +137,10 @@ public Microsoft.PowerShell.Commands.LocalUser InputObject public string Name { get { return this.name;} + set { this.name = value; } } + private string name; /// @@ -138,8 +152,10 @@ public string Name public System.Security.SecureString Password { get { return this.password;} + set { this.password = value; } } + private System.Security.SecureString password; /// @@ -147,12 +163,14 @@ public System.Security.SecureString Password /// Specifies that the password will not expire. /// [Parameter] - public System.Boolean PasswordNeverExpires + public bool PasswordNeverExpires { get { return this.passwordneverexpires; } + set { this.passwordneverexpires = value; } } - private System.Boolean passwordneverexpires; + + private bool passwordneverexpires; /// /// The following is the definition of the input parameter "SID". @@ -167,8 +185,10 @@ public System.Boolean PasswordNeverExpires public System.Security.Principal.SecurityIdentifier SID { get { return this.sid;} + set { this.sid = value; } } + private System.Security.Principal.SecurityIdentifier sid; /// @@ -177,15 +197,15 @@ public System.Security.Principal.SecurityIdentifier SID /// account. The default value is True. /// [Parameter] - public System.Boolean UserMayChangePassword + public bool UserMayChangePassword { get { return this.usermaychangepassword;} + set { this.usermaychangepassword = value; } } - private System.Boolean usermaychangepassword; - #endregion Parameter Properties - + private bool usermaychangepassword; + #endregion Parameter Properties #region Cmdlet Overrides /// @@ -198,10 +218,10 @@ protected override void BeginProcessing() InvalidParametersException ex = new InvalidParametersException("AccountExpires", "AccountNeverExpires"); ThrowTerminatingError(ex.MakeErrorRecord()); } + sam = new Sam(); } - /// /// ProcessRecord method. /// @@ -278,7 +298,6 @@ protected override void ProcessRecord() } } - /// /// EndProcessing method. /// @@ -298,7 +317,6 @@ private bool CheckShouldProcess(string target) return ShouldProcess(target, Strings.ActionSetUser); } #endregion Private Methods - }//End Class - -}//End namespace + } +} diff --git a/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/Exceptions.cs b/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/Exceptions.cs index a9f7193efe4..1c7a7630ea3 100644 --- a/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/Exceptions.cs +++ b/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/Exceptions.cs @@ -1,7 +1,11 @@ -using System; +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; using System.Management.Automation; using System.Management.Automation.SecurityAccountsManager; using System.Runtime.Serialization; + using Microsoft.PowerShell.LocalAccounts; namespace Microsoft.PowerShell.Commands @@ -61,23 +65,23 @@ internal LocalAccountsException(string message, object target, ErrorCategory err } /// - /// Compliance Constructor + /// Compliance Constructor. /// public LocalAccountsException() : base() { } /// - /// Compliance Constructor + /// Compliance Constructor. /// /// - public LocalAccountsException(String message) : base(message) { } + public LocalAccountsException(string message) : base(message) { } /// - /// Compliance Constructor + /// Compliance Constructor. /// /// /// - public LocalAccountsException(String message, Exception ex) : base(message, ex) { } + public LocalAccountsException(string message, Exception ex) : base(message, ex) { } /// - /// Compliance Constructor + /// Compliance Constructor. /// /// /// @@ -121,22 +125,22 @@ internal InternalException(UInt32 ntStatus, } /// - /// Compliance Constructor + /// Compliance Constructor. /// public InternalException() : base() { } /// - /// Compliance Constructor + /// Compliance Constructor. /// /// - public InternalException(String message) : base(message) { } + public InternalException(string message) : base(message) { } /// - /// Compliance Constructor + /// Compliance Constructor. /// /// /// - public InternalException(String message, Exception ex) : base(message, ex) { } + public InternalException(string message, Exception ex) : base(message, ex) { } /// - /// Compliance Constructor + /// Compliance Constructor. /// /// /// @@ -181,22 +185,22 @@ internal Win32InternalException(int errorCode, } /// - /// Compliance Constructor + /// Compliance Constructor. /// public Win32InternalException() : base() {} /// - /// Compliance Constructor + /// Compliance Constructor. /// /// - public Win32InternalException(String message) : base(message) { } + public Win32InternalException(string message) : base(message) { } /// - /// Compliance Constructor + /// Compliance Constructor. /// /// /// - public Win32InternalException(String message, Exception ex) : base(message, ex) { } + public Win32InternalException(string message, Exception ex) : base(message, ex) { } /// - /// Compliance Constructor + /// Compliance Constructor. /// /// /// @@ -235,13 +239,13 @@ public InvalidPasswordException(uint errorCode) } /// - /// Compliance Constructor + /// Compliance Constructor. /// /// /// - public InvalidPasswordException(String message, Exception ex) : base(message, ex) { } + public InvalidPasswordException(string message, Exception ex) : base(message, ex) { } /// - /// Compliance Constructor + /// Compliance Constructor. /// /// /// @@ -268,17 +272,17 @@ internal InvalidParametersException(string parameterA, string parameterB) } /// - /// Compliance Constructor + /// Compliance Constructor. /// public InvalidParametersException() : base() { } /// - /// Compliance Constructor + /// Compliance Constructor. /// /// /// - public InvalidParametersException(String message, Exception ex) : base(message, ex) { } + public InvalidParametersException(string message, Exception ex) : base(message, ex) { } /// - /// Compliance Constructor + /// Compliance Constructor. /// /// /// @@ -296,22 +300,22 @@ internal AccessDeniedException(object target) } /// - /// Compliance Constructor + /// Compliance Constructor. /// public AccessDeniedException() : base() { } /// - /// Compliance Constructor + /// Compliance Constructor. /// /// - public AccessDeniedException(String message) : base(message) { } + public AccessDeniedException(string message) : base(message) { } /// - /// Compliance Constructor + /// Compliance Constructor. /// /// /// - public AccessDeniedException(String message, Exception ex) : base(message, ex) { } + public AccessDeniedException(string message, Exception ex) : base(message, ex) { } /// - /// Compliance Constructor + /// Compliance Constructor. /// /// /// @@ -329,22 +333,22 @@ internal InvalidNameException(string name, object target) } /// - /// Compliance Constructor + /// Compliance Constructor. /// public InvalidNameException() : base() { } /// - /// Compliance Constructor + /// Compliance Constructor. /// /// - public InvalidNameException(String message) : base(message) { } + public InvalidNameException(string message) : base(message) { } /// - /// Compliance Constructor + /// Compliance Constructor. /// /// /// - public InvalidNameException(String message, Exception ex) : base(message, ex) { } + public InvalidNameException(string message, Exception ex) : base(message, ex) { } /// - /// Compliance Constructor + /// Compliance Constructor. /// /// /// @@ -362,22 +366,22 @@ internal NameInUseException(string name, object target) } /// - /// Compliance Constructor + /// Compliance Constructor. /// public NameInUseException() : base() { } /// - /// Compliance Constructor + /// Compliance Constructor. /// /// - public NameInUseException(String message) : base(message) { } + public NameInUseException(string message) : base(message) { } /// - /// Compliance Constructor + /// Compliance Constructor. /// /// /// - public NameInUseException(String message, Exception ex) : base(message, ex) { } + public NameInUseException(string message, Exception ex) : base(message, ex) { } /// - /// Compliance Constructor + /// Compliance Constructor. /// /// /// @@ -396,22 +400,22 @@ internal NotFoundException(string message, object target) } /// - /// Compliance Constructor + /// Compliance Constructor. /// public NotFoundException() : base() { } /// - /// Compliance Constructor + /// Compliance Constructor. /// /// - public NotFoundException(String message) : base(message) { } + public NotFoundException(string message) : base(message) { } /// - /// Compliance Constructor + /// Compliance Constructor. /// /// /// - public NotFoundException(String message, Exception ex) : base(message, ex) { } + public NotFoundException(string message, Exception ex) : base(message, ex) { } /// - /// Compliance Constructor + /// Compliance Constructor. /// /// /// @@ -419,7 +423,7 @@ protected NotFoundException(SerializationInfo info, StreamingContext ctx) : base } /// - /// Exception indicating that a principal was not Found + /// Exception indicating that a principal was not Found. /// public class PrincipalNotFoundException : NotFoundException { @@ -429,22 +433,22 @@ internal PrincipalNotFoundException(string principal, object target) } /// - /// Compliance Constructor + /// Compliance Constructor. /// public PrincipalNotFoundException() : base() { } /// - /// Compliance Constructor + /// Compliance Constructor. /// /// - public PrincipalNotFoundException(String message) : base(message) { } + public PrincipalNotFoundException(string message) : base(message) { } /// - /// Compliance Constructor + /// Compliance Constructor. /// /// /// - public PrincipalNotFoundException(String message, Exception ex) : base(message, ex) { } + public PrincipalNotFoundException(string message, Exception ex) : base(message, ex) { } /// - /// Compliance Constructor + /// Compliance Constructor. /// /// /// @@ -462,22 +466,22 @@ internal GroupNotFoundException(string group, object target) } /// - /// Compliance Constructor + /// Compliance Constructor. /// public GroupNotFoundException() : base() { } /// - /// Compliance Constructor + /// Compliance Constructor. /// /// - public GroupNotFoundException(String message) : base(message) { } + public GroupNotFoundException(string message) : base(message) { } /// - /// Compliance Constructor + /// Compliance Constructor. /// /// /// - public GroupNotFoundException(String message, Exception ex) : base(message, ex) { } + public GroupNotFoundException(string message, Exception ex) : base(message, ex) { } /// - /// Compliance Constructor + /// Compliance Constructor. /// /// /// @@ -495,22 +499,22 @@ internal UserNotFoundException(string user, object target) } /// - /// Compliance Constructor + /// Compliance Constructor. /// public UserNotFoundException() : base() { } /// - /// Compliance Constructor + /// Compliance Constructor. /// /// - public UserNotFoundException(String message) : base(message) { } + public UserNotFoundException(string message) : base(message) { } /// - /// Compliance Constructor + /// Compliance Constructor. /// /// /// - public UserNotFoundException(String message, Exception ex) : base(message, ex) { } + public UserNotFoundException(string message, Exception ex) : base(message, ex) { } /// - /// Compliance Constructor + /// Compliance Constructor. /// /// /// @@ -528,22 +532,22 @@ internal MemberNotFoundException(string member, string group) } /// - /// Compliance Constructor + /// Compliance Constructor. /// public MemberNotFoundException() : base() { } /// - /// Compliance Constructor + /// Compliance Constructor. /// /// - public MemberNotFoundException(String message) : base(message) { } + public MemberNotFoundException(string message) : base(message) { } /// - /// Compliance Constructor + /// Compliance Constructor. /// /// /// - public MemberNotFoundException(String message, Exception ex) : base(message, ex) { } + public MemberNotFoundException(string message, Exception ex) : base(message, ex) { } /// - /// Compliance Constructor + /// Compliance Constructor. /// /// /// @@ -562,22 +566,22 @@ internal ObjectExistsException(string message, object target) } /// - /// Compliance Constructor + /// Compliance Constructor. /// public ObjectExistsException() : base() { } /// - /// Compliance Constructor + /// Compliance Constructor. /// /// - public ObjectExistsException(String message) : base(message) { } + public ObjectExistsException(string message) : base(message) { } /// - /// Compliance Constructor + /// Compliance Constructor. /// /// /// - public ObjectExistsException(String message, Exception ex) : base(message, ex) { } + public ObjectExistsException(string message, Exception ex) : base(message, ex) { } /// - /// Compliance Constructor + /// Compliance Constructor. /// /// /// @@ -595,22 +599,22 @@ internal GroupExistsException(string group, object target) } /// - /// Compliance Constructor + /// Compliance Constructor. /// public GroupExistsException() : base() { } /// - /// Compliance Constructor + /// Compliance Constructor. /// /// - public GroupExistsException(String message) : base(message) { } + public GroupExistsException(string message) : base(message) { } /// - /// Compliance Constructor + /// Compliance Constructor. /// /// /// - public GroupExistsException(String message, Exception ex) : base(message, ex) { } + public GroupExistsException(string message, Exception ex) : base(message, ex) { } /// - /// Compliance Constructor + /// Compliance Constructor. /// /// /// @@ -628,22 +632,22 @@ internal UserExistsException(string user, object target) } /// - /// Compliance Constructor + /// Compliance Constructor. /// public UserExistsException() : base() { } /// - /// Compliance Constructor + /// Compliance Constructor. /// /// - public UserExistsException(String message) : base(message) { } + public UserExistsException(string message) : base(message) { } /// - /// Compliance Constructor + /// Compliance Constructor. /// /// /// - public UserExistsException(String message, Exception ex) : base(message, ex) { } + public UserExistsException(string message, Exception ex) : base(message, ex) { } /// - /// Compliance Constructor + /// Compliance Constructor. /// /// /// @@ -661,22 +665,22 @@ internal MemberExistsException(string member, string group, object target) } /// - /// Compliance Constructor + /// Compliance Constructor. /// public MemberExistsException() : base() { } /// - /// Compliance Constructor + /// Compliance Constructor. /// /// - public MemberExistsException(String message) : base(message) { } + public MemberExistsException(string message) : base(message) { } /// - /// Compliance Constructor + /// Compliance Constructor. /// /// /// - public MemberExistsException(String message, Exception ex) : base(message, ex) { } + public MemberExistsException(string message, Exception ex) : base(message, ex) { } /// - /// Compliance Constructor + /// Compliance Constructor. /// /// /// diff --git a/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/Extensions.cs b/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/Extensions.cs index 86ef63e3ef7..007966cb0a8 100644 --- a/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/Extensions.cs +++ b/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/Extensions.cs @@ -1,4 +1,7 @@ -using System.Runtime.InteropServices; +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.Runtime.InteropServices; using System.Security; using System.Security.Principal; using System.Text.RegularExpressions; @@ -9,7 +12,7 @@ namespace System.Management.Automation.SecurityAccountsManager.Extensions { /// - /// Provides extension methods for the Cmdlet class + /// Provides extension methods for the Cmdlet class. /// internal static class CmdletExtensions { @@ -30,7 +33,7 @@ internal static SecurityIdentifier TrySid(this Cmdlet cmdlet, bool allowSidConstants = false) { if (!allowSidConstants) - if (!(s.Length > 2 && s.StartsWith("S-", StringComparison.Ordinal) && Char.IsDigit(s[2]))) + if (!(s.Length > 2 && s.StartsWith("S-", StringComparison.Ordinal) && char.IsDigit(s[2]))) return null; SecurityIdentifier sid = null; @@ -49,12 +52,12 @@ internal static SecurityIdentifier TrySid(this Cmdlet cmdlet, } /// - /// Provides extension methods for the PSCmdlet class + /// Provides extension methods for the PSCmdlet class. /// internal static class PSExtensions { /// - /// Determine if a given parameter was provided to the cmdlet + /// Determine if a given parameter was provided to the cmdlet. /// /// /// The object to check. diff --git a/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/LocalGroup.cs b/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/LocalGroup.cs index 170493522fb..b43904fb773 100644 --- a/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/LocalGroup.cs +++ b/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/LocalGroup.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; using Microsoft.PowerShell.LocalAccounts; @@ -14,7 +17,7 @@ public class LocalGroup : LocalPrincipal /// /// A short description of the Group. /// - public String Description { get; set; } + public string Description { get; set; } #endregion Public Properties #region Construction @@ -37,7 +40,7 @@ public LocalGroup(string name) } /// - /// Construct a new LocalGroup object that is a copy of another + /// Construct a new LocalGroup object that is a copy of another. /// /// private LocalGroup(LocalGroup other) @@ -47,7 +50,6 @@ private LocalGroup(LocalGroup other) } #endregion Construction - #region Public Methods /// /// Provides a string representation of the LocalGroup object. @@ -68,10 +70,6 @@ public override string ToString() /// public LocalGroup Clone() { - if (null == this) - { - throw new NullReferenceException(); - } return new LocalGroup(this); } #endregion Public Methods diff --git a/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/LocalPrincipal.cs b/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/LocalPrincipal.cs index 94e5972d265..dcfec24631b 100644 --- a/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/LocalPrincipal.cs +++ b/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/LocalPrincipal.cs @@ -1,14 +1,17 @@ -using System.Security.Principal; +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.Security.Principal; namespace Microsoft.PowerShell.Commands { /// - /// Defines the source of a Principal + /// Defines the source of a Principal. /// public enum PrincipalSource { /// - /// The principal source is unknown or could not be determined + /// The principal source is unknown or could not be determined. /// Unknown = 0, diff --git a/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/LocalUser.cs b/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/LocalUser.cs index fb27679feac..9cad9777cac 100644 --- a/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/LocalUser.cs +++ b/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/LocalUser.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; using Microsoft.PowerShell.LocalAccounts; @@ -52,7 +55,6 @@ public class LocalUser : LocalPrincipal /// public bool UserMayChangePassword { get; set; } - /// /// Indicates whether the user must have a password (true) or not (false). /// @@ -69,7 +71,6 @@ public class LocalUser : LocalPrincipal public DateTime? LastLogon { get; set; } #endregion Public Properties - #region Construction /// /// Initializes a new LocalUser object. @@ -134,10 +135,6 @@ public override string ToString() /// public LocalUser Clone() { - if (null == this) - { - throw new NullReferenceException(); - } return new LocalUser(this); } #endregion Public Methods diff --git a/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/Native.cs b/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/Native.cs index 4a085d7867f..c34dbcd64d8 100644 --- a/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/Native.cs +++ b/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/Native.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; using System.Runtime.InteropServices; using System.Text; @@ -123,7 +126,7 @@ internal struct UNICODE_STRING public UNICODE_STRING(string s) { - buffer = String.IsNullOrEmpty(s) ? String.Empty : s; + buffer = string.IsNullOrEmpty(s) ? string.Empty : s; Length = (UInt16)(2 * buffer.Length); MaximumLength = Length; } @@ -134,7 +137,7 @@ public override string ToString() // often have buffers that point to junk if Length = 0, or that // point to non-null-terminated strings, resulting in marshaled // String objects that have more characters than they should. - return Length == 0 ? String.Empty + return Length == 0 ? string.Empty : buffer.Substring(0, Length / 2); } } @@ -162,7 +165,6 @@ public void Dispose() } } - // These structures are filled in by Marshalling, so fields will be initialized // invisibly to the C# compiler, and some fields will not be used in C# code. #pragma warning disable 0649, 0169 @@ -195,7 +197,6 @@ internal static class Win32 internal const UInt32 STANDARD_RIGHTS_WRITE = READ_CONTROL; internal const UInt32 STANDARD_RIGHTS_EXECUTE = READ_CONTROL; - internal const UInt32 STANDARD_RIGHTS_ALL = 0x001F0000; internal const UInt32 SPECIFIC_RIGHTS_ALL = 0x0000FFFF; @@ -209,7 +210,6 @@ internal static class Win32 internal const UInt32 GENERIC_EXECUTE = 0x20000000; internal const UInt32 GENERIC_ALL = 0x10000000; - // These constants control the behavior of the FormatMessage Windows API function internal const uint FORMAT_MESSAGE_ALLOCATE_BUFFER = 0x00000100; internal const uint FORMAT_MESSAGE_IGNORE_INSERTS = 0x00000200; @@ -335,7 +335,6 @@ internal static class Win32 internal const int NERR_LastAdmin = NERR_BASE + 352; // This operation is not allowed on the last administrative account. #endregion Win32 Error Codes - #region SECURITY_DESCRIPTOR Control Flags internal const UInt16 SE_DACL_PRESENT = 0x0004; internal const UInt16 SE_SELF_RELATIVE = 0x8000; @@ -348,6 +347,7 @@ internal static class Win32 #region Win32 Functions [DllImport(PInvokeDllNames.LookupAccountSidDllName, CharSet = CharSet.Unicode, SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] internal static extern bool LookupAccountSid(string systemName, byte[] accountSid, StringBuilder accountName, @@ -357,6 +357,7 @@ internal static extern bool LookupAccountSid(string systemName, out SID_NAME_USE use); [DllImport(PInvokeDllNames.LookupAccountNameDllName, CharSet = CharSet.Unicode, SetLastError = true)] + [return: MarshalAs(UnmanagedType.Bool)] internal static extern bool LookupAccountName(string systemName, string accountName, [MarshalAs(UnmanagedType.LPArray)] @@ -366,7 +367,6 @@ internal static extern bool LookupAccountName(string systemName, ref uint domainNameLength, out SID_NAME_USE peUse); - [DllImport(PInvokeDllNames.GetSecurityDescriptorDaclDllName, SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] internal static extern bool GetSecurityDescriptorDacl(IntPtr pSecurityDescriptor, diff --git a/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/NtStatus.cs b/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/NtStatus.cs index 59e00f0b252..654d221a69b 100644 --- a/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/NtStatus.cs +++ b/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/NtStatus.cs @@ -1,4 +1,6 @@ - +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + using System.Diagnostics.CodeAnalysis; namespace System.Management.Automation.SecurityAccountsManager.Native @@ -18,7 +20,6 @@ internal static class NtStatus public const UInt32 STATUS_SEVERITY_INFORMATIONAL = 0x1; public const UInt32 STATUS_SEVERITY_ERROR = 0x3; - public const UInt32 STATUS_SUCCESS = 0x00000000; // // MessageText: @@ -28,11 +29,6 @@ internal static class NtStatus public const UInt32 STATUS_MORE_ENTRIES = 0x00000105; - - - - - ///////////////////////////////////////////////////////////////////////// // // Standard Information values @@ -430,7 +426,7 @@ internal static class NtStatus #region Public Methods /// - /// Determine if an NTSTATUS value indicates Success + /// Determine if an NTSTATUS value indicates Success. /// /// The NTSTATUS value returned from native functions. /// @@ -442,7 +438,7 @@ public static bool IsSuccess(UInt32 ntstatus) } /// - /// Determine if an NTSTATUS value indicates an Error + /// Determine if an NTSTATUS value indicates an Error. /// /// The NTSTATUS value returned from native functions. /// @@ -453,9 +449,8 @@ public static bool IsError(UInt32 ntstatus) return Severity(ntstatus) == STATUS_SEVERITY_ERROR; } - /// - /// Determine if an NTSTATUS value indicates a Warning + /// Determine if an NTSTATUS value indicates a Warning. /// /// The NTSTATUS value returned from native functions. /// @@ -480,9 +475,8 @@ public static bool IsInformational(UInt32 ntstatus) return Severity(ntstatus) == STATUS_SEVERITY_INFORMATIONAL; } - /// - /// Return the Severity part of an NTSTATUS value + /// Return the Severity part of an NTSTATUS value. /// /// The NTSTATUS value returned from native functions. /// @@ -493,9 +487,8 @@ public static uint Severity(UInt32 ntstatus) return ntstatus >> 30; } - /// - /// Return the Facility part of an NSTATUS value + /// Return the Facility part of an NSTATUS value. /// /// The NTSTATUS value returned from native functions. /// @@ -508,9 +501,8 @@ public static uint Facility(UInt32 ntstatus) return (ntstatus >> 16) & 0x0FFF; } - /// - /// Return the Code part of an NTSTATUS value + /// Return the Code part of an NTSTATUS value. /// /// The NTSTATUS value returned from native functions. /// diff --git a/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/PInvokeDllNames.cs b/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/PInvokeDllNames.cs index cdc55996036..68a7d31e833 100644 --- a/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/PInvokeDllNames.cs +++ b/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/PInvokeDllNames.cs @@ -1,6 +1,5 @@ -// -// Copyright (C) Microsoft. All rights reserved. -// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. namespace System.Management.Automation { diff --git a/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/Sam.cs b/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/Sam.cs index b74184e1685..3e6bbcafd10 100644 --- a/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/Sam.cs +++ b/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/Sam.cs @@ -1,8 +1,11 @@ -using System.Collections.Generic; +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.Collections.Generic; +using System.ComponentModel; using System.Runtime.InteropServices; using System.Security.AccessControl; using System.Security.Principal; -using System.ComponentModel; using Microsoft.PowerShell.Commands; using System.Management.Automation.SecurityAccountsManager.Extensions; @@ -35,7 +38,7 @@ internal enum Enabling internal class SamRidEnumeration { #region Original struct members - public String Name; + public string Name; public UInt32 RelativeId; #endregion Original struct members @@ -44,7 +47,6 @@ internal class SamRidEnumeration #endregion Additional members } - /// /// Provides methods for manipulating local Users and Groups. /// @@ -176,7 +178,6 @@ internal enum DomainAccess : uint Max = Win32.MAXIMUM_ALLOWED } - /// /// The operation under way. Used in the class. /// @@ -213,11 +214,11 @@ private enum ContextObjectType /// Used primarily by the private ThrowOnFailure method when building /// Exception objects to throw. /// - private class Context + private sealed class Context { public ContextOperation operation; public ContextObjectType type; - public Object target; + public object target; public string objectId; public string memberId; @@ -257,7 +258,7 @@ public Context(ContextOperation operation, } /// - /// Default constructor + /// Default constructor. /// public Context() { @@ -307,7 +308,7 @@ public string MemberName /// AccountInfo is the return type from the private /// LookupAccountInfo method. /// - private class AccountInfo + private sealed class AccountInfo { public string AccountName; public string DomainName; @@ -368,7 +369,7 @@ public override string ToString() private IntPtr localDomainHandle = IntPtr.Zero; private IntPtr builtinDomainHandle = IntPtr.Zero; private Context context = null; - private string machineName = String.Empty; + private string machineName = string.Empty; #endregion Instance Data #region Construction @@ -394,7 +395,7 @@ public string StripMachineName(string name) } #region Local Groups /// - /// Retrieve a named local group + /// Retrieve a named local group. /// /// Name of the desired local group. /// @@ -416,7 +417,7 @@ internal LocalGroup GetLocalGroup(string groupName) } /// - /// Retrieve a local group by SID + /// Retrieve a local group by SID. /// /// /// A object identifying the desired group. @@ -461,7 +462,7 @@ internal LocalGroup CreateLocalGroup(LocalGroup group) } /// - /// Update a local group with new property values + /// Update a local group with new property values. /// /// /// A object representing the group to be updated. @@ -745,7 +746,7 @@ internal Exception RemoveLocalGroupMember(SecurityIdentifier groupSid, LocalPrin #region Local Users /// - /// Retrieve a named local user + /// Retrieve a named local user. /// /// Name of the desired local user. /// @@ -767,7 +768,7 @@ internal LocalUser GetLocalUser(string userName) } /// - /// Retrieve a local user by SID + /// Retrieve a local user by SID. /// /// /// A object identifying the desired user. @@ -791,7 +792,7 @@ internal LocalUser GetLocalUser(SecurityIdentifier sid) } /// - /// Create a local user + /// Create a local user. /// /// A object containing /// information about the local user to be created. @@ -855,7 +856,6 @@ internal void RemoveLocalUser(LocalUser user) RemoveUser(user.SID); } - /// /// Rename a local user. /// @@ -900,7 +900,7 @@ internal void RenameLocalUser(LocalUser user, string newName) } /// - /// Enable or disable a Local User + /// Enable or disable a Local User. /// /// /// A object identifying the user to enable or disable. @@ -920,7 +920,7 @@ internal void EnableLocalUser(SecurityIdentifier sid, Enabling enable) } /// - /// Enable or disable a Local User + /// Enable or disable a Local User. /// /// /// A object representing the user to enable or disable. @@ -1277,6 +1277,7 @@ private LocalUser CreateUser(LocalUser userInfo, System.Security.SecureString pa { SamApi.SamDeleteUser(userHandle); } + throw; } finally @@ -1289,7 +1290,7 @@ private LocalUser CreateUser(LocalUser userInfo, System.Security.SecureString pa } /// - /// Remove a group identified by SID + /// Remove a group identified by SID. /// /// /// A object identifying the @@ -1374,6 +1375,7 @@ private void RenameGroup(SecurityIdentifier sid, string newName) Marshal.DestroyStructure(buffer); Marshal.FreeHGlobal(buffer); } + if (aliasHandle != IntPtr.Zero) status = SamApi.SamCloseHandle(aliasHandle); } @@ -1617,12 +1619,12 @@ private LocalUser MakeLocalUserObject(SamRidEnumeration sre, IntPtr userHandle) FullName = allInfo.FullName.ToString(), Description = allInfo.AdminComment.ToString(), - //TODO: why is this coming up as 864000000000 (number of ticks per day)? + // TODO: why is this coming up as 864000000000 (number of ticks per day)? PasswordChangeableDate = DateTimeFromSam(allInfo.PasswordCanChange.QuadPart), PasswordExpires = DateTimeFromSam(allInfo.PasswordMustChange.QuadPart), - //TODO: why is this coming up as 0X7FFFFFFFFFFFFFFF (largest signed 64-bit, and well out of range of DateTime)? + // TODO: why is this coming up as 0X7FFFFFFFFFFFFFFF (largest signed 64-bit, and well out of range of DateTime)? AccountExpires = DateTimeFromSam(allInfo.AccountExpires.QuadPart), LastLogon = DateTimeFromSam(allInfo.LastLogon.QuadPart), PasswordLastSet = DateTimeFromSam(allInfo.PasswordLastSet.QuadPart), @@ -1763,6 +1765,7 @@ private void RenameUser(SecurityIdentifier sid, string newName) Marshal.DestroyStructure(buffer); Marshal.FreeHGlobal(buffer); } + if (userHandle != IntPtr.Zero) status = SamApi.SamCloseHandle(userHandle); } @@ -1998,6 +2001,7 @@ private void UpdateGroup(LocalGroup group, LocalGroup changed) Marshal.DestroyStructure(buffer); Marshal.FreeHGlobal(buffer); } + if (aliasHandle != IntPtr.Zero) status = SamApi.SamCloseHandle(aliasHandle); } @@ -2200,11 +2204,13 @@ private void SetUserData(IntPtr userHandle, ? sourceUser.AccountExpires.Value.ToFileTime() : 0L; } + if (setFlags.HasFlag(UserProperties.Description)) { which |= SamApi.USER_ALL_ADMINCOMMENT; info.AdminComment = new UNICODE_STRING(sourceUser.Description); } + if (setFlags.HasFlag(UserProperties.Enabled)) { which |= SamApi.USER_ALL_USERACCOUNTCONTROL; @@ -2213,6 +2219,7 @@ private void SetUserData(IntPtr userHandle, else uac |= SamApi.USER_ACCOUNT_DISABLED; } + if (setFlags.HasFlag(UserProperties.FullName)) { which |= SamApi.USER_ALL_FULLNAME; @@ -2365,6 +2372,7 @@ private RawAcl GetSamDacl(IntPtr objectHandle) if (IntPtr.Zero != securityObject) status = SamApi.SamFreeMemory(securityObject); } + return rv; } @@ -2657,6 +2665,7 @@ private SecurityIdentifier RidToSid(IntPtr domainHandle, uint rid) if (IntPtr.Zero != sidBytes) status = SamApi.SamFreeMemory(sidBytes); } + return sid; } @@ -2769,7 +2778,7 @@ private AccountInfo LookupAccountInfo(string accountName) { // Bug: 7407413 : // If accountname is in the format domain1\user1, - //then AccountName.ToString() will return domain1\domain1\user1 + // then AccountName.ToString() will return domain1\domain1\user1 // Ideally , accountname should be processed to hold only account name (without domain) // as we are keeping the domain in 'DomainName' variable. @@ -2778,6 +2787,7 @@ private AccountInfo LookupAccountInfo(string accountName) { accountName = accountName.Substring(index + 1); } + return new AccountInfo { AccountName = accountName, @@ -2818,7 +2828,7 @@ private LocalPrincipal MakeLocalPrincipalObject(AccountInfo info) switch (info.Use) { - case SID_NAME_USE.SidTypeAlias: //TODO: is this the right thing to do??? + case SID_NAME_USE.SidTypeAlias: // TODO: is this the right thing to do??? case SID_NAME_USE.SidTypeGroup: case SID_NAME_USE.SidTypeWellKnownGroup: rv.ObjectClass = Strings.ObjectClassGroup; @@ -2938,7 +2948,7 @@ private Exception MakeException(UInt32 ntStatus, Context context = null) return new UserNotFoundException(context.ObjectName, context.target); case NtStatus.STATUS_SPECIAL_GROUP: // The group specified is a special group and cannot be operated on in the requested fashion. - //case NtStatus.STATUS_SPECIAL_ALIAS: // referred to in source for SAM api, but not in ntstatus.h!!! + // case NtStatus.STATUS_SPECIAL_ALIAS: // referred to in source for SAM api, but not in ntstatus.h!!! return new InvalidOperationException(StringUtil.Format(Strings.InvalidForGroup, context.ObjectName)); @@ -2974,7 +2984,7 @@ private Exception MakeException(UInt32 ntStatus, Context context = null) case NtStatus.STATUS_PASSWORD_RESTRICTION: return new InvalidPasswordException(Native.Win32.RtlNtStatusToDosError(ntStatus)); - //TODO: do we want to handle these? + // TODO: do we want to handle these? // they appear to be returned only in functions we are not calling case NtStatus.STATUS_INVALID_SID: // member sid is corrupted case NtStatus.STATUS_INVALID_MEMBER: // member has wrong account type @@ -3125,7 +3135,7 @@ internal struct OSVERSIONINFOEX private static volatile OperatingSystem localOs; /// - /// It only contains the properties that get used in powershell + /// It only contains the properties that get used in powershell. /// internal sealed class OperatingSystem { @@ -3135,15 +3145,14 @@ internal sealed class OperatingSystem internal OperatingSystem(Version version, string servicePack) { - if (version == null) - throw new ArgumentNullException("version"); + ArgumentNullException.ThrowIfNull(version); _version = version; _servicePack = servicePack; } /// - /// OS version + /// OS version. /// public Version Version { @@ -3151,7 +3160,7 @@ public Version Version } /// - /// VersionString + /// VersionString. /// public string VersionString { @@ -3196,6 +3205,7 @@ private OperatingSystem GetOperatingSystem() Version ver = new Version(osviex.MajorVersion, osviex.MinorVersion, osviex.BuildNumber, (osviex.ServicePackMajor << 16) | osviex.ServicePackMinor); localOs = new OperatingSystem(ver, osviex.CSDVersion); } + return localOs; #else return Environment.OSVersion; diff --git a/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/SamApi.cs b/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/SamApi.cs index e0c686ea7c7..c2d9bc7f95b 100644 --- a/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/SamApi.cs +++ b/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/SamApi.cs @@ -1,4 +1,7 @@ -using System; +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System; using System.Collections.Generic; using System.Runtime.InteropServices; @@ -193,7 +196,7 @@ internal struct USER_ADMIN_COMMENT_INFORMATION [StructLayout(LayoutKind.Sequential)] internal struct USER_EXPIRES_INFORMATION { - //LARGE_INTEGER AccountExpires; + // LARGE_INTEGER AccountExpires; public Int64 AccountExpires; } @@ -210,7 +213,6 @@ internal struct USER_LOGON_HOURS_INFORMATION public LOGON_HOURS LogonHours; } - [StructLayout(LayoutKind.Sequential)] internal struct POLICY_PRIMARY_DOMAIN_INFO { @@ -273,7 +275,6 @@ internal static class SamApi internal const UInt32 SAM_USER_ENUMERATION_FILTER_INTERNET = 0x00000002; internal const UInt32 SAM_SERVER_LOOKUP_DOMAIN = 0x0020; - // // Bits to be used in UserAllInformation's WhichFields field (to indicate // which items were queried or set). @@ -311,7 +312,6 @@ internal static class SamApi internal const UInt32 USER_ALL_UNDEFINED_MASK = 0xC0000000; - // // Bit masks for the UserAccountControl member of the USER_ALL_INFORMATION structure // @@ -351,7 +351,6 @@ public static extern UInt32 SamConnect(ref UNICODE_STRING serverName, UInt32 desiredAccess, ref OBJECT_ATTRIBUTES objectAttributes); - [DllImport("samlib.dll")] internal static extern UInt32 SamRidToSid(IntPtr objectHandle, UInt32 rid, out IntPtr sid); @@ -444,7 +443,6 @@ internal static extern UInt32 SamCreateUser2InDomain(IntPtr domainHandle, out UInt32 grantedAccess, out UInt32 relativeId); - [DllImport("samlib.dll")] internal static extern UInt32 SamQueryInformationUser(IntPtr userHandle, USER_INFORMATION_CLASS userInformationClass, diff --git a/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/StringUtil.cs b/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/StringUtil.cs index afc20f12612..25bbeb49329 100644 --- a/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/StringUtil.cs +++ b/src/Microsoft.PowerShell.LocalAccounts/LocalAccounts/StringUtil.cs @@ -1,15 +1,18 @@ -using System.Globalization; +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + +using System.Globalization; using System.Management.Automation.SecurityAccountsManager.Native; namespace System.Management.Automation.SecurityAccountsManager { /// - /// Contains utility functions for formatting localizable strings + /// Contains utility functions for formatting localizable strings. /// internal class StringUtil { /// - /// Private constructor to precent auto-generation of a default constructor with greater accessability. + /// Private constructor to present auto-generation of a default constructor with greater accessibility. /// private StringUtil() { @@ -35,6 +38,7 @@ internal static string Format(string fmt, uint p0) { return string.Format(CultureInfo.CurrentCulture, fmt, p0); } + internal static string Format(string fmt, int p0) { return string.Format(CultureInfo.CurrentCulture, fmt, p0); diff --git a/src/Microsoft.PowerShell.LocalAccounts/Microsoft.PowerShell.LocalAccounts.csproj b/src/Microsoft.PowerShell.LocalAccounts/Microsoft.PowerShell.LocalAccounts.csproj index ebd69091ebe..a85b06d4f90 100644 --- a/src/Microsoft.PowerShell.LocalAccounts/Microsoft.PowerShell.LocalAccounts.csproj +++ b/src/Microsoft.PowerShell.LocalAccounts/Microsoft.PowerShell.LocalAccounts.csproj @@ -1,9 +1,9 @@ - + - PowerShell Core's Microsoft.PowerShell.LocalAccounts project + PowerShell's Microsoft.PowerShell.LocalAccounts project Microsoft.PowerShell.LocalAccounts @@ -11,20 +11,4 @@ - - $(DefineConstants);CORECLR - - - - portable - - - - $(DefineConstants);UNIX - - - - full - - diff --git a/src/Microsoft.PowerShell.LocalAccounts/resources/Microsoft.PowerShell.LocalAccounts.Strings.resx b/src/Microsoft.PowerShell.LocalAccounts/resources/Microsoft.PowerShell.LocalAccounts.Strings.resx index 0c6fa1de8f7..ff1c4a17427 100644 --- a/src/Microsoft.PowerShell.LocalAccounts/resources/Microsoft.PowerShell.LocalAccounts.Strings.resx +++ b/src/Microsoft.PowerShell.LocalAccounts/resources/Microsoft.PowerShell.LocalAccounts.Strings.resx @@ -1,4 +1,4 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - Accept the input or move to the next line if input is missing a closing token. - - - Move the cursor to the next line without attempting to execute the input - - - Move the cursor back one character - - - Delete the character before the cursor - - - Delete text from the cursor to the start of the line - - - Move the text from the cursor to the beginning of the line to the kill ring - - - Move to the first item in the history - - - Move the cursor to the beginning of the line - - - Abort editing the current line and re-evaluate the prompt - - - Remove all items from the kill ring - - - Remove all items from the command line history (not PowerShell history) - - - Complete the input if there is a single completion, otherwise complete the input with common prefix for all completions. Show possible completions if pressed a second time. - - - Delete the character under the cursor - - - Move the cursor to the beginning of the current or previous token or start of the line - - - String is not used in the UI - - - String is not used in the UI - - - Move to the last item (the current input) in the history - - - Move the cursor to the end of the line - - - Mark the location of the cursor and move the cursor to the position of the previous mark - - - Move the cursor forward one character - - - Delete text from the cursor to the end of the line - - - Move the cursor to the beginning of the next token or end of line - - - Search for the previous item in the history that starts with the current input - like PreviousHistory if the input is empty - - - Search for the next item in the history that starts with the current input - like NextHistory if the input is empty - - - String is not used in the UI - - - Move the text from the cursor to the start of the current or previous token to the kill ring - - - Move the text from the cursor to the end of the input to the kill ring - - - Move the text from the cursor to the end of the current or next token to the kill ring - - - Replace the input with the next item in the history - - - Paste text from the system clipboard - - - Display the possible completions without changing the input - - - Replace the input with the previous item in the history - - - Redo an undo - - - Equivalent to undo all edits (clears the line except lines imported from history) - - - Mark the location of the cursor - - - Complete the input using the next completion - - - Complete the input using the previous completion - - - Undo a previous edit - - - Copy the text from the current kill ring position to the input - - - Replace the previously yanked text with the text from the next kill ring position - - - 'start' cannot be less than zero or greater than the length of the buffer - - - length is too big - - - Display all {0} possibilities? (y or n) _ - - - Clear the screen and redraw the current line at the top of the screen - - - Go to matching brace - - - Abort the current operation, e.g. incremental history search - - - Search history forward interactively - - - Search history backwards interactively - - - Move the text from the start of the current or previous word to the cursor to the kill ring - - - Move the cursor to the beginning of the current or previous word - - - Move the cursor forward to the end of the current word, or if between words, to the end of the next word. - - - Move the text from the cursor to the end of the current or next word to the kill ring - - - Move the cursor forward to the start of the next word - - - Move the text from the cursor to the start of the current or previous whitespace delimited word to the kill ring - - - Read a character and move the cursor to the previous occurence of that character - - - Read a character and move the cursor to the next occurence of that character - - - Start or accumulate a numeric argument to other functions - - - Copy the text of the last argument to the input - - - Copy the text of the first argument to the input - - - Accept the current line and recall the next line from history after the current line finishes executing - - - Key is unbound - - - Insert the key typed - - - Show all key bindings - - - Show the key binding for the next chord entered - - - Copy selected region to the system clipboard. If no region is selected, copy the whole line - - - Delete selected region placing deleted text in the system clipboard - - - Kill the text between the cursor and the mark - - - Adjust the current selection to include the previous character - - - Adjust the current selection to include the previous word - - - Adjust the current selection to include the next character - - - Adjust the current selection to include the next word using ForwardWord - - - Adjust the current selection to include the next word - - - Adjust the current selection to include the previous word using ShellBackwardWord - - - Adjust the current selection to include the next word using ShellForwardWord - - - Allows you to select multiple lines from the console using Shift+UpArrow/DownArrow and copy the selected lines to clipboard by pressing Enter. - - - Erases the current prompt and calls the prompt function to redisplay the prompt - - - Scroll the display down one screen - - - Scroll the display down one line - - - Scroll the display to the cursor - - - Scroll the display to the top - - - Scroll the display up one screen - - - Scroll the display up one line - - - Adjust the current selection to include the next word using ShellNextWord - - - Move the cursor to the end of the current token - - - Adjust the current selection to include from the cursor to the end of the line - - - Adjust the current selection to include from the cursor to the start of the line - - - Select the entire line. Moves the cursor to the end of the line - - - Either copy selected text to the clipboard, or if no text is selected, cancel editing the line with CancelLine. - - - Complete the input if there is a single completion, otherwise complete the input by selecting from a menu of possible completions. - - - -Oops, something went wrong. Please report this bug with the details below. -Report on GitHub: https://github.com/lzybkr/PSReadLine/issues/new - - - ----------------------------------------------------------------------- -Last {0} Keys: -{1} - -Exception: -{2} ------------------------------------------------------------------------ - - - Move the cursor to the next line if the input has multiple lines. - - - Move the cursor to the previous line if the input has multiple lines. - - - Command '{0}' cannot be found. - - - Accept the input or move to the next line if input is missing a closing token. -If there are other parse errors, unresolved commands, or incorrect parameters, show the error and continue editing. - - - Delete the character under the cursor, or if the line is empty, exit the process. - - - Unable to translate '{0}' to virtual key code: {1}. - - - Chord can have at most two keys. - - - Duplicate or invalid modifier token '{0}' for key '{1}'. - - - Invalid sequence '{0}'. - - - Unrecognized key '{0}'. Please use a character literal or a well-known key name from the System.ConsoleKey enumeration. - - - Accept the line and switch to Vi's insert mode. - - - Delete the previous word in the line. - - - Switch to VI's command mode. - - - Move to the end of the line. - - - Switch to insert mode, appending at the current line position. - - - Swap the current character with the character before it. - - - Repeats the last search. - - - Searches backward for the prescribed character. - - - Searches for the prescribed character in the prescribed direction. - - - Switches to insert mode after positioning the cursor past the end of the line. - - - Deletes all of the line except for leading whitespace. - - - Replaces the character in front of the cursor. - - - Replaces the line left of the cursor and all of the way to the beginning. - - - Replaces the line left of the cursor and all but one character to the beginning of the line. - - - Inserts the entered character at the beginning and accepts the line. - - - Deletes all characters between the cursor and the matching brace. - - - Deletes the current line. - - - Deletes from the cursor to the end of the line. - - - Deletes from the cursor to the end of the current word. - - - Deletes the current word. - - - Positions the cursor at the first non-blank character. - - - Moves the cursor forward to the end of the next word. - - - Moves the cursor to the prescribed column. - - - Switches to insert mode. - - - Moves the cursor to the beginning of the line and switches to insert mode. - - - Moves the cursor to the end of the line and switches to insert mode. - - - Deletes the current character and switches to insert mode. - - - Inverts the case of the current character and advances the cursor. - - - Repeats the last modification command. - - - Repeat the last search, but in the opposite direction. - - - Repeat the last search. - - - Replace all characters between the current brace character and it's matching partner. - - - Replace the current character with the next set of characters typed. - - - Replace the current character with only one character. - - - Repace the current line with the next set of characters typed. - - - Replace the characters from the cursor position to the end of the line. - - - Replace the current character until an escape is entered or the line is accepted. - - - Replace the current word. - - - Delete the current character and insert the next character. - - - Starts a new search backward in the history. - - - Transposes the current character with the next character in the line. - - - Undoes all commands for this line. - - - Prompts for a search string and initiates a search upon AcceptLine. - - - Repeat the last recorded character search in the opposite direction. - - - Repeat the last recorded character search. - - - Move to the previous occurrence of the specified character. - - - Move to the previous occurrence of the specified character and then forward one character. - - - Move to the next occurrence of the specified character. - - - Move to he next occurrence of the specified character and then back one character. - - - Replace the previous word. - - - Delete backward to the beginning of the previous word, as delimited by white space and common delimiters, and enter insert mode. - - - Write the contents of the local clipboard after the cursor. - - - Move the cursor to the beginning of the next word, as delimited by white space and common delimiters. - - - Move the cursor to the beginning of the previous word, as delimited by white space. - - - Move the cursor to the end this word, as delimited by white space. - - - Write the contents of the local clipboard before the cursor. - - - Move the cursor to the beginning of the next word, as delimited by white space. - - - Move the cursor to the matching brace. - - - Delete backward to the beginning of the previous word, as delimited by white space. - - - Delete the current word, as delimited by white space. - - - Delete to the end of the current word, as delimited by white space and common delimiters. - - - Delete to the end of this word, as delimited by white space. - - - Delete backward to the beginning of the previous word, as delimited by white space, and enter insert mode. - - - Delete to the beginning of the next word, as delimited by white space, and enter insert mode. - - - Delete to the end of the word, as delimited by white space and common delimiters, and enter insert mode. - - - Delete to the end of the word, as delimited by white space, and enter insert mode. - - - Place all characters in the current line into the local clipboard. - - - Place all characters at and after the cursor into the local clipboard. - - - Place all characters from before the cursor to the beginning of the previous word, as delimited by white space and common delimiters, into the local clipboard. - - - Place all characters from before the cursor to the beginning of the previous word, as delimited by white space, into the local clipboard. - - - Place all characters from the cursor to the end of the word, as delimited by white space and common delimiters, into the local clipboard. - - - Place all characters from the cursor to the end of the word, as delimited by white space, into the local clipboard. - - - Place the characters from the cursor to the end of the next word, as delimited by white space and common delimiters, into the local clipboard. - - - Place the characters from the cursor to the end of the next white space delimited word into the local clipboard. - - - Place the character to the left of the cursor into the local clipboard. - - - Place the character at the cursor into the local clipboard. - - - Place the characters before the cursor into the local clipboard. - - - Place all characters before the cursor and to the 1st non-white space character into the local clipboard. - - - Place all characters between the matching brace and the cursor into the local clipboard. - - - Deletes all characters between the cursor position and the matching brace. - - - If the line is empty, exit, otherwise accept the line as input. - - - Handles the processing of a number argument after the first key of a chord. - - - Exit the shell. - - - Invokes TabCompleteNext after doing some vi-specific clean up. - - - Invokes TabCompletePrevious after doing some vi-specific clean up. - - - Invokes the console compatible editor specified by $env:VISUAL or $env:$EDITOR on the current command line. - - - Appends a new multi-line edit mode line to the current line. - - - Inserts a new multi-line edit mode line in front of the current line. - - - Joins the current multi-line edit mode line with the next. - - - The -ViMode parameter was used, but the current EditMode is not Vi. - - - Inserts a new empty line above the current line without attempting to execute the input - - - Inserts a new empty line below the current line without attempting to execute the input - - - Error reading or writing history file '{0}': {1} - - - This error will not be reported again in this session. Consider using a different path with: - Set-PSReadlineOption -HistorySavePath <Path> -Or not saving history with: - Set-PSReadlineOption -HistorySaveStyle SaveNothing - - - An exception occurred in custom key handler, see $error for more information: {0} - - diff --git a/src/Microsoft.PowerShell.PSReadLine/PublicAPI.cs b/src/Microsoft.PowerShell.PSReadLine/PublicAPI.cs deleted file mode 100644 index 3f0163a781d..00000000000 --- a/src/Microsoft.PowerShell.PSReadLine/PublicAPI.cs +++ /dev/null @@ -1,250 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -using System; -using System.Collections; -using System.Diagnostics.CodeAnalysis; -using System.Management.Automation; -using System.Management.Automation.Language; -using System.Management.Automation.Runspaces; - - -namespace Microsoft.PowerShell -{ - namespace Internal - { -#pragma warning disable 1591 - - [SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] - public interface IPSConsoleReadLineMockableMethods - { - void Ding(); - CommandCompletion CompleteInput(string input, int cursorIndex, Hashtable options, System.Management.Automation.PowerShell powershell); - bool RunspaceIsRemote(Runspace runspace); - - } - - [SuppressMessage("Microsoft.MSInternal", "CA903:InternalNamespaceShouldNotContainPublicTypes")] - public interface IConsole - { - object GetConsoleInputMode(); - void SetConsoleInputMode(object mode); - void RestoreConsoleInputMode(object mode); - ConsoleKeyInfo ReadKey(); - bool KeyAvailable { get; } - int CursorLeft { get; set; } - int CursorTop { get; set;} - int CursorSize { get; set; } - int BufferWidth { get; set; } - int BufferHeight { get; set;} - int WindowWidth { get; set; } - int WindowHeight { get; set; } - int WindowTop { get; set; } - ConsoleColor BackgroundColor { get; set; } - ConsoleColor ForegroundColor { get; set; } - void SetWindowPosition(int left, int top); - void SetCursorPosition(int left, int top); - void WriteLine(string s); - void Write(string s); - void WriteBufferLines(BufferChar[] buffer, ref int top); - void WriteBufferLines(BufferChar[] buffer, ref int top, bool ensureBottomLineVisible); - void ScrollBuffer(int lines); - BufferChar[] ReadBufferLines(int top, int count); - - void StartRender(); - int LengthInBufferCells(char c); - void EndRender(); -#if UNIX - void Clear(); -#endif - } - -#pragma warning restore 1591 - } - - /// - public partial class PSConsoleReadLine - { - /// - /// Insert a character at the current position. Supports undo. - /// - /// Character to insert - public static void Insert(char c) - { - _singleton.SaveEditItem(EditItemInsertChar.Create(c, _singleton._current)); - - // Use Append if possible because Insert at end makes StringBuilder quite slow. - if (_singleton._current == _singleton._buffer.Length) - { - _singleton._buffer.Append(c); - } - else - { - _singleton._buffer.Insert(_singleton._current, c); - } - _singleton._current += 1; - _singleton.Render(); - } - - /// - /// Insert a string at the current position. Supports undo. - /// - /// String to insert - public static void Insert(string s) - { - _singleton.SaveEditItem(EditItemInsertString.Create(s, _singleton._current)); - - // Use Append if possible because Insert at end makes StringBuilder quite slow. - if (_singleton._current == _singleton._buffer.Length) - { - _singleton._buffer.Append(s); - } - else - { - _singleton._buffer.Insert(_singleton._current, s); - } - _singleton._current += s.Length; - _singleton.Render(); - } - - /// - /// Delete some text at the given position. Supports undo. - /// - /// The start position to delete - /// The length to delete - public static void Delete(int start, int length) - { - Replace(start, length, null); - } - - /// - /// Replace some text at the given position. Supports undo. - /// - /// The start position to replace - /// The length to replace - /// The replacement text - /// The action that initiated the replace (used for undo) - /// The argument to the action that initiated the replace (used for undo) - public static void Replace(int start, int length, string replacement, Action instigator = null, object instigatorArg = null) - { - if (start < 0 || start > _singleton._buffer.Length) - { - throw new ArgumentException(PSReadLineResources.StartOutOfRange, "start"); - } - if (length > (_singleton._buffer.Length - start)) - { - throw new ArgumentException(PSReadLineResources.ReplacementLengthTooBig, "length"); - } - - bool useEditGroup = (_singleton._editGroupStart == -1); - - if (useEditGroup) - { - _singleton.StartEditGroup(); - } - - var str = _singleton._buffer.ToString(start, length); - _singleton.SaveEditItem(EditItemDelete.Create(str, start)); - _singleton._buffer.Remove(start, length); - if (replacement != null) - { - _singleton.SaveEditItem(EditItemInsertString.Create(replacement, start)); - _singleton._buffer.Insert(start, replacement); - _singleton._current = start + replacement.Length; - } - else - { - _singleton._current = start; - } - - if (useEditGroup) - { - _singleton.EndEditGroup(instigator, instigatorArg); // Instigator is needed for VI undo - _singleton.Render(); - } - } - - /// - /// Get the state of the buffer - the current input and the position of the cursor - /// - public static void GetBufferState(out string input, out int cursor) - { - input = _singleton._buffer.ToString(); - cursor = _singleton._current; - } - - /// - /// Get the state of the buffer - the ast, tokens, errors, and position of the cursor - /// - public static void GetBufferState(out Ast ast, out Token[] tokens, out ParseError[] parseErrors, out int cursor) - { - _singleton.ParseInput(); - ast = _singleton._ast; - tokens = _singleton._tokens; - parseErrors = _singleton._parseErrors; - cursor = _singleton._current; - } - - /// - /// Get the selection state of the buffer - /// - /// The start of the current selection or -1 if nothing is selected. - /// The length of the current selection or -1 if nothing is selected. - public static void GetSelectionState(out int start, out int length) - { - if (_singleton._visualSelectionCommandCount == 0) - { - start = -1; - length = -1; - } - else - { - _singleton.GetRegion(out start, out length); - } - } - - /// - /// Set the position of the cursor. - /// - public static void SetCursorPosition(int cursor) - { - if (cursor > _singleton._buffer.Length + ViEndOfLineFactor) - { - cursor = _singleton._buffer.Length + ViEndOfLineFactor; - } - if (cursor < 0) - { - cursor = 0; - } - - _singleton._current = cursor; - _singleton.PlaceCursor(); - } - - /// - /// A helper method when your function expects an optional int argument (e.g. from DigitArgument) - /// If there is not argument (it's null), returns true and sets numericArg to defaultNumericArg. - /// Dings and returns false if the argument is not an int (no conversion is attempted) - /// Otherwise returns true, and numericArg has the result. - /// - public static bool TryGetArgAsInt(object arg, out int numericArg, int defaultNumericArg) - { - if (arg == null) - { - numericArg = defaultNumericArg; - return true; - } - - if (arg is int) - { - numericArg = (int)arg; - return true; - } - - Ding(); - numericArg = 0; - return false; - } - } -} diff --git a/src/Microsoft.PowerShell.PSReadLine/ReadLine.cs b/src/Microsoft.PowerShell.PSReadLine/ReadLine.cs deleted file mode 100644 index 0282d77841f..00000000000 --- a/src/Microsoft.PowerShell.PSReadLine/ReadLine.cs +++ /dev/null @@ -1,957 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; -using System.Globalization; -using System.Management.Automation; -using System.Management.Automation.Language; -using System.Management.Automation.Runspaces; -using System.Runtime.InteropServices; -using System.Text; -using System.Threading; -using Microsoft.PowerShell.Commands; -using Microsoft.PowerShell.Internal; - -[module: SuppressMessage("Microsoft.Design", "CA1014:MarkAssembliesWithClsCompliant")] - -namespace Microsoft.PowerShell -{ - [SuppressMessage("Microsoft.Design", "CA1032:ImplementStandardExceptionConstructors")] - [SuppressMessage("Microsoft.Usage", "CA2237:MarkISerializableTypesWithSerializable")] - class ExitException : Exception { } - - public partial class PSConsoleReadLine : IPSConsoleReadLineMockableMethods - { - private static readonly PSConsoleReadLine _singleton = new PSConsoleReadLine(); - - private bool _delayedOneTimeInitCompleted; - - private IPSConsoleReadLineMockableMethods _mockableMethods; - private IConsole _console; - - private EngineIntrinsics _engineIntrinsics; -#if !UNIX - private static GCHandle _breakHandlerGcHandle; -#endif - private Thread _readKeyThread; - private AutoResetEvent _readKeyWaitHandle; - private AutoResetEvent _keyReadWaitHandle; - private ManualResetEvent _closingWaitHandle; - private WaitHandle[] _threadProcWaitHandles; - private WaitHandle[] _requestKeyWaitHandles; - private object _savedConsoleInputMode; - - private readonly StringBuilder _buffer; - private readonly StringBuilder _statusBuffer; - private bool _statusIsErrorMessage; - private string _statusLinePrompt; - private List _edits; - private int _editGroupStart; - private int _undoEditIndex; - private int _mark; - private bool _inputAccepted; - private readonly Queue _queuedKeys; - private Stopwatch _lastRenderTime; - private static Stopwatch _readkeyStopwatch = new Stopwatch(); - - // Save a fixed # of keys so we can reconstruct a repro after a crash - private readonly static HistoryQueue _lastNKeys = new HistoryQueue(200); - - // Tokens etc. - private Token[] _tokens; - private Ast _ast; - private ParseError[] _parseErrors; - - bool IPSConsoleReadLineMockableMethods.RunspaceIsRemote(Runspace runspace) - { - return runspace != null && runspace.ConnectionInfo != null; - } - - private void ReadOneOrMoreKeys() - { - _readkeyStopwatch.Restart(); - while (_console.KeyAvailable) - { - var key = _console.ReadKey(); - _lastNKeys.Enqueue(key); - _queuedKeys.Enqueue(key); - if (_readkeyStopwatch.ElapsedMilliseconds > 2) - { - // Don't spend too long in this loop if there are lots of queued keys - break; - } - } - - if (_queuedKeys.Count == 0) - { - var key = _console.ReadKey(); - _lastNKeys.Enqueue(key); - _queuedKeys.Enqueue(key); - } - } - - private void ReadKeyThreadProc() - { - while (true) - { - // Wait until ReadKey tells us to read a key (or it's time to exit). - int handleId = WaitHandle.WaitAny(_singleton._threadProcWaitHandles); - if (handleId == 1) // It was the _closingWaitHandle that was signaled. - break; - - ReadOneOrMoreKeys(); - - // One or more keys were read - let ReadKey know we're done. - _keyReadWaitHandle.Set(); - } - } - - private static ConsoleKeyInfo ReadKey() - { - // Reading a key is handled on a different thread. During process shutdown, - // PowerShell will wait in it's ConsoleCtrlHandler until the pipeline has completed. - // If we're running, we're most likely blocked waiting for user input. - // This is a problem for two reasons. First, exiting takes a long time (5 seconds - // on Win8) because PowerShell is waiting forever, but the OS will forcibly terminate - // the console. Also - if there are any event handlers for the engine event - // PowerShell.Exiting, those handlers won't get a chance to run. - // - // By waiting for a key on a different thread, our pipeline execution thread - // (the thread Readline is called from) avoid being blocked in code that can't - // be unblocked and instead blocks on events we control. - - // First, set an event so the thread to read a key actually attempts to read a key. - _singleton._readKeyWaitHandle.Set(); - - int handleId; - System.Management.Automation.PowerShell ps = null; - - try - { - while (true) - { - // Next, wait for one of three things: - // - a key is pressed - // - the console is exiting - // - 300ms - to process events if we're idle - - handleId = WaitHandle.WaitAny(_singleton._requestKeyWaitHandles, 300); - if (handleId != WaitHandle.WaitTimeout) - break; - if (_singleton._engineIntrinsics == null) - continue; - - // If we timed out, check for event subscribers (which is just - // a hint that there might be an event waiting to be processed.) - var eventSubscribers = _singleton._engineIntrinsics.Events.Subscribers; - if (eventSubscribers.Count > 0) - { - bool runPipelineForEventProcessing = false; - foreach (var sub in eventSubscribers) - { - if (sub.SourceIdentifier.Equals("PowerShell.OnIdle", StringComparison.OrdinalIgnoreCase)) - { - // There is an OnIdle event. We're idle because we timed out. Normally - // PowerShell generates this event, but PowerShell assumes the engine is not - // idle because it called PSConsoleHostReadline which isn't returning. - // So we generate the event instead. - _singleton._engineIntrinsics.Events.GenerateEvent("PowerShell.OnIdle", null, null, null); - runPipelineForEventProcessing = true; - break; - } - - // If there are any event subscribers that have an action (which might - // write to the console) and have a source object (i.e. aren't engine - // events), run a tiny useless bit of PowerShell so that the events - // can be processed. - if (sub.Action != null && sub.SourceObject != null) - { - runPipelineForEventProcessing = true; - break; - } - } - - if (runPipelineForEventProcessing) - { - if (ps == null) - { - ps = System.Management.Automation.PowerShell.Create(RunspaceMode.CurrentRunspace); - ps.AddScript("0"); - } - - // To detect output during possible event processing, see if the cursor moved - // and rerender if so. - var console = _singleton._console; - var y = console.CursorTop; - ps.Invoke(); - if (y != console.CursorTop) - { - _singleton._initialY = console.CursorTop; - _singleton.Render(); - } - } - } - } - } - finally - { - if (ps != null) { ps.Dispose(); } - } - - if (handleId == 1) - { - // The console is exiting - throw an exception to unwind the stack to the point - // where we can return from ReadLine. - if (_singleton.Options.HistorySaveStyle == HistorySaveStyle.SaveAtExit) - { - _singleton.SaveHistoryAtExit(); - } - _singleton._historyFileMutex.Dispose(); - - throw new OperationCanceledException(); - } - - var key = _singleton._queuedKeys.Dequeue(); - return key; - } - - private void PrependQueuedKeys(ConsoleKeyInfo key) - { - if (_queuedKeys.Count > 0) - { - // This should almost never happen so being inefficient is fine. - var list = new List(_queuedKeys); - _queuedKeys.Clear(); - _queuedKeys.Enqueue(key); - list.ForEach(k => _queuedKeys.Enqueue(k)); - } - else - { - _queuedKeys.Enqueue(key); - } - } - - private bool BreakHandler(ConsoleBreakSignal signal) - { - if (signal == ConsoleBreakSignal.Close || signal == ConsoleBreakSignal.Shutdown) - { - // Set the event so ReadKey throws an exception to unwind. - _closingWaitHandle.Set(); - } - - return false; - } - - /// - /// Entry point - called from the PowerShell function PSConsoleHostReadline - /// after the prompt has been displayed. - /// - /// The complete command line. - public static string ReadLine(Runspace runspace, EngineIntrinsics engineIntrinsics) - { - var console = _singleton._console; - - // If either stdin or stdout is redirected, PSReadline doesn't really work, so throw - // and let PowerShell call Console.ReadLine or do whatever else it decides to do. - if (Console.IsInputRedirected || Console.IsOutputRedirected) - { - throw new NotSupportedException(); - } - - _singleton._savedConsoleInputMode = _singleton._console.GetConsoleInputMode(); - bool firstTime = true; - while (true) - { - try - { - _singleton._console.SetConsoleInputMode(_singleton._savedConsoleInputMode); - - if (firstTime) - { - firstTime = false; - _singleton.Initialize(runspace, engineIntrinsics); - } - - return _singleton.InputLoop(); - } - catch (OperationCanceledException) - { - // Console is exiting - return value isn't too critical - null or 'exit' could work equally well. - return ""; - } - catch (ExitException) - { - return "exit"; - } - catch (CustomHandlerException e) - { - var oldColor = console.ForegroundColor; - console.ForegroundColor = ConsoleColor.Red; - console.WriteLine( - string.Format(CultureInfo.CurrentUICulture, PSReadLineResources.OopsCustomHandlerException, e.InnerException.Message)); - console.ForegroundColor = oldColor; - - var lineBeforeCrash = _singleton._buffer.ToString(); - _singleton.Initialize(runspace, _singleton._engineIntrinsics); - InvokePrompt(); - Insert(lineBeforeCrash); - } - catch (Exception e) - { - // If we're running tests, just throw. - if (_singleton._mockableMethods != _singleton) - { - throw; - } - - while (e.InnerException != null) - { - e = e.InnerException; - } - var oldColor = console.ForegroundColor; - console.ForegroundColor = ConsoleColor.Red; - console.WriteLine(PSReadLineResources.OopsAnErrorMessage1); - console.ForegroundColor = oldColor; - var sb = new StringBuilder(); - for (int i = 0; i < _lastNKeys.Count; i++) - { - sb.Append(' '); - sb.Append(_lastNKeys[i].ToGestureString()); - - KeyHandler handler; - if (_singleton._dispatchTable.TryGetValue(_lastNKeys[i], out handler) && - "AcceptLine".Equals(handler.BriefDescription, StringComparison.OrdinalIgnoreCase)) - { - // Make it a little easier to see the keys - sb.Append('\n'); - } - } - - console.WriteLine(string.Format(CultureInfo.CurrentUICulture, PSReadLineResources.OopsAnErrorMessage2, _lastNKeys.Count, sb, e)); - var lineBeforeCrash = _singleton._buffer.ToString(); - _singleton.Initialize(runspace, _singleton._engineIntrinsics); - InvokePrompt(); - Insert(lineBeforeCrash); - } - finally - { - _singleton._console.RestoreConsoleInputMode(_singleton._savedConsoleInputMode); - } - } - } - - private string InputLoop() - { - ProcessViVisualEditing(); - - while (true) - { - var killCommandCount = _killCommandCount; - var yankCommandCount = _yankCommandCount; - var tabCommandCount = _tabCommandCount; - var searchHistoryCommandCount = _searchHistoryCommandCount; - var recallHistoryCommandCount = _recallHistoryCommandCount; - var yankLastArgCommandCount = _yankLastArgCommandCount; - var visualSelectionCommandCount = _visualSelectionCommandCount; - var movingAtEndOfLineCount = _moveToLineCommandCount; - - var key = ReadKey(); - ProcessOneKey(key, _dispatchTable, ignoreIfNoAction: false, arg: null); - if (_inputAccepted) - { - return MaybeAddToHistory(_buffer.ToString(), _edits, _undoEditIndex, readingHistoryFile: false, fromDifferentSession: false); - } - - if (killCommandCount == _killCommandCount) - { - // Reset kill command count if it didn't change - _killCommandCount = 0; - } - if (yankCommandCount == _yankCommandCount) - { - // Reset yank command count if it didn't change - _yankCommandCount = 0; - } - if (yankLastArgCommandCount == _yankLastArgCommandCount) - { - // Reset yank last arg command count if it didn't change - _yankLastArgCommandCount = 0; - _yankLastArgState = null; - } - if (tabCommandCount == _tabCommandCount) - { - // Reset tab command count if it didn't change - _tabCommandCount = 0; - _tabCompletions = null; - } - if (searchHistoryCommandCount == _searchHistoryCommandCount) - { - if (_searchHistoryCommandCount > 0) - { - _emphasisStart = -1; - _emphasisLength = 0; - Render(); - _currentHistoryIndex = _history.Count; - } - _searchHistoryCommandCount = 0; - _searchHistoryPrefix = null; - } - if (recallHistoryCommandCount == _recallHistoryCommandCount) - { - if (_recallHistoryCommandCount > 0) - { - _currentHistoryIndex = _history.Count; - } - _recallHistoryCommandCount = 0; - } - if (searchHistoryCommandCount == _searchHistoryCommandCount && - recallHistoryCommandCount == _recallHistoryCommandCount) - { - _hashedHistory = null; - } - if (visualSelectionCommandCount == _visualSelectionCommandCount && _visualSelectionCommandCount > 0) - { - _visualSelectionCommandCount = 0; - Render(); // Clears the visual selection - } - if (movingAtEndOfLineCount == _moveToLineCommandCount) - { - _moveToLineCommandCount = 0; - } - } - } - - T CalloutUsingDefaultConsoleMode(Func func) - { - var currentMode = _console.GetConsoleInputMode(); - try - { - _console.RestoreConsoleInputMode(_savedConsoleInputMode); - return func(); - } - finally - { - _console.RestoreConsoleInputMode(currentMode); - } - } - - void CalloutUsingDefaultConsoleMode(Action action) - { - CalloutUsingDefaultConsoleMode(() => { action(); return null; }); - } - - void ProcessOneKey(ConsoleKeyInfo key, Dictionary dispatchTable, bool ignoreIfNoAction, object arg) - { - KeyHandler handler; - - if (!dispatchTable.TryGetValue(key, out handler)) - { - // If we see a control character where Ctrl wasn't used but shift was, treat that like - // shift hadn't be pressed. This cleanly allows Shift+Backspace without adding a key binding. - if (key.KeyChar > 0 && char.IsControl(key.KeyChar) && key.Modifiers == ConsoleModifiers.Shift) - { - key = new ConsoleKeyInfo(key.KeyChar, key.Key, false, false, false); - dispatchTable.TryGetValue(key, out handler); - } - } - - if (handler != null) - { - if (handler.ScriptBlock != null) - { - CalloutUsingDefaultConsoleMode(() => handler.Action(key, arg)); - } - else - { - handler.Action(key, arg); - } - } - else if (!ignoreIfNoAction && key.KeyChar != 0) - { - SelfInsert(key, arg); - } - } - - private PSConsoleReadLine() - { - _mockableMethods = this; -#if UNIX - _console = new TTYConsole(); -#else - _console = new ConhostConsole(); -#endif - - _buffer = new StringBuilder(8 * 1024); - _statusBuffer = new StringBuilder(256); - _savedCurrentLine = new HistoryItem(); - _queuedKeys = new Queue(); - - string hostName = null; - // This works mostly by luck - we're not doing anything to guarantee the constructor for our - // singleton is called on a thread with a runspace, but it is happening by coincidence. - using (var ps = System.Management.Automation.PowerShell.Create(RunspaceMode.CurrentRunspace)) - { - try - { - ps.AddCommand("Get-Variable").AddParameter("Name", "host").AddParameter("ValueOnly"); - var results = ps.Invoke(); - dynamic host = results.Count == 1 ? results[0] : null; - if (host != null) - { - hostName = host.Name as string; - } - } - catch - { - } - } - if (hostName == null) - { - hostName = "PSReadline"; - } - _options = new PSConsoleReadlineOptions(hostName); - SetDefaultBindings(_options.EditMode); - } - - private void Initialize(Runspace runspace, EngineIntrinsics engineIntrinsics) - { - _engineIntrinsics = engineIntrinsics; - _runspace = runspace; - - if (!_delayedOneTimeInitCompleted) - { - DelayedOneTimeInitialize(); - _delayedOneTimeInitCompleted = true; - } - - _buffer.Clear(); - _edits = new List(); - _undoEditIndex = 0; - _editGroupStart = -1; - _current = 0; - _mark = 0; - _emphasisStart = -1; - _emphasisLength = 0; - _tokens = null; - _parseErrors = null; - _inputAccepted = false; - _initialX = _console.CursorLeft; - _initialY = _console.CursorTop - Options.ExtraPromptLineCount; - _initialBackgroundColor = _console.BackgroundColor; - _initialForegroundColor = _console.ForegroundColor; - _space = new BufferChar - { - UnicodeChar = ' ', - BackgroundColor = _initialBackgroundColor, - ForegroundColor = _initialForegroundColor - }; - _bufferWidth = _console.BufferWidth; - _killCommandCount = 0; - _yankCommandCount = 0; - _yankLastArgCommandCount = 0; - _tabCommandCount = 0; - _visualSelectionCommandCount = 0; - _statusIsErrorMessage = false; - - -#if UNIX // TODO: not necessary if ReadBufferLines worked, or if rendering worked on spans instead of complete lines - string newPrompt = GetPrompt(); - int index = newPrompt.LastIndexOf('\n'); - if (index != -1) - { - // The prompt text could be a multi-line string, and in such case - // we only want the part of it that is shown on the input line. - newPrompt = newPrompt.Substring(index + 1); - } - var bufferLineCount = (newPrompt.Length) / (_console.BufferWidth) + 1; - _consoleBuffer = ReadBufferLines(_initialY, bufferLineCount); - - for (int i=0; i 0) - { - _currentHistoryIndex = _getNextHistoryIndex; - UpdateFromHistory(moveCursor: true); - _getNextHistoryIndex = 0; - if (_searchHistoryCommandCount > 0) - { - _searchHistoryPrefix = ""; - if (Options.HistoryNoDuplicates) - { - _hashedHistory = new Dictionary(); - } - } - } - else - { - _searchHistoryCommandCount = 0; - } - } - - private void DelayedOneTimeInitialize() - { - // Delayed initialization is needed so that options can be set - // after the constuctor but have an affect before the user starts - // editing their first command line. For example, if the user - // specifies a custom history save file, we don't want to try reading - // from the default one. - - if (_engineIntrinsics != null) - { - var historyCountVar = _engineIntrinsics.SessionState.PSVariable.Get("MaximumHistoryCount"); - if (historyCountVar != null && historyCountVar.Value is int) - { - _options.MaximumHistoryCount = (int)historyCountVar.Value; - } - } - -#if UNIX - _historyFileMutex = new Mutex(false); -#else - _historyFileMutex = new Mutex(false, GetHistorySaveFileMutexName()); -#endif - - _history = new HistoryQueue(Options.MaximumHistoryCount); - _currentHistoryIndex = 0; - - bool readHistoryFile = true; - try - { - if (_options.HistorySaveStyle == HistorySaveStyle.SaveNothing && Runspace.DefaultRunspace != null) - { - using (var ps = System.Management.Automation.PowerShell.Create(RunspaceMode.CurrentRunspace)) - { - ps.AddCommand("Microsoft.PowerShell.Core\\Get-History"); - foreach (var historyInfo in ps.Invoke()) - { - AddToHistory(historyInfo.CommandLine); - } - readHistoryFile = false; - } - } - } - catch - { - } - - if (readHistoryFile) - { - ReadHistoryFile(); - } - - _killIndex = -1; // So first add indexes 0. - _killRing = new List(Options.MaximumKillRingCount); - -#if !UNIX - _breakHandlerGcHandle = GCHandle.Alloc(new BreakHandler(_singleton.BreakHandler)); - NativeMethods.SetConsoleCtrlHandler((BreakHandler)_breakHandlerGcHandle.Target, true); -#endif - _singleton._readKeyWaitHandle = new AutoResetEvent(false); - _singleton._keyReadWaitHandle = new AutoResetEvent(false); - _singleton._closingWaitHandle = new ManualResetEvent(false); - _singleton._requestKeyWaitHandles = new WaitHandle[] {_singleton._keyReadWaitHandle, _singleton._closingWaitHandle}; - _singleton._threadProcWaitHandles = new WaitHandle[] {_singleton._readKeyWaitHandle, _singleton._closingWaitHandle}; - -#if !CORECLR - // This is for a "being hosted in an alternate appdomain scenario" (the - // DomainUnload event is not raised for the default appdomain). It allows us - // to exit cleanly when the appdomain is unloaded but the process is not going - // away. - if (!AppDomain.CurrentDomain.IsDefaultAppDomain()) - { - AppDomain.CurrentDomain.DomainUnload += (x, y) => - { - _singleton._closingWaitHandle.Set(); - _singleton._readKeyThread.Join(); // may need to wait for history to be written - }; - } -#endif - - _singleton._readKeyThread = new Thread(_singleton.ReadKeyThreadProc) {IsBackground = true}; - _singleton._readKeyThread.Start(); - } - - [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")] - private static void Chord(ConsoleKeyInfo? key = null, object arg = null) - { - if (!key.HasValue) - { - throw new ArgumentNullException("key"); - } - - Dictionary secondKeyDispatchTable; - if (_singleton._chordDispatchTable.TryGetValue(key.Value, out secondKeyDispatchTable)) - { - var secondKey = ReadKey(); - _singleton.ProcessOneKey(secondKey, secondKeyDispatchTable, ignoreIfNoAction: true, arg: arg); - } - } - - [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")] - private static void Ignore(ConsoleKeyInfo? key = null, object arg = null) - { - } - - private static void ExecuteOnSTAThread(Action action) - { -#if CORECLR - action(); -#else - if (Thread.CurrentThread.GetApartmentState() == ApartmentState.STA) - { - action(); - return; - } - - Exception exception = null; - var thread = new Thread(() => - { - try - { - action(); - } - catch (Exception e) - { - exception = e; - } - }); - - thread.SetApartmentState(ApartmentState.STA); - thread.Start(); - thread.Join(); - - if (exception != null) - { - throw exception; - } -#endif - } - - #region Miscellaneous bindable functions - - /// - /// Abort current action, e.g. incremental history search - /// - [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")] - public static void Abort(ConsoleKeyInfo? key = null, object arg = null) - { - } - - /// - /// Start a new digit argument to pass to other functions - /// - [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")] - public static void DigitArgument(ConsoleKeyInfo? key = null, object arg = null) - { - if (!key.HasValue || char.IsControl(key.Value.KeyChar)) - { - Ding(); - return; - } - - if (_singleton._options.EditMode == EditMode.Vi && key.Value.KeyChar == '0') - { - BeginningOfLine(); - return; - } - - bool sawDigit = false; - _singleton._statusLinePrompt = "digit-argument: "; - var argBuffer = _singleton._statusBuffer; - argBuffer.Append(key.Value.KeyChar); - if (key.Value.KeyChar == '-') - { - argBuffer.Append('1'); - } - else - { - sawDigit = true; - } - - _singleton.Render(); // Render prompt - while (true) - { - var nextKey = ReadKey(); - KeyHandler handler; - if (_singleton._dispatchTable.TryGetValue(nextKey, out handler)) - { - if (handler.Action == DigitArgument) - { - if (nextKey.KeyChar == '-') - { - if (argBuffer[0] == '-') - { - argBuffer.Remove(0, 1); - } - else - { - argBuffer.Insert(0, '-'); - } - _singleton.Render(); // Render prompt - continue; - } - - if (nextKey.KeyChar >= '0' && nextKey.KeyChar <= '9') - { - if (!sawDigit && argBuffer.Length > 0) - { - // Buffer is either '-1' or '1' from one or more Alt+- keys - // but no digits yet. Remove the '1'. - argBuffer.Length -= 1; - } - sawDigit = true; - argBuffer.Append(nextKey.KeyChar); - _singleton.Render(); // Render prompt - continue; - } - } - else if (handler.Action == Abort || - handler.Action == CancelLine || - handler.Action == CopyOrCancelLine) - { - break; - } - } - - int intArg; - if (int.TryParse(argBuffer.ToString(), out intArg)) - { - _singleton.ProcessOneKey(nextKey, _singleton._dispatchTable, ignoreIfNoAction: false, arg: intArg); - } - else - { - Ding(); - } - break; - } - - // Remove our status line - argBuffer.Clear(); - _singleton.ClearStatusMessage(render: true); - } - - - /// - /// Erases the current prompt and calls the prompt function to redisplay - /// the prompt. Useful for custom key handlers that change state, e.g. - /// change the current directory. - /// - [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")] - public static void InvokePrompt(ConsoleKeyInfo? key = null, object arg = null) - { - var currentBuffer = _singleton._buffer.ToString(); - var currentPos = _singleton._current; - _singleton._buffer.Clear(); - _singleton._current = 0; - for (int i = 0; i < _singleton._consoleBuffer.Length; i++) - { - _singleton._consoleBuffer[i].UnicodeChar = ' '; - _singleton._consoleBuffer[i].ForegroundColor = _singleton._console.ForegroundColor; - _singleton._consoleBuffer[i].BackgroundColor = _singleton._console.BackgroundColor; - } - _singleton.Render(); - _singleton._console.CursorLeft = 0; - _singleton._console.CursorTop = _singleton._initialY - _singleton.Options.ExtraPromptLineCount; - - string newPrompt = GetPrompt(); - _singleton._console.Write(newPrompt); - - _singleton._initialX = _singleton._console.CursorLeft; - _singleton._consoleBuffer = ReadBufferLines(_singleton._initialY, 1 + _singleton.Options.ExtraPromptLineCount); -#if UNIX // TODO: ReadBufferLines only needed for extra line, but doesn't work on Linux - for (int i=0; i - /// Gets the current prompt as possibly defined by the user through the - /// prompt function, and returns a default prompt if no other is - /// available. Also handles remote prompts. - /// - public static string GetPrompt() - { - string newPrompt; - var runspaceIsRemote = _singleton._mockableMethods.RunspaceIsRemote(_singleton._runspace); - - if ((_singleton._runspace.Debugger != null) && _singleton._runspace.Debugger.InBreakpoint) - { - // Run prompt command in debugger API to ensure it is run correctly on the runspace. - // This handles remote runspace debugging and nested debugger scenarios. - PSDataCollection results = new PSDataCollection(); - var command = new PSCommand(); - command.AddCommand(PromptCommand); - _singleton._runspace.Debugger.ProcessCommand( - command, - results); - - newPrompt = (results.Count == 1) ? (results[0].BaseObject as string) : DefaultPrompt; - } - else - { - System.Management.Automation.PowerShell ps; - if (!runspaceIsRemote) - { - ps = System.Management.Automation.PowerShell.Create(RunspaceMode.CurrentRunspace); - } - else - { - ps = System.Management.Automation.PowerShell.Create(); - ps.Runspace = _singleton._runspace; - } - using (ps) - { - ps.AddCommand(PromptCommand); - var result = ps.Invoke(); - newPrompt = result.Count == 1 ? result[0] : DefaultPrompt; - } - } - - if (string.IsNullOrEmpty(newPrompt)) - { - newPrompt = DefaultPrompt; - } - - if (runspaceIsRemote) - { - var connectionInfo = _singleton._runspace.ConnectionInfo; - if (!string.IsNullOrEmpty(connectionInfo.ComputerName)) - { - newPrompt = string.Format(CultureInfo.InvariantCulture, "[{0}]: {1}", connectionInfo.ComputerName, newPrompt); - } - } - - return newPrompt; - } - - #endregion Miscellaneous bindable functions - - } -} diff --git a/src/Microsoft.PowerShell.PSReadLine/ReadLine.vi.cs b/src/Microsoft.PowerShell.PSReadLine/ReadLine.vi.cs deleted file mode 100644 index 9c7b79289da..00000000000 --- a/src/Microsoft.PowerShell.PSReadLine/ReadLine.vi.cs +++ /dev/null @@ -1,1247 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; -using System.IO; -using System.Linq; -using System.Management.Automation; -using System.Management.Automation.Language; -using System.Runtime.InteropServices; -using System.Text; -using System.Text.RegularExpressions; -using System.Threading; - -namespace Microsoft.PowerShell -{ - public partial class PSConsoleReadLine - { - /// - /// Remembers last history search direction. - /// - private bool _searchHistoryBackward = true; - - private class ViCharacterSearcher - { - private char searchChar = '\0'; - private bool wasBackward = false; - private bool wasBackoff = false; - - public static ViCharacterSearcher instance = new ViCharacterSearcher(); - - public static bool IsRepeatable - { - get { return instance.searchChar != '\0'; } - } - - public static char SearchChar - { - get { return instance.searchChar; } - } - - public static bool WasBackward - { - get { return instance.wasBackward; } - } - - public static bool WasBackoff - { - get { return instance.wasBackoff; } - } - - public static void Set(char theChar, bool isBackward = false, bool isBackoff = false) - { - instance.searchChar = theChar; - instance.wasBackward = isBackward; - instance.wasBackoff = isBackoff; - } - - - public static void Search(char keyChar, object arg, bool backoff) - { - int qty = (arg is int) ? (int) arg : 1; - - for (int i = _singleton._current + 1; i < _singleton._buffer.Length; i++) - { - if (_singleton._buffer[i] == keyChar) - { - qty -= 1; - if (qty == 0) - { - _singleton._current = backoff ? i - 1 : i; - _singleton.PlaceCursor(); - return; - } - } - } - Ding(); - } - - public static bool SearchDelete(char keyChar, object arg, bool backoff, Action instigator) - { - int qty = (arg is int) ? (int) arg : 1; - - for (int i = _singleton._current + 1; i < _singleton._buffer.Length; i++) - { - if (_singleton._buffer[i] == keyChar) - { - qty -= 1; - if (qty == 0) - { - DeleteToEndPoint(arg, backoff ? i : i + 1, instigator); - return true; - } - } - } - Ding(); - return false; - } - - public static void SearchBackward(char keyChar, object arg, bool backoff) - { - Set(keyChar, isBackward: true, isBackoff: backoff); - int qty = (arg is int) ? (int) arg : 1; - - for (int i = _singleton._current - 1; i >= 0; i--) - { - if (_singleton._buffer[i] == keyChar) - { - qty -= 1; - if (qty == 0) - { - _singleton._current = backoff ? i + 1 : i; - _singleton.PlaceCursor(); - return; - } - } - } - Ding(); - } - - public static bool SearchBackwardDelete(char keyChar, object arg, bool backoff, Action instigator) - { - Set(keyChar, isBackward: true, isBackoff: backoff); - int qty = (arg is int) ? (int) arg : 1; - - for (int i = _singleton._current - 1; i >= 0; i--) - { - if (_singleton._buffer[i] == keyChar) - { - qty -= 1; - if (qty == 0) - { - DeleteBackwardToEndPoint(arg, backoff ? i + 1 : i, instigator); - return true; - } - } - } - Ding(); - return false; - } - } - - /// - /// Repeat the last recorded character search. - /// - public static void RepeatLastCharSearch(ConsoleKeyInfo? key = null, object arg = null) - { - if (!ViCharacterSearcher.IsRepeatable) - { - Ding(); - return; - } - - if (ViCharacterSearcher.WasBackward) - { - ViCharacterSearcher.SearchBackward(ViCharacterSearcher.SearchChar, null, ViCharacterSearcher.WasBackoff); - } - else - { - ViCharacterSearcher.Search(ViCharacterSearcher.SearchChar, null, ViCharacterSearcher.WasBackoff); - } - } - - /// - /// Repeat the last recorded character search, but in the opposite direction. - /// - public static void RepeatLastCharSearchBackwards(ConsoleKeyInfo? key = null, object arg = null) - { - if (!ViCharacterSearcher.IsRepeatable) - { - Ding(); - return; - } - - if (ViCharacterSearcher.WasBackward) - { - ViCharacterSearcher.Search(ViCharacterSearcher.SearchChar, null, ViCharacterSearcher.WasBackoff); - } - else - { - ViCharacterSearcher.SearchBackward(ViCharacterSearcher.SearchChar, null, ViCharacterSearcher.WasBackoff); - } - } - - /// - /// Read the next character and then find it, going forward, and then back off a character. - /// This is for 't' functionality. - /// - public static void SearchChar(ConsoleKeyInfo? key = null, object arg = null) - { - char keyChar = ReadKey().KeyChar; - ViCharacterSearcher.Set(keyChar, isBackward: false, isBackoff: false); - ViCharacterSearcher.Search(keyChar, arg, backoff: false); - } - - /// - /// Read the next character and then find it, going backward, and then back off a character. - /// This is for 'T' functionality. - /// - public static void SearchCharBackward(ConsoleKeyInfo? key = null, object arg = null) - { - char keyChar = ReadKey().KeyChar; - ViCharacterSearcher.Set(keyChar, isBackward: true, isBackoff: false); - ViCharacterSearcher.SearchBackward(keyChar, arg, backoff: false); - } - - /// - /// Read the next character and then find it, going forward, and then back off a character. - /// This is for 't' functionality. - /// - public static void SearchCharWithBackoff(ConsoleKeyInfo? key = null, object arg = null) - { - char keyChar = ReadKey().KeyChar; - ViCharacterSearcher.Set(keyChar, isBackward: false, isBackoff: true); - ViCharacterSearcher.Search(keyChar, arg, backoff: true); - } - - /// - /// Read the next character and then find it, going backward, and then back off a character. - /// This is for 'T' functionality. - /// - public static void SearchCharBackwardWithBackoff(ConsoleKeyInfo? key = null, object arg = null) - { - char keyChar = ReadKey().KeyChar; - ViCharacterSearcher.Set(keyChar, isBackward: true, isBackoff: true); - ViCharacterSearcher.SearchBackward(keyChar, arg, backoff: true); - } - - /// - /// Exits the shell. - /// - public static void ViExit(ConsoleKeyInfo? key = null, object arg = null) - { - throw new ExitException(); - } - - /// - /// Delete to the end of the line. - /// - public static void DeleteToEnd(ConsoleKeyInfo? key = null, object arg = null) - { - if (_singleton._current >= _singleton._buffer.Length) - { - Ding(); - return; - } - - _singleton._clipboard = _singleton._buffer.ToString(_singleton._current, _singleton._buffer.Length - _singleton._current); - _singleton.SaveEditItem(EditItemDelete.Create( - _singleton._clipboard, - _singleton._current, - DeleteToEnd, - arg - )); - _singleton._buffer.Remove(_singleton._current, _singleton._buffer.Length - _singleton._current); - _singleton._current = Math.Max(0, _singleton._buffer.Length - 1); - _singleton.Render(); - } - - /// - /// Delete the next word. - /// - public static void DeleteWord(ConsoleKeyInfo? key = null, object arg = null) - { - int qty = (arg is int) ? (int)arg : 1; - int endPoint = _singleton._current; - for (int i = 0; i < qty; i++) - { - endPoint = _singleton.ViFindNextWordPoint(endPoint, _singleton.Options.WordDelimiters); - } - - if (endPoint <= _singleton._current) - { - Ding(); - return; - } - - DeleteToEndPoint(arg, endPoint, DeleteWord); - } - - private static void DeleteToEndPoint(object arg, int endPoint, Action instigator) - { - _singleton.SaveToClipboard(_singleton._current, endPoint - _singleton._current); - _singleton.SaveEditItem(EditItemDelete.Create( - _singleton._clipboard, - _singleton._current, - instigator, - arg - )); - _singleton._buffer.Remove(_singleton._current, endPoint - _singleton._current); - if (_singleton._current >= _singleton._buffer.Length) - { - _singleton._current = Math.Max(0, _singleton._buffer.Length - 1); - } - _singleton.Render(); - } - - private static void DeleteBackwardToEndPoint(object arg, int endPoint, Action instigator) - { - int deleteLength = _singleton._current - endPoint + 1; - - _singleton.SaveToClipboard(endPoint, deleteLength); - _singleton.SaveEditItem(EditItemDelete.Create( - _singleton._clipboard, - endPoint, - instigator, - arg - )); - _singleton._buffer.Remove(endPoint, deleteLength); - _singleton._current = endPoint; - _singleton.Render(); - } - - /// - /// Delete the next glob (white space delimited word). - /// - public static void ViDeleteGlob(ConsoleKeyInfo? key = null, object arg = null) - { - int qty = (arg is int) ? (int)arg : 1; - int endPoint = _singleton._current; - while (qty-- > 0) - { - endPoint = _singleton.ViFindNextGlob(endPoint); - } - int length = endPoint - _singleton._current; - - _singleton.SaveToClipboard(_singleton._current, length); - _singleton.SaveEditItem(EditItemDelete.Create( - _singleton._clipboard, - _singleton._current, - ViDeleteGlob, - arg - )); - _singleton._buffer.Remove(_singleton._current, length); - if (_singleton._current >= _singleton._buffer.Length) - { - _singleton._current = Math.Max(0, _singleton._buffer.Length - 1); - } - _singleton.Render(); - } - - /// - /// Delete to the end of the word. - /// - public static void DeleteEndOfWord(ConsoleKeyInfo? key = null, object arg = null) - { - int qty = (arg is int) ? (int)arg : 1; - int endPoint = _singleton._current; - for (int i = 0; i < qty; i++) - { - endPoint = _singleton.ViFindNextWordEnd(endPoint, _singleton.Options.WordDelimiters); - } - - if (endPoint <= _singleton._current) - { - Ding(); - return; - } - _singleton.SaveToClipboard(_singleton._current, 1 + endPoint - _singleton._current); - _singleton.SaveEditItem(EditItemDelete.Create( - _singleton._clipboard, - _singleton._current, - DeleteEndOfWord, - arg - )); - _singleton._buffer.Remove(_singleton._current, 1 + endPoint - _singleton._current); - if (_singleton._current >= _singleton._buffer.Length) - { - _singleton._current = Math.Max(0,_singleton._buffer.Length - 1); - } - _singleton.Render(); - } - - /// - /// Delete to the end of the word. - /// - public static void ViDeleteEndOfGlob(ConsoleKeyInfo? key = null, object arg = null) - { - int qty = (arg is int) ? (int)arg : 1; - int endPoint = _singleton._current; - for (int i = 0; i < qty; i++) - { - endPoint = _singleton.ViFindGlobEnd(endPoint); - } - - _singleton.SaveToClipboard(_singleton._current, 1 + endPoint - _singleton._current); - _singleton.SaveEditItem(EditItemDelete.Create( - _singleton._clipboard, - _singleton._current, - ViDeleteEndOfGlob, - arg - )); - _singleton._buffer.Remove(_singleton._current, 1 + endPoint - _singleton._current); - if (_singleton._current >= _singleton._buffer.Length) - { - _singleton._current = Math.Max(0, _singleton._buffer.Length - 1); - } - _singleton.Render(); - } - - /// - /// Deletes until given character - /// - /// - /// - [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")] - private static void ViDeleteToChar(ConsoleKeyInfo? key = null, object arg = null) - { - var keyChar = ReadKey().KeyChar; - ViDeleteToChar(keyChar, key, arg); - } - - private static void ViDeleteToChar(char keyChar, ConsoleKeyInfo? key = null, object arg = null) - { - ViCharacterSearcher.Set(keyChar, isBackward: false, isBackoff: false); - ViCharacterSearcher.SearchDelete(keyChar, arg, backoff: false, instigator: (_key, _arg) => ViDeleteToChar(keyChar, _key, _arg)); - } - - /// - /// Deletes until given character - /// - /// - /// - [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")] - private static void ViDeleteToCharBackward(ConsoleKeyInfo? key = null, object arg = null) - { - var keyChar = ReadKey().KeyChar; - ViDeleteToCharBack(keyChar, key, arg); - } - - private static void ViDeleteToCharBack(char keyChar, ConsoleKeyInfo? key = null, object arg = null) - { - ViCharacterSearcher.SearchBackwardDelete(keyChar, arg, backoff: false, instigator: (_key, _arg) => ViDeleteToCharBack(keyChar, _key, _arg)); - } - - /// - /// Deletes until given character - /// - /// - /// - [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")] - private static void ViDeleteToBeforeChar(ConsoleKeyInfo? key = null, object arg = null) - { - var keyChar = ReadKey().KeyChar; - ViDeleteToBeforeChar(keyChar, key, arg); - } - - private static void ViDeleteToBeforeChar(char keyChar, ConsoleKeyInfo? key = null, object arg = null) - { - ViCharacterSearcher.Set(keyChar, isBackward: false, isBackoff: true); - ViCharacterSearcher.SearchDelete(keyChar, arg, backoff: true, instigator: (_key, _arg) => ViDeleteToBeforeChar(keyChar, _key, _arg)); - } - - /// - /// Deletes until given character - /// - /// - /// - [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")] - private static void ViDeleteToBeforeCharBackward(ConsoleKeyInfo? key = null, object arg = null) - { - var keyChar = ReadKey().KeyChar; - ViDeleteToBeforeCharBack(keyChar, key, arg); - } - - private static void ViDeleteToBeforeCharBack(char keyChar, ConsoleKeyInfo? key = null, object arg = null) - { - ViCharacterSearcher.Set(keyChar, isBackward: true, isBackoff: true); - ViCharacterSearcher.SearchBackwardDelete(keyChar, arg, backoff: true, instigator: (_key, _arg) => ViDeleteToBeforeCharBack(keyChar, _key, _arg)); - } - - /// - /// Ring the bell. - /// - private static void Ding(ConsoleKeyInfo? key = null, object arg = null) - { - Ding(); - } - - /// - /// Switch the current operating mode from Vi-Insert to Vi-Command. - /// - public static void ViCommandMode(ConsoleKeyInfo? key = null, object arg = null) - { - if (_singleton._editGroupStart >= 0) - { - _singleton._groupUndoHelper.EndGroup(); - } - _singleton._dispatchTable = _viCmdKeyMap; - _singleton._chordDispatchTable = _viCmdChordTable; - BackwardChar(); - _singleton.PlaceCursor(); - _singleton.ViIndicateCommandMode(); - } - - /// - /// Switch to Insert mode. - /// - public static void ViInsertMode(ConsoleKeyInfo? key = null, object arg = null) - { - _singleton._dispatchTable = _viInsKeyMap; - _singleton._chordDispatchTable = _viInsChordTable; - _singleton.ViIndicateInsertMode(); - } - - /// - /// Temporarily swap in Vi-Command dispatch tables. Used for setting handlers. - /// - internal static IDisposable UseViCommandModeTables() - { - var oldDispatchTable = _singleton._dispatchTable; - var oldChordDispatchTable = _singleton._chordDispatchTable; - - _singleton._dispatchTable = _viCmdKeyMap; - _singleton._chordDispatchTable = _viCmdChordTable; - - return new Disposable( () => - { - _singleton._dispatchTable = oldDispatchTable; - _singleton._chordDispatchTable = oldChordDispatchTable; - } ); - } - - /// - /// Temporarily swap in Vi-Insert dispatch tables. Used for setting handlers. - /// - internal static IDisposable UseViInsertModeTables() - { - var oldDispatchTable = _singleton._dispatchTable; - var oldChordDispatchTable = _singleton._chordDispatchTable; - - _singleton._dispatchTable = _viInsKeyMap; - _singleton._chordDispatchTable = _viInsChordTable; - - return new Disposable( () => - { - _singleton._dispatchTable = oldDispatchTable; - _singleton._chordDispatchTable = oldChordDispatchTable; - } ); - } - - private void ViIndicateCommandMode() - { - if (_options.ViModeIndicator == ViModeStyle.Cursor) - { - _console.CursorSize = _normalCursorSize < 50 ? 100 : 25; - } - else if (_options.ViModeIndicator == ViModeStyle.Prompt) - { - ConsoleColor savedBackground = _console.BackgroundColor; - _console.BackgroundColor = AlternateBackground(_console.BackgroundColor); - InvokePrompt(); - _console.BackgroundColor = savedBackground; - } - } - - private void ViIndicateInsertMode() - { - if (_options.ViModeIndicator == ViModeStyle.Cursor) - { - _console.CursorSize = _normalCursorSize; - } - else if (_options.ViModeIndicator == ViModeStyle.Prompt) - { - InvokePrompt(); - } - } - - /// - /// Switch to Insert mode and position the cursor at the beginning of the line. - /// - public static void ViInsertAtBegining(ConsoleKeyInfo? key = null, object arg = null) - { - ViInsertMode(key, arg); - BeginningOfLine(key, arg); - } - - /// - /// Switch to Insert mode and position the cursor at the end of the line. - /// - public static void - ViInsertAtEnd(ConsoleKeyInfo? key = null, object arg = null) - { - ViInsertMode(key, arg); - EndOfLine(key, arg); - } - - /// - /// Append from the current line position. - /// - public static void ViInsertWithAppend(ConsoleKeyInfo? key = null, object arg = null) - { - ViInsertMode(key, arg); - ForwardChar(key, arg); - } - - /// - /// Delete the current character and switch to Insert mode. - /// - public static void ViInsertWithDelete(ConsoleKeyInfo? key = null, object arg = null) - { - _singleton._groupUndoHelper.StartGroup(ViInsertWithDelete, arg); - DeleteChar(key, arg); - ViInsertMode(key, arg); - } - - /// - /// Accept the line and switch to Insert mode. - /// - public static void ViAcceptLine(ConsoleKeyInfo? key = null, object arg = null) - { - ViInsertMode(key, arg); - AcceptLine(key, arg); - } - - /// - /// Prepend a '#' and accept the line. - /// - public static void PrependAndAccept(ConsoleKeyInfo? key = null, object arg = null) - { - BeginningOfLine(key, arg); - SelfInsert(key, arg); - ViAcceptLine(key, arg); - } - - /// - /// Invert the case of the current character and move to the next one. - /// - public static void InvertCase(ConsoleKeyInfo? key = null, object arg = null) - { - if (_singleton._current >= _singleton._buffer.Length) - { - Ding(); - return; - } - - int qty = (arg is int) ? (int) arg : 1; - - for (; qty > 0 && _singleton._current < _singleton._buffer.Length; qty--) - { - char c = _singleton._buffer[_singleton._current]; - if (Char.IsLetter(c)) - { - char newChar = Char.IsUpper(c) ? Char.ToLower(c) : char.ToUpper(c); - EditItem delEditItem = EditItemDelete.Create(c.ToString(), _singleton._current); - EditItem insEditItem = EditItemInsertChar.Create(newChar, _singleton._current); - _singleton.SaveEditItem(GroupedEdit.Create(new List - { - delEditItem, - insEditItem - }, - InvertCase, - arg - )); - - _singleton._buffer[_singleton._current] = newChar; - } - _singleton._current = Math.Min(_singleton._current + 1, _singleton._buffer.Length); - _singleton.PlaceCursor(); - } - _singleton.Render(); - } - - /// - /// Swap the current character and the one before it. - /// - public static void SwapCharacters(ConsoleKeyInfo? key = null, object arg = null) - { - if (_singleton._current <= 0 || _singleton._current >= _singleton._buffer.Length) - { - Ding(); - return; - } - - char current = _singleton._buffer[_singleton._current]; - char previous = _singleton._buffer[_singleton._current - 1]; - - _singleton.StartEditGroup(); - _singleton.SaveEditItem(EditItemDelete.Create(_singleton._buffer.ToString(_singleton._current - 1, 2), _singleton._current - 1)); - _singleton.SaveEditItem(EditItemInsertChar.Create(current, _singleton._current - 1)); - _singleton.SaveEditItem(EditItemInsertChar.Create(previous, _singleton._current)); - _singleton.EndEditGroup(); - - _singleton._buffer[_singleton._current] = previous; - _singleton._buffer[_singleton._current - 1] = current; - _singleton._current = Math.Min(_singleton._current + 1, _singleton._buffer.Length - 1); - _singleton.PlaceCursor(); - _singleton.Render(); - } - - /// - /// Deletes text from the cursor to the first non-blank character of the line, - /// - public static void DeleteLineToFirstChar(ConsoleKeyInfo? key = null, object arg = null) - { - if (_singleton._current > 0) - { - int i = 0; - for (; i < _singleton._current; i++) - { - if (!Char.IsWhiteSpace(_singleton._buffer[i])) - { - break; - } - } - - _singleton.SaveToClipboard(i, _singleton._current - i); - _singleton.SaveEditItem(EditItemDelete.Create(_singleton._clipboard, i, DeleteLineToFirstChar)); - - _singleton._buffer.Remove(i, _singleton._current - i); - _singleton._current = i; - _singleton.Render(); - } - else - { - Ding(); - } - } - - /// - /// Deletes the current line, enabling undo. - /// - public static void DeleteLine(ConsoleKeyInfo? key = null, object arg = null) - { - _singleton._clipboard = _singleton._buffer.ToString(); - _singleton.SaveEditItem(EditItemDelete.Create(_singleton._clipboard, 0)); - _singleton._current = 0; - _singleton._buffer.Remove(0, _singleton._buffer.Length); - _singleton.Render(); - } - - /// - /// Deletes the previous word. - /// - public static void BackwardDeleteWord(ConsoleKeyInfo? key = null, object arg = null) - { - int qty = (arg is int) ? (int) arg : 1; - int deletePoint = _singleton._current; - for (int i = 0; i < qty; i++) - { - deletePoint = _singleton.ViFindPreviousWordPoint(deletePoint, _singleton.Options.WordDelimiters); - } - if (deletePoint == _singleton._current) - { - Ding(); - return; - } - _singleton._clipboard = _singleton._buffer.ToString(deletePoint, _singleton._current - deletePoint); - _singleton.SaveEditItem(EditItemDelete.Create( - _singleton._clipboard, - deletePoint, - BackwardDeleteWord, - arg - )); - _singleton._buffer.Remove(deletePoint, _singleton._current - deletePoint); - _singleton._current = deletePoint; - _singleton.Render(); - } - - /// - /// Deletes the previous word, using only white space as the word delimiter. - /// - public static void ViBackwardDeleteGlob(ConsoleKeyInfo? key = null, object arg = null) - { - if (_singleton._current == 0) - { - Ding(); - return; - } - int qty = (arg is int) ? (int)arg : 1; - int deletePoint = _singleton._current; - for (int i = 0; i < qty && deletePoint > 0; i++) - { - deletePoint = _singleton.ViFindPreviousGlob(deletePoint - 1); - } - if (deletePoint == _singleton._current) - { - Ding(); - return; - } - _singleton._clipboard = _singleton._buffer.ToString(deletePoint, _singleton._current - deletePoint); - _singleton.SaveEditItem(EditItemDelete.Create( - _singleton._clipboard, - deletePoint, - BackwardDeleteWord, - arg - )); - _singleton._buffer.Remove(deletePoint, _singleton._current - deletePoint); - _singleton._current = deletePoint; - _singleton.Render(); - } - - /// - /// Find the matching brace, paren, or square bracket and delete all contents within, including the brace. - /// - public static void ViDeleteBrace(ConsoleKeyInfo? key = null, object arg = null) - { - int newCursor = _singleton.ViFindBrace(_singleton._current); - - if (_singleton._current < newCursor) - { - DeleteRange(_singleton._current, newCursor, ViDeleteBrace); - } - else if (newCursor < _singleton._current) - { - DeleteRange(newCursor, _singleton._current, ViDeleteBrace); - } - else - { - Ding(); - } - } - - /// - /// Delete all characters included in the supplied range. - /// - /// Index of where to begin the delete. - /// Index of where to end the delete. - /// Action that generated this request, used for repeat command ('.'). - private static void DeleteRange(int first, int last, Action action) - { - int length = last - first + 1; - - _singleton.SaveToClipboard(first, length); - _singleton.SaveEditItem(EditItemDelete.Create(_singleton._clipboard, first, action)); - _singleton._current = first; - _singleton._buffer.Remove(first, length); - _singleton.Render(); - } - - - /// - /// Prompts for a search string and initiates search upon AcceptLine. - /// - public static void ViSearchHistoryBackward(ConsoleKeyInfo? key = null, object arg = null) - { - if (!key.HasValue || char.IsControl(key.Value.KeyChar)) - { - Ding(); - return; - } - - _singleton.StartSearch(backward: true); - } - - /// - /// Prompts for a search string and initiates search upon AcceptLine. - /// - public static void SearchForward(ConsoleKeyInfo? key = null, object arg = null) - { - if (!key.HasValue || char.IsControl(key.Value.KeyChar)) - { - Ding(); - return; - } - - _singleton.StartSearch(backward: false); - } - - /// - /// Repeat the last search in the same direction as before. - /// - public static void RepeatSearch(ConsoleKeyInfo? key = null, object arg = null) - { - if (string.IsNullOrEmpty(_singleton._searchHistoryPrefix)) - { - Ding(); - return; - } - - _singleton.HistorySearch(); - } - - /// - /// Repeat the last search in the same direction as before. - /// - public static void RepeatSearchBackward(ConsoleKeyInfo? key = null, object arg = null) - { - _singleton._searchHistoryBackward = !_singleton._searchHistoryBackward; - RepeatSearch(); - _singleton._searchHistoryBackward = !_singleton._searchHistoryBackward; - } - - /// - /// Prompts for a string for history searching. - /// - /// True for searching backward in the history. - private void StartSearch(bool backward) - { - _statusLinePrompt = "find: "; - var argBuffer = _statusBuffer; - Render(); // Render prompt - - while (true) - { - var nextKey = ReadKey(); - if (nextKey.Key == Keys.Enter.Key || nextKey.Key == Keys.Tab.Key) - { - _searchHistoryPrefix = argBuffer.ToString(); - _searchHistoryBackward = backward; - HistorySearch(); - break; - } - if (nextKey.Key == Keys.Escape.Key) - { - break; - } - if (nextKey.Key == Keys.Backspace.Key) - { - if (argBuffer.Length > 0) - { - argBuffer.Remove(argBuffer.Length - 1, 1); - Render(); // Render prompt - continue; - } - break; - } - argBuffer.Append(nextKey.KeyChar); - Render(); // Render prompt - } - - // Remove our status line - argBuffer.Clear(); - _statusLinePrompt = null; - Render(); // Render prompt - } - - /// - /// Searches line history. - /// - private void HistorySearch() - { - _searchHistoryCommandCount++; - - int incr = _searchHistoryBackward ? -1 : +1; - for (int i = _currentHistoryIndex + incr; i >= 0 && i < _history.Count; i += incr) - { - if (Options.HistoryStringComparison.HasFlag(StringComparison.OrdinalIgnoreCase)) - { - if (_history[i]._line.ToLower().Contains(_searchHistoryPrefix.ToLower())) - { - _currentHistoryIndex = i; - UpdateFromHistory(moveCursor: Options.HistorySearchCursorMovesToEnd); - return; - } - } - else - { - if (_history[i]._line.Contains(_searchHistoryPrefix)) - { - _currentHistoryIndex = i; - UpdateFromHistory(moveCursor: Options.HistorySearchCursorMovesToEnd); - return; - } - } - } - - Ding(); - } - - /// - /// Repeat the last text modification. - /// - public static void RepeatLastCommand(ConsoleKeyInfo? key = null, object arg = null) - { - if (_singleton._undoEditIndex > 0) - { - EditItem editItem = _singleton._edits[_singleton._undoEditIndex - 1]; - if (editItem._instigator != null) - { - editItem._instigator(key, editItem._instigatorArg); - return; - } - } - Ding(); - } - - /// - /// Chords in vi needs special handling because a numeric argument can be input between the 1st and 2nd key. - /// - /// - /// - private static void ViChord(ConsoleKeyInfo? key = null, object arg = null) - { - if (!key.HasValue) - { - throw new ArgumentNullException("key"); - } - if (arg != null) - { - Chord(key, arg); - return; - } - - Dictionary secondKeyDispatchTable; - if (_singleton._chordDispatchTable.TryGetValue(key.Value, out secondKeyDispatchTable)) - { - //if (_singleton._demoMode) - //{ - // // Render so the first key of the chord appears in the demo window - // _singleton.Render(); - //} - var secondKey = ReadKey(); - KeyHandler handler; - if (secondKeyDispatchTable.TryGetValue(secondKey, out handler)) - { - _singleton.ProcessOneKey(secondKey, secondKeyDispatchTable, ignoreIfNoAction: true, arg: arg); - } - else if (!IsNumberic(secondKey)) - { - _singleton.ProcessOneKey(secondKey, secondKeyDispatchTable, ignoreIfNoAction: true, arg: arg); - } - else - { - var argBuffer = _singleton._statusBuffer; - argBuffer.Clear(); - _singleton._statusLinePrompt = "digit-argument: "; - while (IsNumberic(secondKey)) - { - argBuffer.Append(secondKey.KeyChar); - _singleton.Render(); - secondKey = ReadKey(); - } - int numericArg = int.Parse(argBuffer.ToString()); - if (secondKeyDispatchTable.TryGetValue(secondKey, out handler)) - { - _singleton.ProcessOneKey(secondKey, secondKeyDispatchTable, ignoreIfNoAction: true, arg: numericArg); - } - else - { - Ding(); - } - argBuffer.Clear(); - _singleton.ClearStatusMessage(render: true); - } - } - } - - private static bool IsNumberic(ConsoleKeyInfo key) - { - return char.IsNumber(key.KeyChar); - } - - /// - /// Start a new digit argument to pass to other functions while in one of vi's chords. - /// - public static void ViDigitArgumentInChord(ConsoleKeyInfo? key = null, object arg = null) - { - if (!key.HasValue || char.IsControl(key.Value.KeyChar)) - { - Ding(); - return; - } - - #region VI special case - if (_singleton._options.EditMode == EditMode.Vi && key.Value.KeyChar == '0') - { - BeginningOfLine(); - return; - } - #endregion VI special case - - bool sawDigit = false; - _singleton._statusLinePrompt = "digit-argument: "; - var argBuffer = _singleton._statusBuffer; - argBuffer.Append(key.Value.KeyChar); - if (key.Value.KeyChar == '-') - { - argBuffer.Append('1'); - } - else - { - sawDigit = true; - } - - _singleton.Render(); // Render prompt - while (true) - { - var nextKey = ReadKey(); - KeyHandler handler; - if (_singleton._dispatchTable.TryGetValue(nextKey, out handler) && handler.Action == DigitArgument) - { - if (nextKey.KeyChar == '-') - { - if (argBuffer[0] == '-') - { - argBuffer.Remove(0, 1); - } - else - { - argBuffer.Insert(0, '-'); - } - _singleton.Render(); // Render prompt - continue; - } - - if (nextKey.KeyChar >= '0' && nextKey.KeyChar <= '9') - { - if (!sawDigit && argBuffer.Length > 0) - { - // Buffer is either '-1' or '1' from one or more Alt+- keys - // but no digits yet. Remove the '1'. - argBuffer.Length -= 1; - } - sawDigit = true; - argBuffer.Append(nextKey.KeyChar); - _singleton.Render(); // Render prompt - continue; - } - } - - int intArg; - if (int.TryParse(argBuffer.ToString(), out intArg)) - { - _singleton.ProcessOneKey(nextKey, _singleton._dispatchTable, ignoreIfNoAction: false, arg: intArg); - } - else - { - Ding(); - } - break; - } - } - - /// - /// Like DeleteCharOrExit in Emacs mode, but accepts the line instead of deleting a character. - /// - public static void ViAcceptLineOrExit(ConsoleKeyInfo? key = null, object arg = null) - { - if (_singleton._buffer.Length > 0) - { - _singleton.AcceptLineImpl(false); - } - else - { - ViExit(key, arg); - } - } - - /// - /// A new line is inserted above the current line. - /// - [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")] - public static void ViInsertLine(ConsoleKeyInfo? key = null, object arg = null) - { - _singleton._groupUndoHelper.StartGroup(ViInsertLine, arg); - _singleton.MoveToBeginningOfPhrase(); - _singleton._buffer.Insert(_singleton._current, '\n'); - //_singleton._current = Math.Max(0, _singleton._current - 1); - _singleton.SaveEditItem(EditItemInsertChar.Create( '\n', _singleton._current)); - _singleton.Render(); - ViInsertMode(); - } - - private void MoveToBeginningOfPhrase() - { - while (!IsAtBeginningOfPhrase()) - { - _current--; - } - } - - private bool IsAtBeginningOfPhrase() - { - if (_current == 0) - { - return true; - } - if (_buffer[_current - 1] == '\n') - { - return true; - } - return false; - } - - /// - /// A new line is inserted below the current line. - /// - [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")] - public static void ViAppendLine(ConsoleKeyInfo? key = null, object arg = null) - { - _singleton._groupUndoHelper.StartGroup(ViInsertLine, arg); - _singleton.MoveToEndOfPhrase(); - int insertPoint = 0; - if (_singleton.IsAtEndOfLine(_singleton._current)) - { - insertPoint = _singleton._buffer.Length; - _singleton._buffer.Append('\n'); - _singleton._current = insertPoint; - } - else - { - insertPoint = _singleton._current + 1; - _singleton._buffer.Insert(insertPoint, '\n'); - } - _singleton.SaveEditItem(EditItemInsertChar.Create('\n', insertPoint)); - _singleton.Render(); - ViInsertWithAppend(); - } - - private void MoveToEndOfPhrase() - { - while (!IsAtEndOfPhrase()) - { - _current++; - } - } - - private bool IsAtEndOfPhrase() - { - if (_buffer.Length == 0 || _current == _buffer.Length + ViEndOfLineFactor) - { - return true; - } - if (_buffer[_current] == '\n') - { - return true; - } - return false; - } - - /// - /// Joins 2 lines. - /// - [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")] - public static void ViJoinLines(ConsoleKeyInfo? key = null, object arg = null) - { - _singleton.MoveToEndOfPhrase(); - if (_singleton.IsAtEndOfLine(_singleton._current)) - { - Ding(); - } - else - { - _singleton._buffer[_singleton._current] = ' '; - _singleton._groupUndoHelper.StartGroup(ViJoinLines, arg); - _singleton.SaveEditItem(EditItemDelete.Create("\n", _singleton._current)); - _singleton.SaveEditItem(EditItemInsertChar.Create(' ', _singleton._current)); - _singleton._groupUndoHelper.EndGroup(); - _singleton.Render(); - } - } - } -} diff --git a/src/Microsoft.PowerShell.PSReadLine/Render.cs b/src/Microsoft.PowerShell.PSReadLine/Render.cs deleted file mode 100644 index e0aec994daf..00000000000 --- a/src/Microsoft.PowerShell.PSReadLine/Render.cs +++ /dev/null @@ -1,815 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -using System; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; -using System.Linq; -using System.Management.Automation.Language; -using Microsoft.PowerShell.Internal; - -namespace Microsoft.PowerShell -{ - public partial class PSConsoleReadLine - { -#if UNIX - private const ConsoleColor UnknownColor = (ConsoleColor) (-1); -#endif - private BufferChar[] _consoleBuffer; - private int _initialX; - private int _initialY; - private int _bufferWidth; - private ConsoleColor _initialBackgroundColor; - private ConsoleColor _initialForegroundColor; - private BufferChar _space; - private int _current; - private int _emphasisStart; - private int _emphasisLength; - - private class SavedTokenState - { - internal Token[] Tokens { get; set; } - internal int Index { get; set; } - internal ConsoleColor BackgroundColor { get; set; } - internal ConsoleColor ForegroundColor { get; set; } - } - - private void MaybeParseInput() - { - if (_tokens == null) - { - ParseInput(); - } - } - - private string ParseInput() - { - var text = _buffer.ToString(); - _ast = Parser.ParseInput(text, out _tokens, out _parseErrors); - return text; - } - - private void ClearStatusMessage(bool render) - { - _statusBuffer.Clear(); - _statusLinePrompt = null; - _statusIsErrorMessage = false; - if (render) - { - Render(); - } - } - - private void Render() - { - // If there are a bunch of keys queued up, skip rendering if we've rendered - // recently. - if (_queuedKeys.Count > 10 && (_lastRenderTime.ElapsedMilliseconds < 50)) - { - // We won't render, but most likely the tokens will be different, so make - // sure we don't use old tokens. - _tokens = null; - _ast = null; - return; - } - - ReallyRender(); - } - - [SuppressMessage("Microsoft.Usage", "CA1806:DoNotIgnoreMethodResults")] - private void ReallyRender() - { - var text = ParseInput(); - - int statusLineCount = GetStatusLineCount(); - int j = _initialX + (_bufferWidth * Options.ExtraPromptLineCount); - var backgroundColor = _initialBackgroundColor; - var foregroundColor = _initialForegroundColor; - bool afterLastToken = false; - int totalBytes = j; - int bufferWidth = _console.BufferWidth; - - var tokenStack = new Stack(); - tokenStack.Push(new SavedTokenState - { - Tokens = _tokens, - Index = 0, - BackgroundColor = _initialBackgroundColor, - ForegroundColor = _initialForegroundColor - }); - - int bufferLineCount; - - try - { - _console.StartRender(); - bufferLineCount = ConvertOffsetToCoordinates(text.Length).Y - _initialY + 1 + statusLineCount; - if (_consoleBuffer.Length != bufferLineCount * bufferWidth) - { - var newBuffer = new BufferChar[bufferLineCount * bufferWidth]; - Array.Copy(_consoleBuffer, newBuffer, _initialX + (Options.ExtraPromptLineCount * _bufferWidth)); - if (_consoleBuffer.Length > bufferLineCount * bufferWidth) - { - int consoleBufferOffset = ConvertOffsetToConsoleBufferOffset(text.Length, _initialX + (Options.ExtraPromptLineCount * _bufferWidth)); - // Need to erase the extra lines that we won't draw again - for (int i = consoleBufferOffset; i < _consoleBuffer.Length; i++) - { - _consoleBuffer[i] = _space; - } - _console.WriteBufferLines(_consoleBuffer, ref _initialY); - } - _consoleBuffer = newBuffer; - } - - for (int i = 0; i < text.Length; i++) - { - totalBytes = totalBytes % bufferWidth; - if (!afterLastToken) - { - // Figure out the color of the character - if it's in a token, - // use the tokens color otherwise use the initial color. - var state = tokenStack.Peek(); - var token = state.Tokens[state.Index]; - - if (i == token.Extent.EndOffset) - { - if (token == state.Tokens[state.Tokens.Length - 1]) - { - tokenStack.Pop(); - if (tokenStack.Count == 0) - { - afterLastToken = true; - token = null; - foregroundColor = _initialForegroundColor; - backgroundColor = _initialBackgroundColor; - } - else - { - state = tokenStack.Peek(); - } - } - - if (!afterLastToken) - { - foregroundColor = state.ForegroundColor; - backgroundColor = state.BackgroundColor; - token = state.Tokens[++state.Index]; - } - } - - if (!afterLastToken && i == token.Extent.StartOffset) - { - GetTokenColors(token, out foregroundColor, out backgroundColor); - - var stringToken = token as StringExpandableToken; - if (stringToken != null) - { - // We might have nested tokens. - if (stringToken.NestedTokens != null && stringToken.NestedTokens.Any()) - { - var tokens = new Token[stringToken.NestedTokens.Count + 1]; - stringToken.NestedTokens.CopyTo(tokens, 0); - // NestedTokens doesn't have an "EOS" token, so we use - // the string literal token for that purpose. - tokens[tokens.Length - 1] = stringToken; - - tokenStack.Push(new SavedTokenState - { - Tokens = tokens, - Index = 0, - BackgroundColor = backgroundColor, - ForegroundColor = foregroundColor - }); - - if (i == tokens[0].Extent.StartOffset) - { - GetTokenColors(tokens[0], out foregroundColor, out backgroundColor); - } - } - } - } - } - - var charToRender = text[i]; - if (charToRender == '\n') - { - while ((j % bufferWidth) != 0) - { - _consoleBuffer[j++] = _space; - } - - for (int k = 0; k < Options.ContinuationPrompt.Length; k++, j++) - { - _consoleBuffer[j].UnicodeChar = Options.ContinuationPrompt[k]; - _consoleBuffer[j].ForegroundColor = Options.ContinuationPromptForegroundColor; - _consoleBuffer[j].BackgroundColor = Options.ContinuationPromptBackgroundColor; - } - } - else - { - int size = LengthInBufferCells(charToRender); - totalBytes += size; - - //if there is no enough space for the character at the edge, fill in spaces at the end and - //put the character to next line. - int filling = totalBytes > bufferWidth ? (totalBytes - bufferWidth) % size : 0; - for (int f = 0; f < filling; f++) - { - _consoleBuffer[j++] = _space; - totalBytes++; - } - - if (char.IsControl(charToRender)) - { - _consoleBuffer[j].UnicodeChar = '^'; - MaybeEmphasize(ref _consoleBuffer[j++], i, foregroundColor, backgroundColor); - _consoleBuffer[j].UnicodeChar = (char)('@' + charToRender); - MaybeEmphasize(ref _consoleBuffer[j++], i, foregroundColor, backgroundColor); - - } -#if !UNIX - else if (size > 1) - { - _consoleBuffer[j].UnicodeChar = charToRender; - _consoleBuffer[j].IsLeadByte = true; - MaybeEmphasize(ref _consoleBuffer[j++], i, foregroundColor, backgroundColor); - _consoleBuffer[j].UnicodeChar = charToRender; - _consoleBuffer[j].IsTrailByte = true; - MaybeEmphasize(ref _consoleBuffer[j++], i, foregroundColor, backgroundColor); - } -#endif - else - { - _consoleBuffer[j].UnicodeChar = charToRender; - MaybeEmphasize(ref _consoleBuffer[j++], i, foregroundColor, backgroundColor); - } - } - } - } - finally - { - _console.EndRender(); - } - - for (; j < (_consoleBuffer.Length - (statusLineCount * _bufferWidth)); j++) - { - _consoleBuffer[j] = _space; - } - - if (_statusLinePrompt != null) - { - foregroundColor = _statusIsErrorMessage ? Options.ErrorForegroundColor : _console.ForegroundColor; - backgroundColor = _statusIsErrorMessage ? Options.ErrorBackgroundColor : _console.BackgroundColor; - - for (int i = 0; i < _statusLinePrompt.Length; i++, j++) - { - _consoleBuffer[j].UnicodeChar = _statusLinePrompt[i]; - _consoleBuffer[j].ForegroundColor = foregroundColor; - _consoleBuffer[j].BackgroundColor = backgroundColor; - } - for (int i = 0; i < _statusBuffer.Length; i++, j++) - { - _consoleBuffer[j].UnicodeChar = _statusBuffer[i]; - _consoleBuffer[j].ForegroundColor = foregroundColor; - _consoleBuffer[j].BackgroundColor = backgroundColor; - } - - for (; j < _consoleBuffer.Length; j++) - { - _consoleBuffer[j] = _space; - } - } - - bool rendered = false; - if (_parseErrors.Length > 0) - { - int promptChar = _initialX - 1 + (_bufferWidth * Options.ExtraPromptLineCount); - - while (promptChar >= 0) - { - var c = _consoleBuffer[promptChar].UnicodeChar; - if (char.IsWhiteSpace(c)) - { - promptChar -= 1; - continue; - } - - ConsoleColor prevColor = _consoleBuffer[promptChar].ForegroundColor; - _consoleBuffer[promptChar].ForegroundColor = ConsoleColor.Red; - _console.WriteBufferLines(_consoleBuffer, ref _initialY); - rendered = true; - _consoleBuffer[promptChar].ForegroundColor = prevColor; - break; - } - } - - if (!rendered) - { - _console.WriteBufferLines(_consoleBuffer, ref _initialY); - } - - PlaceCursor(); - - if ((_initialY + bufferLineCount) > (_console.WindowTop + _console.WindowHeight)) - { -#if !UNIX // TODO: verify this isn't necessary for LINUX - _console.WindowTop = _initialY + bufferLineCount - _console.WindowHeight; -#endif - } - - _lastRenderTime.Restart(); - } - - private int LengthInBufferCells(char c) - { - int length = Char.IsControl(c) ? 1 : 0; - if (c < 256) - { - return length + 1; - } - return _console.LengthInBufferCells(c); - } - - private static void WriteBlankLines(int count, int top) - { - var console = _singleton._console; - var blanks = new BufferChar[count * console.BufferWidth]; - for (int i = 0; i < blanks.Length; i++) - { - blanks[i].BackgroundColor = console.BackgroundColor; - blanks[i].ForegroundColor = console.ForegroundColor; - blanks[i].UnicodeChar = ' '; - } - console.WriteBufferLines(blanks, ref top); - } - - private static BufferChar[] ReadBufferLines(int top, int count) - { - return _singleton._console.ReadBufferLines(top, count); - } - - private void GetTokenColors(Token token, out ConsoleColor foregroundColor, out ConsoleColor backgroundColor) - { - switch (token.Kind) - { - case TokenKind.Comment: - foregroundColor = _options.CommentForegroundColor; - backgroundColor = _options.CommentBackgroundColor; - return; - - case TokenKind.Parameter: - foregroundColor = _options.ParameterForegroundColor; - backgroundColor = _options.ParameterBackgroundColor; - return; - - case TokenKind.Variable: - case TokenKind.SplattedVariable: - foregroundColor = _options.VariableForegroundColor; - backgroundColor = _options.VariableBackgroundColor; - return; - - case TokenKind.StringExpandable: - case TokenKind.StringLiteral: - case TokenKind.HereStringExpandable: - case TokenKind.HereStringLiteral: - foregroundColor = _options.StringForegroundColor; - backgroundColor = _options.StringBackgroundColor; - return; - - case TokenKind.Number: - foregroundColor = _options.NumberForegroundColor; - backgroundColor = _options.NumberBackgroundColor; - return; - } - - if ((token.TokenFlags & TokenFlags.CommandName) != 0) - { - foregroundColor = _options.CommandForegroundColor; - backgroundColor = _options.CommandBackgroundColor; - return; - } - - if ((token.TokenFlags & TokenFlags.Keyword) != 0) - { - foregroundColor = _options.KeywordForegroundColor; - backgroundColor = _options.KeywordBackgroundColor; - return; - } - - if ((token.TokenFlags & (TokenFlags.BinaryOperator | TokenFlags.UnaryOperator | TokenFlags.AssignmentOperator)) != 0) - { - foregroundColor = _options.OperatorForegroundColor; - backgroundColor = _options.OperatorBackgroundColor; - return; - } - - if ((token.TokenFlags & TokenFlags.TypeName) != 0) - { - foregroundColor = _options.TypeForegroundColor; - backgroundColor = _options.TypeBackgroundColor; - return; - } - - if ((token.TokenFlags & TokenFlags.MemberName) != 0) - { - foregroundColor = _options.MemberForegroundColor; - backgroundColor = _options.MemberBackgroundColor; - return; - } - - foregroundColor = _options.DefaultTokenForegroundColor; - backgroundColor = _options.DefaultTokenBackgroundColor; - } - - private void GetRegion(out int start, out int length) - { - if (_mark < _current) - { - start = _mark; - length = _current - start; - } - else - { - start = _current; - length = _mark - start; - } - } - - private bool InRegion(int i) - { - int start, end; - if (_mark > _current) - { - start = _current; - end = _mark; - } - else - { - start = _mark; - end = _current; - } - return i >= start && i < end; - } - - private void MaybeEmphasize(ref BufferChar charInfo, int i, ConsoleColor foregroundColor, ConsoleColor backgroundColor) - { - if (i >= _emphasisStart && i < (_emphasisStart + _emphasisLength)) - { - backgroundColor = _options.EmphasisBackgroundColor; - foregroundColor = _options.EmphasisForegroundColor; - } - else if (_visualSelectionCommandCount > 0 && InRegion(i)) - { - // We can't quite emulate real console selection because it inverts - // based on actual screen colors, our palette is limited. The choice - // to invert only the lower 3 bits to change the color is somewhat - // but looks best with the 2 default color schemes - starting PowerShell - // from it's shortcut or from a cmd shortcut. -#if UNIX // TODO: set Inverse attribute and let render choose what to do. - ConsoleColor tempColor = (foregroundColor == UnknownColor) ? ConsoleColor.White : foregroundColor; - foregroundColor = (backgroundColor == UnknownColor) ? ConsoleColor.Black : backgroundColor; - backgroundColor = tempColor; -#else - foregroundColor = (ConsoleColor)((int)foregroundColor ^ 7); - backgroundColor = (ConsoleColor)((int)backgroundColor ^ 7); -#endif - } - - charInfo.ForegroundColor = foregroundColor; - charInfo.BackgroundColor = backgroundColor; - } - - private void PlaceCursor(int x, ref int y) - { - int statusLineCount = GetStatusLineCount(); - if ((y + statusLineCount) >= _console.BufferHeight) - { - _console.ScrollBuffer((y + statusLineCount) - _console.BufferHeight + 1); - y = _console.BufferHeight - 1; - } - _console.SetCursorPosition(x, y); - } - - private void PlaceCursor() - { - var coordinates = ConvertOffsetToCoordinates(_current); - int y = coordinates.Y; - PlaceCursor(coordinates.X, ref y); - } - - private COORD ConvertOffsetToCoordinates(int offset) - { - int x = _initialX; - int y = _initialY + Options.ExtraPromptLineCount; - - int bufferWidth = _console.BufferWidth; - var continuationPromptLength = Options.ContinuationPrompt.Length; - - for (int i = 0; i < offset; i++) - { - char c = _buffer[i]; - if (c == '\n') - { - y += 1; - x = continuationPromptLength; - } - else - { - int size = LengthInBufferCells(c); - x += size; - // Wrap? No prompt when wrapping - if (x >= bufferWidth) - { - int offsize = x - bufferWidth; - if (offsize % size == 0) - { - x -= bufferWidth; - } - else - { - x = size; - } - y += 1; - } - } - } - - //if the next character has bigger size than the remain space on this line, - //the cursor goes to next line where the next character is. - if (_buffer.Length > offset) - { - int size = LengthInBufferCells(_buffer[offset]); - // next one is Wrapped to next line - if (x + size > bufferWidth && (x + size - bufferWidth) % size != 0) - { - x = 0; - y++; - } - } - - return new COORD {X = (short)x, Y = (short)y}; - } - - private int ConvertOffsetToConsoleBufferOffset(int offset, int startIndex) - { - int j = startIndex; - for (int i = 0; i < offset; i++) - { - var c = _buffer[i]; - if (c == '\n') - { - for (int k = 0; k < Options.ContinuationPrompt.Length; k++) - { - j++; - } - } - else if (LengthInBufferCells(c) > 1) - { - j += 2; - } - else - { - j++; - } - } - return j; - } - - private int ConvertLineAndColumnToOffset(COORD coord) - { - int offset; - int x = _initialX; - int y = _initialY + Options.ExtraPromptLineCount; - - int bufferWidth = _console.BufferWidth; - var continuationPromptLength = Options.ContinuationPrompt.Length; - for (offset = 0; offset < _buffer.Length; offset++) - { - // If we are on the correct line, return when we find - // the correct column - if (coord.Y == y && coord.X <= x) - { - return offset; - } - char c = _buffer[offset]; - if (c == '\n') - { - // If we are about to move off of the correct line, - // the line was shorter than the column we wanted so return. - if (coord.Y == y) - { - return offset; - } - y += 1; - x = continuationPromptLength; - } - else - { - int size = LengthInBufferCells(c); - x += size; - // Wrap? No prompt when wrapping - if (x >= bufferWidth) - { - int offsize = x - bufferWidth; - if (offsize % size == 0) - { - x -= bufferWidth; - } - else - { - x = size; - } - y += 1; - } - } - } - - // Return -1 if y is out of range, otherwise the last line was shorter - // than we wanted, but still in range so just return the last offset. - return (coord.Y == y) ? offset : -1; - } - - private bool LineIsMultiLine() - { - for (int i = 0; i < _buffer.Length; i++) - { - if (_buffer[i] == '\n') - return true; - } - return false; - } - - private int GetStatusLineCount() - { - if (_statusLinePrompt == null) - return 0; - - return (_statusLinePrompt.Length + _statusBuffer.Length) / _console.BufferWidth + 1; - } - - [ExcludeFromCodeCoverage] - void IPSConsoleReadLineMockableMethods.Ding() - { - switch (Options.BellStyle) - { - case BellStyle.None: - break; - case BellStyle.Audible: -#if UNIX - Console.Beep(); -#else - Console.Beep(Options.DingTone, Options.DingDuration); -#endif - break; - case BellStyle.Visual: - // TODO: flash prompt? command line? - break; - } - } - - /// - /// Notify the user based on their preference for notification. - /// - public static void Ding() - { - _singleton._mockableMethods.Ding(); - } - - private bool PromptYesOrNo(string s) - { - _statusLinePrompt = s; - Render(); - - var key = ReadKey(); - - _statusLinePrompt = null; - Render(); - return key.Key == ConsoleKey.Y; - } - -#region Screen scrolling - -#if !UNIX - /// - /// Scroll the display up one screen. - /// - [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")] - public static void ScrollDisplayUp(ConsoleKeyInfo? key = null, object arg = null) - { - int numericArg; - TryGetArgAsInt(arg, out numericArg, +1); - var console = _singleton._console; - var newTop = console.WindowTop - (numericArg * console.WindowHeight); - if (newTop < 0) - { - newTop = 0; - } - console.SetWindowPosition(0, newTop); - } - - /// - /// Scroll the display up one line. - /// - [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")] - public static void ScrollDisplayUpLine(ConsoleKeyInfo? key = null, object arg = null) - { - int numericArg; - TryGetArgAsInt(arg, out numericArg, +1); - var console = _singleton._console; - var newTop = console.WindowTop - numericArg; - if (newTop < 0) - { - newTop = 0; - } - console.SetWindowPosition(0, newTop); - } - - /// - /// Scroll the display down one screen. - /// - [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")] - public static void ScrollDisplayDown(ConsoleKeyInfo? key = null, object arg = null) - { - int numericArg; - TryGetArgAsInt(arg, out numericArg, +1); - var console = _singleton._console; - var newTop = console.WindowTop + (numericArg * console.WindowHeight); - if (newTop > (console.BufferHeight - console.WindowHeight)) - { - newTop = (console.BufferHeight - console.WindowHeight); - } - console.SetWindowPosition(0, newTop); - } - - /// - /// Scroll the display down one line. - /// - [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")] - public static void ScrollDisplayDownLine(ConsoleKeyInfo? key = null, object arg = null) - { - int numericArg; - TryGetArgAsInt(arg, out numericArg, +1); - var console = _singleton._console; - var newTop = console.WindowTop + numericArg; - if (newTop > (console.BufferHeight - console.WindowHeight)) - { - newTop = (console.BufferHeight - console.WindowHeight); - } - console.SetWindowPosition(0, newTop); - } - - /// - /// Scroll the display to the top. - /// - [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")] - public static void ScrollDisplayTop(ConsoleKeyInfo? key = null, object arg = null) - { - _singleton._console.SetWindowPosition(0, 0); - } - - /// - /// Scroll the display to the cursor. - /// - [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")] - public static void ScrollDisplayToCursor(ConsoleKeyInfo? key = null, object arg = null) - { - // Ideally, we'll put the last input line at the bottom of the window - var coordinates = _singleton.ConvertOffsetToCoordinates(_singleton._buffer.Length); - - var console = _singleton._console; - var newTop = coordinates.Y - console.WindowHeight + 1; - - // If the cursor is already visible, and we're on the first - // page-worth of the buffer, then just scroll to the top (we can't - // scroll to before the beginning of the buffer). - // - // Note that we don't want to just return, because the window may - // have been scrolled way past the end of the content, so we really - // do need to set the new window top to 0 to bring it back into - // view. - if (newTop < 0) - { - newTop = 0; - } - - // But if the cursor won't be visible, make sure it is. - if (newTop > console.CursorTop) - { - // Add 10 for some extra context instead of putting the - // cursor on the bottom line. - newTop = console.CursorTop - console.WindowHeight + 10; - } - - // But we can't go past the end of the buffer. - if (newTop > (console.BufferHeight - console.WindowHeight)) - { - newTop = (console.BufferHeight - console.WindowHeight); - } - console.SetWindowPosition(0, newTop); - } - -#endif -#endregion Screen scrolling - } -} diff --git a/src/Microsoft.PowerShell.PSReadLine/Replace.vi.cs b/src/Microsoft.PowerShell.PSReadLine/Replace.vi.cs deleted file mode 100644 index cf8c39e750c..00000000000 --- a/src/Microsoft.PowerShell.PSReadLine/Replace.vi.cs +++ /dev/null @@ -1,350 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; -using System.IO; -using System.Linq; -using System.Management.Automation; -using System.Management.Automation.Language; -using System.Runtime.InteropServices; -using System.Text; -using System.Text.RegularExpressions; -using System.Threading; - -namespace Microsoft.PowerShell -{ - public partial class PSConsoleReadLine - { - private static void ViReplaceUntilEsc(ConsoleKeyInfo? key, object arg) - { - if (_singleton._current >= _singleton._buffer.Length) - { - Ding(); - return; - } - - int startingCursor = _singleton._current; - int maxDeleteLength = _singleton._buffer.Length - _singleton._current; - StringBuilder deletedStr = new StringBuilder(); - - ConsoleKeyInfo nextKey = ReadKey(); - while (nextKey.Key != ConsoleKey.Escape && nextKey.Key != ConsoleKey.Enter) - { - if (nextKey.Key != ConsoleKey.Backspace && nextKey.KeyChar != '\u0000') - { - if (_singleton._current >= _singleton._buffer.Length) - { - _singleton._buffer.Append(nextKey.KeyChar); - } - else - { - deletedStr.Append(_singleton._buffer[_singleton._current]); - _singleton._buffer[_singleton._current] = nextKey.KeyChar; - } - _singleton._current++; - _singleton.Render(); - } - if (nextKey.Key == ConsoleKey.Backspace) - { - if (_singleton._current == startingCursor) - { - Ding(); - } - else - { - if (deletedStr.Length == _singleton._current - startingCursor) - { - _singleton._buffer[_singleton._current - 1] = deletedStr[deletedStr.Length - 1]; - deletedStr.Remove(deletedStr.Length - 1, 1); - } - else - { - _singleton._buffer.Remove(_singleton._current - 1, 1); - } - _singleton._current--; - _singleton.Render(); - } - } - nextKey = ReadKey(); - } - - if (_singleton._current > startingCursor) - { - _singleton.StartEditGroup(); - string insStr = _singleton._buffer.ToString(startingCursor, _singleton._current - startingCursor); - _singleton.SaveEditItem(EditItemDelete.Create(deletedStr.ToString(), startingCursor)); - _singleton.SaveEditItem(EditItemInsertString.Create(insStr, startingCursor)); - _singleton.EndEditGroup(); - } - - if (nextKey.Key == ConsoleKey.Enter) - { - ViAcceptLine(nextKey); - } - } - - private static void ViReplaceBrace(ConsoleKeyInfo? key, object arg) - { - _singleton._groupUndoHelper.StartGroup(ViReplaceBrace, arg); - ViDeleteBrace(key, arg); - ViInsertMode(key, arg); - } - - private static void ViBackwardReplaceLineToFirstChar(ConsoleKeyInfo? key, object arg) - { - _singleton._groupUndoHelper.StartGroup(ViBackwardReplaceLineToFirstChar, arg); - DeleteLineToFirstChar(key, arg); - ViInsertMode(key, arg); - } - - private static void ViBackwardReplaceLine(ConsoleKeyInfo? key, object arg) - { - _singleton._groupUndoHelper.StartGroup(ViBackwardReplaceLine, arg); - BackwardDeleteLine(key, arg); - ViInsertMode(key, arg); - } - - private static void BackwardReplaceChar(ConsoleKeyInfo? key, object arg) - { - _singleton._groupUndoHelper.StartGroup(BackwardReplaceChar, arg); - BackwardDeleteChar(key, arg); - ViInsertMode(key, arg); - } - - private static void ViBackwardReplaceWord(ConsoleKeyInfo? key, object arg) - { - _singleton._groupUndoHelper.StartGroup(ViBackwardReplaceWord, arg); - BackwardDeleteWord(key, arg); - ViInsertMode(key, arg); - } - - private static void ViBackwardReplaceGlob(ConsoleKeyInfo? key, object arg) - { - _singleton._groupUndoHelper.StartGroup(ViBackwardReplaceGlob, arg); - ViBackwardDeleteGlob(key, arg); - ViInsertMode(key, arg); - } - - private static void ViReplaceToEnd(ConsoleKeyInfo? key, object arg) - { - _singleton._groupUndoHelper.StartGroup(ViReplaceToEnd, arg); - DeleteToEnd(key, arg); - _singleton._current = Math.Min(_singleton._buffer.Length, _singleton._current + 1); - _singleton.PlaceCursor(); - ViInsertMode(key, arg); - } - - private static void ViReplaceLine(ConsoleKeyInfo? key, object arg) - { - _singleton._groupUndoHelper.StartGroup(ViReplaceLine, arg); - DeleteLine(key, arg); - ViInsertMode(key, arg); - } - - private static void ViReplaceWord(ConsoleKeyInfo? key, object arg) - { - _singleton._groupUndoHelper.StartGroup(ViReplaceWord, arg); - _singleton._lastWordDelimiter = char.MinValue; - _singleton._shouldAppend = false; - DeleteWord(key, arg); - if (_singleton._current < _singleton._buffer.Length - 1) - { - if (char.IsWhiteSpace(_singleton._lastWordDelimiter)) - { - Insert(_singleton._lastWordDelimiter); - _singleton._current--; - } - _singleton._lastWordDelimiter = char.MinValue; - _singleton.PlaceCursor(); - } - if (_singleton._current == _singleton._buffer.Length - 1 - && !_singleton.IsDelimiter(_singleton._lastWordDelimiter, _singleton.Options.WordDelimiters) - && _singleton._shouldAppend) - { - ViInsertWithAppend(key, arg); - } - else - { - ViInsertMode(key, arg); - } - } - - private static void ViReplaceGlob(ConsoleKeyInfo? key, object arg) - { - _singleton._groupUndoHelper.StartGroup(ViReplaceGlob, arg); - ViDeleteGlob(key, arg); - if (_singleton._current < _singleton._buffer.Length - 1) - { - Insert(' '); - _singleton._current--; - _singleton.PlaceCursor(); - } - if (_singleton._current == _singleton._buffer.Length - 1) - { - ViInsertWithAppend(key, arg); - } - else - { - ViInsertMode(key, arg); - } - } - - private static void ViReplaceEndOfWord(ConsoleKeyInfo? key, object arg) - { - _singleton._groupUndoHelper.StartGroup(ViReplaceEndOfWord, arg); - DeleteEndOfWord(key, arg); - if (_singleton._current == _singleton._buffer.Length - 1) - { - ViInsertWithAppend(key, arg); - } - else - { - ViInsertMode(key, arg); - } - } - - private static void ViReplaceEndOfGlob(ConsoleKeyInfo? key, object arg) - { - _singleton._groupUndoHelper.StartGroup(ViReplaceEndOfGlob, arg); - ViDeleteEndOfGlob(key, arg); - if (_singleton._current == _singleton._buffer.Length - 1) - { - ViInsertWithAppend(key, arg); - } - else - { - ViInsertMode(key, arg); - } - } - - private static void ReplaceChar(ConsoleKeyInfo? key, object arg) - { - _singleton._groupUndoHelper.StartGroup(ReplaceChar, arg); - DeleteChar(key, arg); - ViInsertMode(key, arg); - } - - /// - /// Replaces the current character with the next character typed. - /// - private static void ReplaceCharInPlace(ConsoleKeyInfo? key, object arg) - { - ConsoleKeyInfo nextKey = ReadKey(); - if (_singleton._buffer.Length > 0 && nextKey.KeyChar > 0 && nextKey.Key != ConsoleKey.Escape && nextKey.Key != ConsoleKey.Enter) - { - _singleton.StartEditGroup(); - _singleton.SaveEditItem(EditItemDelete.Create(_singleton._buffer[_singleton._current].ToString(), _singleton._current)); - _singleton.SaveEditItem(EditItemInsertString.Create(nextKey.KeyChar.ToString(), _singleton._current)); - _singleton.EndEditGroup(); - - _singleton._buffer[_singleton._current] = nextKey.KeyChar; - _singleton.Render(); - } - else - { - Ding(); - } - } - - /// - /// Deletes until given character - /// - /// - /// - [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")] - private static void ViReplaceToChar(ConsoleKeyInfo? key = null, object arg = null) - { - var keyChar = ReadKey().KeyChar; - ViReplaceToChar(keyChar, key, arg); - } - - private static void ViReplaceToChar(char keyChar, ConsoleKeyInfo? key = null, object arg = null) - { - bool shouldAppend = _singleton._current > 0; - - _singleton._groupUndoHelper.StartGroup(ReplaceChar, arg); - ViCharacterSearcher.Set(keyChar, isBackward: false, isBackoff: false); - if (ViCharacterSearcher.SearchDelete(keyChar, arg, backoff: false, instigator: (_key, _arg) => ViReplaceToChar(keyChar, _key, _arg))) - { - if (shouldAppend) - { - ViInsertWithAppend(key, arg); - } - else - { - ViInsertMode(key, arg); - } - } - } - - /// - /// Replaces until given character - /// - /// - /// - [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")] - private static void ViReplaceToCharBackward(ConsoleKeyInfo? key = null, object arg = null) - { - var keyChar = ReadKey().KeyChar; - ViReplaceToCharBack(keyChar, key, arg); - } - - private static void ViReplaceToCharBack(char keyChar, ConsoleKeyInfo? key = null, object arg = null) - { - _singleton._groupUndoHelper.StartGroup(ReplaceChar, arg); - if (ViCharacterSearcher.SearchBackwardDelete(keyChar, arg, backoff: false, instigator: (_key, _arg) => ViReplaceToCharBack(keyChar, _key, _arg))) - { - ViInsertMode(key, arg); - } - } - - /// - /// Replaces until given character - /// - /// - /// - [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")] - private static void ViReplaceToBeforeChar(ConsoleKeyInfo? key = null, object arg = null) - { - var keyChar = ReadKey().KeyChar; - ViReplaceToBeforeChar(keyChar, key, arg); - } - - private static void ViReplaceToBeforeChar(char keyChar, ConsoleKeyInfo? key = null, object arg = null) - { - _singleton._groupUndoHelper.StartGroup(ReplaceChar, arg); - ViCharacterSearcher.Set(keyChar, isBackward: false, isBackoff: true); - if (ViCharacterSearcher.SearchDelete(keyChar, arg, backoff: true, instigator: (_key, _arg) => ViReplaceToBeforeChar(keyChar, _key, _arg))) - { - ViInsertMode(key, arg); - } - } - - /// - /// Replaces until given character - /// - /// - /// - [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")] - private static void ViReplaceToBeforeCharBackward(ConsoleKeyInfo? key = null, object arg = null) - { - var keyChar = ReadKey().KeyChar; - ViReplaceToBeforeCharBack(keyChar, key, arg); - } - - private static void ViReplaceToBeforeCharBack(char keyChar, ConsoleKeyInfo? key = null, object arg = null) - { - _singleton._groupUndoHelper.StartGroup(ReplaceChar, arg); - if (ViCharacterSearcher.SearchBackwardDelete(keyChar, arg, backoff: true, instigator: (_key, _arg) => ViReplaceToBeforeCharBack(keyChar, _key, _arg))) - { - ViInsertMode(key, arg); - } - } - - - } -} diff --git a/src/Microsoft.PowerShell.PSReadLine/SamplePSReadlineProfile.ps1 b/src/Microsoft.PowerShell.PSReadLine/SamplePSReadlineProfile.ps1 deleted file mode 100644 index cfde8a0c817..00000000000 --- a/src/Microsoft.PowerShell.PSReadLine/SamplePSReadlineProfile.ps1 +++ /dev/null @@ -1,491 +0,0 @@ - -# This is an example profile for PSReadline. -# -# This is roughly what I use so there is some emphasis on emacs bindings, -# but most of these bindings make sense in Windows mode as well. - -Import-Module PSReadLine - -Set-PSReadLineOption -EditMode Emacs - -# Searching for commands with up/down arrow is really handy. The -# option "moves to end" is useful if you want the cursor at the end -# of the line while cycling through history like it does w/o searching, -# without that option, the cursor will remain at the position it was -# when you used up arrow, which can be useful if you forget the exact -# string you started the search on. -Set-PSReadLineOption -HistorySearchCursorMovesToEnd -Set-PSReadlineKeyHandler -Key UpArrow -Function HistorySearchBackward -Set-PSReadlineKeyHandler -Key DownArrow -Function HistorySearchForward - -# This key handler shows the entire or filtered history using Out-GridView. The -# typed text is used as the substring pattern for filtering. A selected command -# is inserted to the command line without invoking. Multiple command selection -# is supported, e.g. selected by Ctrl + Click. -Set-PSReadlineKeyHandler -Key F7 ` - -BriefDescription History ` - -LongDescription 'Show command history' ` - -ScriptBlock { - $pattern = $null - [Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref]$pattern, [ref]$null) - if ($pattern) - { - $pattern = [regex]::Escape($pattern) - } - - $history = [System.Collections.ArrayList]@( - $last = '' - $lines = '' - foreach ($line in [System.IO.File]::ReadLines((Get-PSReadlineOption).HistorySavePath)) - { - if ($line.EndsWith('`')) - { - $line = $line.Substring(0, $line.Length - 1) - $lines = if ($lines) - { - "$lines`n$line" - } - else - { - $line - } - continue - } - - if ($lines) - { - $line = "$lines`n$line" - $lines = '' - } - - if (($line -cne $last) -and (!$pattern -or ($line -match $pattern))) - { - $last = $line - $line - } - } - ) - $history.Reverse() - - $command = $history | Out-GridView -Title History -PassThru - if ($command) - { - [Microsoft.PowerShell.PSConsoleReadLine]::RevertLine() - [Microsoft.PowerShell.PSConsoleReadLine]::Insert(($command -join "`n")) - } -} - -# This is an example of a macro that you might use to execute a command. -# This will add the command to history. -Set-PSReadlineKeyHandler -Key Ctrl+B ` - -BriefDescription BuildCurrentDirectory ` - -LongDescription "Build the current directory" ` - -ScriptBlock { - [Microsoft.PowerShell.PSConsoleReadLine]::RevertLine() - [Microsoft.PowerShell.PSConsoleReadLine]::Insert("msbuild") - [Microsoft.PowerShell.PSConsoleReadLine]::AcceptLine() -} - -# In Emacs mode - Tab acts like in bash, but the Windows style completion -# is still useful sometimes, so bind some keys so we can do both -Set-PSReadlineKeyHandler -Key Ctrl+Q -Function TabCompleteNext -Set-PSReadlineKeyHandler -Key Ctrl+Shift+Q -Function TabCompletePrevious - -# Clipboard interaction is bound by default in Windows mode, but not Emacs mode. -Set-PSReadlineKeyHandler -Key Shift+Ctrl+C -Function Copy -Set-PSReadlineKeyHandler -Key Ctrl+V -Function Paste - -# CaptureScreen is good for blog posts or email showing a transaction -# of what you did when asking for help or demonstrating a technique. -Set-PSReadlineKeyHandler -Chord 'Ctrl+D,Ctrl+C' -Function CaptureScreen - -# The built-in word movement uses character delimiters, but token based word -# movement is also very useful - these are the bindings you'd use if you -# prefer the token based movements bound to the normal emacs word movement -# key bindings. -Set-PSReadlineKeyHandler -Key Alt+D -Function ShellKillWord -Set-PSReadlineKeyHandler -Key Alt+Backspace -Function ShellBackwardKillWord -Set-PSReadlineKeyHandler -Key Alt+B -Function ShellBackwardWord -Set-PSReadlineKeyHandler -Key Alt+F -Function ShellForwardWord -Set-PSReadlineKeyHandler -Key Shift+Alt+B -Function SelectShellBackwardWord -Set-PSReadlineKeyHandler -Key Shift+Alt+F -Function SelectShellForwardWord - -#region Smart Insert/Delete - -# The next four key handlers are designed to make entering matched quotes -# parens, and braces a nicer experience. I'd like to include functions -# in the module that do this, but this implementation still isn't as smart -# as ReSharper, so I'm just providing it as a sample. - -Set-PSReadlineKeyHandler -Key '"',"'" ` - -BriefDescription SmartInsertQuote ` - -LongDescription "Insert paired quotes if not already on a quote" ` - -ScriptBlock { - param($key, $arg) - - $line = $null - $cursor = $null - [Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref]$line, [ref]$cursor) - - if ($line[$cursor] -eq $key.KeyChar) { - # Just move the cursor - [Microsoft.PowerShell.PSConsoleReadLine]::SetCursorPosition($cursor + 1) - } - else { - # Insert matching quotes, move cursor to be in between the quotes - [Microsoft.PowerShell.PSConsoleReadLine]::Insert("$($key.KeyChar)" * 2) - [Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref]$line, [ref]$cursor) - [Microsoft.PowerShell.PSConsoleReadLine]::SetCursorPosition($cursor - 1) - } -} - -Set-PSReadlineKeyHandler -Key '(','{','[' ` - -BriefDescription InsertPairedBraces ` - -LongDescription "Insert matching braces" ` - -ScriptBlock { - param($key, $arg) - - $closeChar = switch ($key.KeyChar) - { - <#case#> '(' { [char]')'; break } - <#case#> '{' { [char]'}'; break } - <#case#> '[' { [char]']'; break } - } - - [Microsoft.PowerShell.PSConsoleReadLine]::Insert("$($key.KeyChar)$closeChar") - $line = $null - $cursor = $null - [Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref]$line, [ref]$cursor) - [Microsoft.PowerShell.PSConsoleReadLine]::SetCursorPosition($cursor - 1) -} - -Set-PSReadlineKeyHandler -Key ')',']','}' ` - -BriefDescription SmartCloseBraces ` - -LongDescription "Insert closing brace or skip" ` - -ScriptBlock { - param($key, $arg) - - $line = $null - $cursor = $null - [Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref]$line, [ref]$cursor) - - if ($line[$cursor] -eq $key.KeyChar) - { - [Microsoft.PowerShell.PSConsoleReadLine]::SetCursorPosition($cursor + 1) - } - else - { - [Microsoft.PowerShell.PSConsoleReadLine]::Insert("$($key.KeyChar)") - } -} - -Set-PSReadlineKeyHandler -Key Backspace ` - -BriefDescription SmartBackspace ` - -LongDescription "Delete previous character or matching quotes/parens/braces" ` - -ScriptBlock { - param($key, $arg) - - $line = $null - $cursor = $null - [Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref]$line, [ref]$cursor) - - if ($cursor -gt 0) - { - $toMatch = $null - if ($cursor -lt $line.Length) - { - switch ($line[$cursor]) - { - <#case#> '"' { $toMatch = '"'; break } - <#case#> "'" { $toMatch = "'"; break } - <#case#> ')' { $toMatch = '('; break } - <#case#> ']' { $toMatch = '['; break } - <#case#> '}' { $toMatch = '{'; break } - } - } - - if ($null -ne $toMatch -and $line[$cursor-1] -eq $toMatch) - { - [Microsoft.PowerShell.PSConsoleReadLine]::Delete($cursor - 1, 2) - } - else - { - [Microsoft.PowerShell.PSConsoleReadLine]::BackwardDeleteChar($key, $arg) - } - } -} - -#endregion Smart Insert/Delete - -# Sometimes you enter a command but realize you forgot to do something else first. -# This binding will let you save that command in the history so you can recall it, -# but it doesn't actually execute. It also clears the line with RevertLine so the -# undo stack is reset - though redo will still reconstruct the command line. -Set-PSReadlineKeyHandler -Key Alt+w ` - -BriefDescription SaveInHistory ` - -LongDescription "Save current line in history but do not execute" ` - -ScriptBlock { - param($key, $arg) - - $line = $null - $cursor = $null - [Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref]$line, [ref]$cursor) - [Microsoft.PowerShell.PSConsoleReadLine]::AddToHistory($line) - [Microsoft.PowerShell.PSConsoleReadLine]::RevertLine() -} - -# Insert text from the clipboard as a here string -Set-PSReadlineKeyHandler -Key Ctrl+Shift+v ` - -BriefDescription PasteAsHereString ` - -LongDescription "Paste the clipboard text as a here string" ` - -ScriptBlock { - param($key, $arg) - - Add-Type -Assembly PresentationCore - if ([System.Windows.Clipboard]::ContainsText()) - { - # Get clipboard text - remove trailing spaces, convert \r\n to \n, and remove the final \n. - $text = ([System.Windows.Clipboard]::GetText() -replace "\p{Zs}*`r?`n","`n").TrimEnd() - [Microsoft.PowerShell.PSConsoleReadLine]::Insert("@'`n$text`n'@") - } - else - { - [Microsoft.PowerShell.PSConsoleReadLine]::Ding() - } -} - -# Sometimes you want to get a property of invoke a member on what you've entered so far -# but you need parens to do that. This binding will help by putting parens around the current selection, -# or if nothing is selected, the whole line. -Set-PSReadlineKeyHandler -Key 'Alt+(' ` - -BriefDescription ParenthesizeSelection ` - -LongDescription "Put parenthesis around the selection or entire line and move the cursor to after the closing parenthesis" ` - -ScriptBlock { - param($key, $arg) - - $selectionStart = $null - $selectionLength = $null - [Microsoft.PowerShell.PSConsoleReadLine]::GetSelectionState([ref]$selectionStart, [ref]$selectionLength) - - $line = $null - $cursor = $null - [Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref]$line, [ref]$cursor) - if ($selectionStart -ne -1) - { - [Microsoft.PowerShell.PSConsoleReadLine]::Replace($selectionStart, $selectionLength, '(' + $line.SubString($selectionStart, $selectionLength) + ')') - [Microsoft.PowerShell.PSConsoleReadLine]::SetCursorPosition($selectionStart + $selectionLength + 2) - } - else - { - [Microsoft.PowerShell.PSConsoleReadLine]::Replace(0, $line.Length, '(' + $line + ')') - [Microsoft.PowerShell.PSConsoleReadLine]::EndOfLine() - } -} - -# Each time you press Alt+', this key handler will change the token -# under or before the cursor. It will cycle through single quotes, double quotes, or -# no quotes each time it is invoked. -Set-PSReadlineKeyHandler -Key "Alt+'" ` - -BriefDescription ToggleQuoteArgument ` - -LongDescription "Toggle quotes on the argument under the cursor" ` - -ScriptBlock { - param($key, $arg) - - $ast = $null - $tokens = $null - $errors = $null - $cursor = $null - [Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref]$ast, [ref]$tokens, [ref]$errors, [ref]$cursor) - - $tokenToChange = $null - foreach ($token in $tokens) - { - $extent = $token.Extent - if ($extent.StartOffset -le $cursor -and $extent.EndOffset -ge $cursor) - { - $tokenToChange = $token - - # If the cursor is at the end (it's really 1 past the end) of the previous token, - # we only want to change the previous token if there is no token under the cursor - if ($extent.EndOffset -eq $cursor -and $foreach.MoveNext()) - { - $nextToken = $foreach.Current - if ($nextToken.Extent.StartOffset -eq $cursor) - { - $tokenToChange = $nextToken - } - } - break - } - } - - if ($null -ne $tokenToChange) - { - $extent = $tokenToChange.Extent - $tokenText = $extent.Text - if ($tokenText[0] -eq '"' -and $tokenText[-1] -eq '"') - { - # Switch to no quotes - $replacement = $tokenText.Substring(1, $tokenText.Length - 2) - } - elseif ($tokenText[0] -eq "'" -and $tokenText[-1] -eq "'") - { - # Switch to double quotes - $replacement = '"' + $tokenText.Substring(1, $tokenText.Length - 2) + '"' - } - else - { - # Add single quotes - $replacement = "'" + $tokenText + "'" - } - - [Microsoft.PowerShell.PSConsoleReadLine]::Replace( - $extent.StartOffset, - $tokenText.Length, - $replacement) - } -} - -# This example will replace any aliases on the command line with the resolved commands. -Set-PSReadlineKeyHandler -Key "Alt+%" ` - -BriefDescription ExpandAliases ` - -LongDescription "Replace all aliases with the full command" ` - -ScriptBlock { - param($key, $arg) - - $ast = $null - $tokens = $null - $errors = $null - $cursor = $null - [Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref]$ast, [ref]$tokens, [ref]$errors, [ref]$cursor) - - $startAdjustment = 0 - foreach ($token in $tokens) - { - if ($token.TokenFlags -band [System.Management.Automation.Language.TokenFlags]::CommandName) - { - $alias = $ExecutionContext.InvokeCommand.GetCommand($token.Extent.Text, 'Alias') - if ($null -ne $alias) - { - $resolvedCommand = $alias.ResolvedCommandName - if ($null -ne $resolvedCommand) - { - $extent = $token.Extent - $length = $extent.EndOffset - $extent.StartOffset - [Microsoft.PowerShell.PSConsoleReadLine]::Replace( - $extent.StartOffset + $startAdjustment, - $length, - $resolvedCommand) - - # Our copy of the tokens won't have been updated, so we need to - # adjust by the difference in length - $startAdjustment += ($resolvedCommand.Length - $length) - } - } - } - } -} - -# F1 for help on the command line - naturally -Set-PSReadlineKeyHandler -Key F1 ` - -BriefDescription CommandHelp ` - -LongDescription "Open the help window for the current command" ` - -ScriptBlock { - param($key, $arg) - - $ast = $null - $tokens = $null - $errors = $null - $cursor = $null - [Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref]$ast, [ref]$tokens, [ref]$errors, [ref]$cursor) - - $commandAst = $ast.FindAll( { - $node = $args[0] - $node -is [System.Management.Automation.Language.CommandAst] -and - $node.Extent.StartOffset -le $cursor -and - $node.Extent.EndOffset -ge $cursor - }, $true) | Select-Object -Last 1 - - if ($null -ne $commandAst) - { - $commandName = $commandAst.GetCommandName() - if ($null -ne $commandName) - { - $command = $ExecutionContext.InvokeCommand.GetCommand($commandName, 'All') - if ($command -is [System.Management.Automation.AliasInfo]) - { - $commandName = $command.ResolvedCommandName - } - - if ($null -ne $commandName) - { - Get-Help $commandName -ShowWindow - } - } - } -} - - -# -# Ctrl+Shift+j then type a key to mark the current directory. -# Ctrj+j then the same key will change back to that directory without -# needing to type cd and won't change the command line. - -# -$global:PSReadlineMarks = @{} - -Set-PSReadlineKeyHandler -Key Ctrl+Shift+j ` - -BriefDescription MarkDirectory ` - -LongDescription "Mark the current directory" ` - -ScriptBlock { - param($key, $arg) - - $key = [Console]::ReadKey($true) - $global:PSReadlineMarks[$key.KeyChar] = $pwd -} - -Set-PSReadlineKeyHandler -Key Ctrl+j ` - -BriefDescription JumpDirectory ` - -LongDescription "Goto the marked directory" ` - -ScriptBlock { - param($key, $arg) - - $key = [Console]::ReadKey() - $dir = $global:PSReadlineMarks[$key.KeyChar] - if ($dir) - { - cd $dir - [Microsoft.PowerShell.PSConsoleReadLine]::InvokePrompt() - } -} - -Set-PSReadlineKeyHandler -Key Alt+j ` - -BriefDescription ShowDirectoryMarks ` - -LongDescription "Show the currently marked directories" ` - -ScriptBlock { - param($key, $arg) - - $global:PSReadlineMarks.GetEnumerator() | ForEach-Object { - [PSCustomObject]@{Key = $_.Key; Dir = $_.Value} } | - Format-Table -AutoSize | Out-Host - - [Microsoft.PowerShell.PSConsoleReadLine]::InvokePrompt() -} - -Set-PSReadlineOption -CommandValidationHandler { - param([System.Management.Automation.Language.CommandAst]$CommandAst) - - switch ($CommandAst.GetCommandName()) - { - 'git' { - $gitCmd = $CommandAst.CommandElements[1].Extent - switch ($gitCmd.Text) - { - 'cmt' { - [Microsoft.PowerShell.PSConsoleReadLine]::Replace( - $gitCmd.StartOffset, $gitCmd.EndOffset - $gitCmd.StartOffset, 'commit') - } - } - } - } -} diff --git a/src/Microsoft.PowerShell.PSReadLine/ScreenCapture.cs b/src/Microsoft.PowerShell.PSReadLine/ScreenCapture.cs deleted file mode 100644 index e35d83a5c57..00000000000 --- a/src/Microsoft.PowerShell.PSReadLine/ScreenCapture.cs +++ /dev/null @@ -1,309 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -#if !UNIX -using System; -using System.Diagnostics.CodeAnalysis; -using System.Runtime.InteropServices; -using System.Text; -using Microsoft.PowerShell.Internal; - -namespace Microsoft.PowerShell -{ - public partial class PSConsoleReadLine - { - private static void InvertLines(int start, int count) - { - var buffer = ReadBufferLines(start, count); - for (int i = 0; i < buffer.Length; i++) - { - buffer[i].ForegroundColor = (ConsoleColor)((int)buffer[i].ForegroundColor ^ 7); - buffer[i].BackgroundColor = (ConsoleColor)((int)buffer[i].BackgroundColor ^ 7); - } - _singleton._console.WriteBufferLines(buffer, ref start, false); - } - - /// - /// Start interactive screen capture - up/down arrows select lines, enter copies - /// selected text to clipboard as text and html - /// - [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")] - public static void CaptureScreen(ConsoleKeyInfo? key = null, object arg = null) - { - int selectionTop = _singleton._console.CursorTop; - int selectionHeight = 1; - int currentY = selectionTop; - Internal.IConsole console = _singleton._console; - - // We'll keep the current selection line (currentY) at least 4 lines - // away from the top or bottom of the window. - const int margin = 5; - Func tooCloseToTop = () => { return (currentY - console.WindowTop) < margin; }; - Func tooCloseToBottom = () => { return ((console.WindowTop + console.WindowHeight) - currentY) < margin; }; - - // Current lines starts out selected - InvertLines(selectionTop, selectionHeight); - bool done = false; - while (!done) - { - var k = ReadKey(); - switch (k.Key) - { - case ConsoleKey.K: - case ConsoleKey.UpArrow: - if (tooCloseToTop()) - ScrollDisplayUpLine(); - - if (currentY > 0) - { - currentY -= 1; - if ((k.Modifiers & ConsoleModifiers.Shift) == ConsoleModifiers.Shift) - { - if (currentY < selectionTop) - { - // Extend selection up, only invert newly selected line. - InvertLines(currentY, 1); - selectionTop = currentY; - selectionHeight += 1; - } - else if (currentY >= selectionTop) - { - // Selection shortend 1 line, invert unselected line. - InvertLines(currentY + 1, 1); - selectionHeight -= 1; - } - break; - } - goto updateSelectionCommon; - } - break; - - case ConsoleKey.J: - case ConsoleKey.DownArrow: - if (tooCloseToBottom()) - ScrollDisplayDownLine(); - - if (currentY < (console.BufferHeight - 1)) - { - currentY += 1; - if ((k.Modifiers & ConsoleModifiers.Shift) == ConsoleModifiers.Shift) - { - if (currentY == (selectionTop + selectionHeight)) - { - // Extend selection down, only invert newly selected line. - InvertLines(selectionTop + selectionHeight, 1); - selectionHeight += 1; - } - else if (currentY == (selectionTop + 1)) - { - // Selection shortend 1 line, invert unselected line. - InvertLines(selectionTop, 1); - selectionTop = currentY; - selectionHeight -= 1; - } - break; - } - goto updateSelectionCommon; - } - break; - - updateSelectionCommon: - // Shift not pressed - unselect current selection - InvertLines(selectionTop, selectionHeight); - selectionTop = currentY; - selectionHeight = 1; - InvertLines(selectionTop, selectionHeight); - break; - - case ConsoleKey.Enter: - InvertLines(selectionTop, selectionHeight); - DumpScreenToClipboard(selectionTop, selectionHeight); - ScrollDisplayToCursor(); - return; - - case ConsoleKey.Escape: - done = true; - continue; - - case ConsoleKey.C: - case ConsoleKey.G: - if (k.Modifiers == ConsoleModifiers.Control) - { - done = true; - continue; - } - Ding(); - break; - default: - Ding(); - break; - } - } - InvertLines(selectionTop, selectionHeight); - ScrollDisplayToCursor(); - } - - private const string CmdColorTable = @" -\red0\green0\blue0; -\red0\green0\blue128; -\red0\green128\blue0; -\red0\green128\blue128; -\red128\green0\blue0; -\red128\green0\blue128; -\red128\green128\blue0; -\red192\green192\blue192; -\red128\green128\blue128; -\red0\green0\blue255; -\red0\green255\blue0; -\red0\green255\blue255; -\red255\green0\blue0; -\red255\green0\blue255; -\red255\green255\blue0; -\red255\green255\blue255; -"; - - private const string PowerShellColorTable = @" -\red1\green36\blue86; -\red0\green0\blue128; -\red0\green128\blue0; -\red0\green128\blue128; -\red128\green0\blue0; -\red1\green36\blue86; -\red238\green237\blue240; -\red192\green192\blue192; -\red128\green128\blue128; -\red0\green0\blue255; -\red0\green255\blue0; -\red0\green255\blue255; -\red255\green0\blue0; -\red255\green0\blue255; -\red255\green255\blue0; -\red255\green255\blue255; -"; - - private static string GetRTFColorFromColorRef(NativeMethods.COLORREF colorref) - { - return string.Concat("\\red", colorref.R.ToString("D"), - "\\green", colorref.G.ToString("D"), - "\\blue", colorref.B.ToString("D"), ";"); - } - - private static string GetColorTable() - { - var handle = NativeMethods.GetStdHandle((uint) StandardHandleId.Output); - var csbe = new NativeMethods.CONSOLE_SCREEN_BUFFER_INFO_EX - { - cbSize = Marshal.SizeOf() - }; - if (NativeMethods.GetConsoleScreenBufferInfoEx(handle, ref csbe)) - { - return GetRTFColorFromColorRef(csbe.Black) + - GetRTFColorFromColorRef(csbe.DarkBlue) + - GetRTFColorFromColorRef(csbe.DarkGreen) + - GetRTFColorFromColorRef(csbe.DarkCyan) + - GetRTFColorFromColorRef(csbe.DarkRed) + - GetRTFColorFromColorRef(csbe.DarkMagenta) + - GetRTFColorFromColorRef(csbe.DarkYellow) + - GetRTFColorFromColorRef(csbe.Gray) + - GetRTFColorFromColorRef(csbe.DarkGray) + - GetRTFColorFromColorRef(csbe.Blue) + - GetRTFColorFromColorRef(csbe.Green) + - GetRTFColorFromColorRef(csbe.Cyan) + - GetRTFColorFromColorRef(csbe.Red) + - GetRTFColorFromColorRef(csbe.Magenta) + - GetRTFColorFromColorRef(csbe.Yellow) + - GetRTFColorFromColorRef(csbe.White); - } - - // A bit of a hack if the above failed - assume PowerShell's color scheme if the - // background color is Magenta, otherwise we assume the default scheme. - return _singleton._console.BackgroundColor == ConsoleColor.DarkMagenta - ? PowerShellColorTable - : CmdColorTable; - } - - private static void DumpScreenToClipboard(int top, int count) - { - var buffer = ReadBufferLines(top, count); - var bufferWidth = _singleton._console.BufferWidth; - - var textBuffer = new StringBuilder(buffer.Length + count); - - var rtfBuffer = new StringBuilder(); - rtfBuffer.Append(@"{\rtf\ansi{\fonttbl{\f0 Consolas;}}"); - - var colorTable = GetColorTable(); - rtfBuffer.AppendFormat(@"{{\colortbl;{0}}}{1}", colorTable, Environment.NewLine); - rtfBuffer.Append(@"\f0 \fs18 "); - - var charInfo = buffer[0]; - var fgColor = (int)charInfo.ForegroundColor; - var bgColor = (int)charInfo.BackgroundColor; - rtfBuffer.AppendFormat(@"{{\cf{0}\chshdng0\chcbpat{1} ", fgColor + 1, bgColor + 1); - for (int i = 0; i < count; i++) - { - var spaces = 0; - var rtfSpaces = 0; - for (int j = 0; j < bufferWidth; j++) - { - charInfo = buffer[i * bufferWidth + j]; - if ((int)charInfo.ForegroundColor != fgColor || (int)charInfo.BackgroundColor != bgColor) - { - if (rtfSpaces > 0) - { - rtfBuffer.Append(' ', rtfSpaces); - rtfSpaces = 0; - } - fgColor = (int)charInfo.ForegroundColor; - bgColor = (int)charInfo.BackgroundColor; - rtfBuffer.AppendFormat(@"}}{{\cf{0}\chshdng0\chcbpat{1} ", fgColor + 1, bgColor + 1); - } - - var c = (char)charInfo.UnicodeChar; - if (c == ' ') - { - // Trailing spaces are skipped, we'll add them back if we find a non-space - // before the end of line - ++spaces; - ++rtfSpaces; - } - else - { - if (spaces > 0) - { - textBuffer.Append(' ', spaces); - spaces = 0; - } - if (rtfSpaces > 0) - { - rtfBuffer.Append(' ', rtfSpaces); - rtfSpaces = 0; - } - - textBuffer.Append(c); - switch (c) - { - case '\\': rtfBuffer.Append(@"\\"); break; - case '\t': rtfBuffer.Append(@"\tab"); break; - case '{': rtfBuffer.Append(@"\{"); break; - case '}': rtfBuffer.Append(@"\}"); break; - default: rtfBuffer.Append(c); break; - } - } - } - rtfBuffer.AppendFormat(@"\shading0 \cbpat{0} \par{1}", bgColor + 1, Environment.NewLine); - textBuffer.Append(Environment.NewLine); - } - rtfBuffer.Append("}}"); - -#if !CORECLR // TODO: break dependency on Window.Forms w/ p/invokes to clipboard directly, for now, just silently skip the copy. - var dataObject = new System.Windows.Forms.DataObject(); - dataObject.SetData(System.Windows.Forms.DataFormats.Text, textBuffer.ToString()); - dataObject.SetData(System.Windows.Forms.DataFormats.Rtf, rtfBuffer.ToString()); - ExecuteOnSTAThread(() => System.Windows.Forms.Clipboard.SetDataObject(dataObject, copy: true)); -#endif - } - } -} -#endif diff --git a/src/Microsoft.PowerShell.PSReadLine/UndoRedo.cs b/src/Microsoft.PowerShell.PSReadLine/UndoRedo.cs deleted file mode 100644 index b451231e1a6..00000000000 --- a/src/Microsoft.PowerShell.PSReadLine/UndoRedo.cs +++ /dev/null @@ -1,242 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; - -namespace Microsoft.PowerShell -{ - public partial class PSConsoleReadLine - { - private void RemoveEditsAfterUndo() - { - // If there is some sort of edit after an undo, forget - // any edit items that were undone. - int removeCount = _edits.Count - _undoEditIndex; - if (removeCount > 0) - { - _edits.RemoveRange(_undoEditIndex, removeCount); - if (_editGroupStart >= 0) - { - // Adjust the edit group start if we are started a group. - _editGroupStart -= removeCount; - } - } - } - - private void SaveEditItem(EditItem editItem) - { - if (_statusIsErrorMessage) - { - // After an edit, clear the error message - ClearStatusMessage(render: true); - } - - RemoveEditsAfterUndo(); - - _edits.Add(editItem); - _undoEditIndex = _edits.Count; - } - - private void StartEditGroup() - { - if (_editGroupStart != -1) - { - // Nesting not supported. - throw new InvalidOperationException(); - } - - RemoveEditsAfterUndo(); - _editGroupStart = _edits.Count; - } - - private void EndEditGroup(Action instigator = null, object instigatorArg = null) - { - var groupEditCount = _edits.Count - _editGroupStart; - var groupedEditItems = _edits.GetRange(_editGroupStart, groupEditCount); - _edits.RemoveRange(_editGroupStart, groupEditCount); - SaveEditItem(GroupedEdit.Create(groupedEditItems, instigator, instigatorArg)); - _editGroupStart = -1; - } - - /// - /// Undo a previous edit. - /// - [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")] - public static void Undo(ConsoleKeyInfo? key = null, object arg = null) - { - if (_singleton._undoEditIndex > 0) - { - if (_singleton._statusIsErrorMessage) - { - // After an edit, clear the error message - _singleton.ClearStatusMessage(render: false); - } - _singleton._edits[_singleton._undoEditIndex - 1].Undo(); - _singleton._undoEditIndex--; - if (_singleton._options.EditMode == EditMode.Vi && _singleton._current >= _singleton._buffer.Length) - { - _singleton._current = Math.Max(0, _singleton._buffer.Length - 1); - } - _singleton.Render(); - } - else - { - Ding(); - } - } - - /// - /// Undo an undo. - /// - [SuppressMessage("Microsoft.Design", "CA1026:DefaultParametersShouldNotBeUsed")] - public static void Redo(ConsoleKeyInfo? key = null, object arg = null) - { - if (_singleton._undoEditIndex < _singleton._edits.Count) - { - _singleton._edits[_singleton._undoEditIndex].Redo(); - _singleton._undoEditIndex++; - _singleton.Render(); - } - else - { - Ding(); - } - } - - abstract class EditItem - { - public Action _instigator = null; - public object _instigatorArg = null; - - public abstract void Undo(); - public abstract void Redo(); - } - - [DebuggerDisplay("Insert '{_insertedCharacter}' ({_insertStartPosition})")] - class EditItemInsertChar : EditItem - { - // The character inserted is not needed for undo, only for redo - private char _insertedCharacter; - private int _insertStartPosition; - - public static EditItem Create(char character, int position) - { - return new EditItemInsertChar - { - _insertedCharacter = character, - _insertStartPosition = position - }; - } - - public override void Undo() - { - Debug.Assert(_singleton._buffer[_insertStartPosition] == _insertedCharacter, "Character to undo is not what it should be"); - _singleton._buffer.Remove(_insertStartPosition, 1); - _singleton._current = _insertStartPosition; - } - - public override void Redo() - { - _singleton._buffer.Insert(_insertStartPosition, _insertedCharacter); - _singleton._current++; - } - } - - [DebuggerDisplay("Insert '{_insertedString}' ({_insertStartPosition})")] - class EditItemInsertString : EditItem - { - // The string inserted tells us the length to delete on undo. - // The contents of the string are only needed for redo. - private string _insertedString; - private int _insertStartPosition; - - public static EditItem Create(string str, int position) - { - return new EditItemInsertString - { - _insertedString = str, - _insertStartPosition = position - }; - } - - public override void Undo() - { - Debug.Assert(_singleton._buffer.ToString(_insertStartPosition, _insertedString.Length).Equals(_insertedString), - "Character to undo is not what it should be"); - _singleton._buffer.Remove(_insertStartPosition, _insertedString.Length); - _singleton._current = _insertStartPosition; - } - - public override void Redo() - { - _singleton._buffer.Insert(_insertStartPosition, _insertedString); - _singleton._current += _insertedString.Length; - } - } - - [DebuggerDisplay("Delete '{_deletedString}' ({_deleteStartPosition})")] - class EditItemDelete : EditItem - { - private string _deletedString; - private int _deleteStartPosition; - - public static EditItem Create(string str, int position, Action instigator = null, object instigatorArg = null) - { - return new EditItemDelete - { - _deletedString = str, - _deleteStartPosition = position, - _instigator = instigator, - _instigatorArg = instigatorArg - }; - } - - public override void Undo() - { - _singleton._buffer.Insert(_deleteStartPosition, _deletedString); - _singleton._current = _deleteStartPosition + _deletedString.Length; - } - - public override void Redo() - { - _singleton._buffer.Remove(_deleteStartPosition, _deletedString.Length); - _singleton._current = _deleteStartPosition; - } - } - - class GroupedEdit : EditItem - { - internal List _groupedEditItems; - - public static EditItem Create(List groupedEditItems, Action instigator = null, object instigatorArg = null) - { - return new GroupedEdit - { - _groupedEditItems = groupedEditItems, - _instigator = instigator, - _instigatorArg = instigatorArg - }; - } - - public override void Undo() - { - for (int i = _groupedEditItems.Count - 1; i >= 0; i--) - { - _groupedEditItems[i].Undo(); - } - } - - public override void Redo() - { - foreach (var editItem in _groupedEditItems) - { - editItem.Redo(); - } - } - } - } -} diff --git a/src/Microsoft.PowerShell.PSReadLine/UndoRedo.vi.cs b/src/Microsoft.PowerShell.PSReadLine/UndoRedo.vi.cs deleted file mode 100644 index 11e5f452d32..00000000000 --- a/src/Microsoft.PowerShell.PSReadLine/UndoRedo.vi.cs +++ /dev/null @@ -1,70 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Text; - -namespace Microsoft.PowerShell -{ - public partial class PSConsoleReadLine - { - private class GroupUndoHelper - { - public Action _instigator = null; - public object _instigatorArg = null; - - public GroupUndoHelper() - { - _instigator = null; - _instigatorArg = null; - } - - public void StartGroup(Action instigator, object instigatorArg) - { - _instigator = instigator; - _instigatorArg = instigatorArg; - _singleton.StartEditGroup(); - } - - public void Clear() - { - _instigator = null; - _instigatorArg = null; - } - - public void EndGroup() - { - if (_singleton._editGroupStart >= 0) - { - _singleton.EndEditGroup(_instigator, _instigatorArg); - } - Clear(); - } - } - private GroupUndoHelper _groupUndoHelper = new GroupUndoHelper(); - - /// - /// Undo all previous edits for line. - /// - public static void UndoAll(ConsoleKeyInfo? key = null, object arg = null) - { - if (_singleton._undoEditIndex > 0) - { - while (_singleton._undoEditIndex > 0) - { - _singleton._edits[_singleton._undoEditIndex - 1].Undo(); - _singleton._undoEditIndex--; - } - _singleton.Render(); - } - else - { - Ding(); - } - } - } -} diff --git a/src/Microsoft.PowerShell.PSReadLine/VisualEditing.vi.cs b/src/Microsoft.PowerShell.PSReadLine/VisualEditing.vi.cs deleted file mode 100644 index 59a14053eab..00000000000 --- a/src/Microsoft.PowerShell.PSReadLine/VisualEditing.vi.cs +++ /dev/null @@ -1,111 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Text; - -namespace Microsoft.PowerShell -{ - public partial class PSConsoleReadLine - { - private string _visualEditTemporaryFilename = null; - private Func _savedAddToHistoryHandler = null; - - /// - /// Edit the command line in a text editor specified by $env:EDITOR or $env:VISUAL - /// - public static void ViEditVisually(ConsoleKeyInfo? key = null, object arg = null) - { - string editorOfChoice = GetPreferredEditor(); - if (string.IsNullOrWhiteSpace(editorOfChoice)) - { - Ding(); - return; - } - - _singleton._visualEditTemporaryFilename = GetTemporaryPowerShellFile(); - using (FileStream fs = File.OpenWrite(_singleton._visualEditTemporaryFilename)) - { - using (TextWriter tw = new StreamWriter(fs)) - { - tw.Write(_singleton._buffer.ToString()); - } - } - - _singleton._savedAddToHistoryHandler = _singleton.Options.AddToHistoryHandler; - _singleton.Options.AddToHistoryHandler = ((string s) => - { - return false; - }); - - _singleton._buffer.Clear(); - _singleton._current = 0; - _singleton.Render(); - _singleton._buffer.Append(editorOfChoice + " \'" + _singleton._visualEditTemporaryFilename + "\'"); - AcceptLine(); - } - - private static string GetTemporaryPowerShellFile() - { - string filename; - do - { - filename = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName() + ".ps1"); - } while (File.Exists(filename) || Directory.Exists(filename)); - - return filename; - } - - private void ProcessViVisualEditing() - { - if (_visualEditTemporaryFilename == null) - { - return; - } - - Options.AddToHistoryHandler = _savedAddToHistoryHandler; - _savedAddToHistoryHandler = null; - - string editedCommand = null; - using (TextReader tr = File.OpenText(_visualEditTemporaryFilename)) - { - editedCommand = tr.ReadToEnd(); - } - File.Delete(_visualEditTemporaryFilename); - _visualEditTemporaryFilename = null; - - if (!string.IsNullOrWhiteSpace(editedCommand)) - { - while (editedCommand.Length > 0 && char.IsWhiteSpace(editedCommand[editedCommand.Length - 1])) - { - editedCommand = editedCommand.Substring(0, editedCommand.Length - 1); - } - editedCommand = editedCommand.Replace(Environment.NewLine, "\n"); - _buffer.Clear(); - _buffer.Append(editedCommand); - _current = _buffer.Length - 1; - Render(); - //_queuedKeys.Enqueue(Keys.Enter); - } - } - - private static string GetPreferredEditor() - { - string[] names = {"VISUAL", "EDITOR"}; - foreach (string name in names) - { - string editor = Environment.GetEnvironmentVariable(name); - if (!string.IsNullOrWhiteSpace(editor)) - { - return editor; - } - } - - return null; - } - } -} diff --git a/src/Microsoft.PowerShell.PSReadLine/Words.cs b/src/Microsoft.PowerShell.PSReadLine/Words.cs deleted file mode 100644 index 5aa0eca46a9..00000000000 --- a/src/Microsoft.PowerShell.PSReadLine/Words.cs +++ /dev/null @@ -1,204 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -using System.Collections.Generic; -using System.Management.Automation.Language; - -namespace Microsoft.PowerShell -{ - public partial class PSConsoleReadLine - { - private enum FindTokenMode - { - CurrentOrNext, - Next, - Previous, - } - - static bool OffsetWithinToken(int offset, Token token) - { - return offset < token.Extent.EndOffset && offset >= token.Extent.StartOffset; - } - - private Token FindNestedToken(int offset, IList tokens, FindTokenMode mode) - { - Token token = null; - bool foundNestedToken = false; - int i; - for (i = tokens.Count - 1; i >= 0; i--) - { - if (OffsetWithinToken(offset, tokens[i])) - { - token = tokens[i]; - var strToken = token as StringExpandableToken; - if (strToken != null && strToken.NestedTokens != null) - { - var nestedToken = FindNestedToken(offset, strToken.NestedTokens, mode); - if (nestedToken != null) - { - token = nestedToken; - foundNestedToken = true; - } - } - break; - } - if (offset >= tokens[i].Extent.EndOffset) - { - break; - } - } - - switch (mode) - { - case FindTokenMode.CurrentOrNext: - if (token == null && (i + 1) < tokens.Count) - { - token = tokens[i + 1]; - } - break; - case FindTokenMode.Next: - if (!foundNestedToken) - { - // If there is no next token, return null (happens with nested - // tokens where there is no EOF/EOS token). - token = ((i + 1) < tokens.Count) ? tokens[i + 1] : null; - } - break; - case FindTokenMode.Previous: - if (token == null) - { - if (i >= 0) - { - token = tokens[i]; - } - } - else if (offset == token.Extent.StartOffset) - { - token = i > 0 ? tokens[i - 1] : null; - } - break; - } - - return token; - } - - private Token FindToken(int current, FindTokenMode mode) - { - MaybeParseInput(); - return FindNestedToken(current, _tokens, mode); - } - - private bool InWord(int index, string wordDelimiters) - { - char c = _buffer[index]; - return !char.IsWhiteSpace(c) && wordDelimiters.IndexOf(c) < 0; - } - - /// - /// Find the end of the current/next word as defined by wordDelimiters and whitespace. - /// - private int FindForwardWordPoint(string wordDelimiters) - { - int i = _current; - if (i == _buffer.Length) - { - return i; - } - - if (!InWord(i, wordDelimiters)) - { - // Scan to end of current non-word region - while (i < _buffer.Length) - { - if (InWord(i, wordDelimiters)) - { - break; - } - i += 1; - } - } - while (i < _buffer.Length) - { - if (!InWord(i, wordDelimiters)) - { - break; - } - i += 1; - } - return i; - } - - /// - /// Find the start of the next word. - /// - private int FindNextWordPoint(string wordDelimiters) - { - int i = _singleton._current; - if (i == _singleton._buffer.Length) - { - return i; - } - - if (InWord(i, wordDelimiters)) - { - // Scan to end of current word region - while (i < _singleton._buffer.Length) - { - if (!InWord(i, wordDelimiters)) - { - break; - } - i += 1; - } - } - - while (i < _singleton._buffer.Length) - { - if (InWord(i, wordDelimiters)) - { - break; - } - i += 1; - } - return i; - } - - /// - /// Find the beginning of the previous word. - /// - private int FindBackwardWordPoint(string wordDelimiters) - { - int i = _current - 1; - if (i < 0) - { - return 0; - } - - if (!InWord(i, wordDelimiters)) - { - // Scan backwards until we are at the end of the previous word. - while (i > 0) - { - if (InWord(i, wordDelimiters)) - { - break; - } - i -= 1; - } - } - while (i > 0) - { - if (!InWord(i, wordDelimiters)) - { - i += 1; - break; - } - i -= 1; - } - return i; - } - - - } -} diff --git a/src/Microsoft.PowerShell.PSReadLine/Words.vi.cs b/src/Microsoft.PowerShell.PSReadLine/Words.vi.cs deleted file mode 100644 index eab0df2f4f8..00000000000 --- a/src/Microsoft.PowerShell.PSReadLine/Words.vi.cs +++ /dev/null @@ -1,488 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -using System.Collections.Generic; -using System.Management.Automation.Language; - -namespace Microsoft.PowerShell -{ - public partial class PSConsoleReadLine - { - private char _lastWordDelimiter = char.MinValue; - private bool _shouldAppend = false; - - /// - /// Returns the position of the beginning of the next word as delimited by white space and delimiters. - /// - private int ViFindNextWordPoint(string wordDelimiters) - { - return ViFindNextWordPoint(_current, wordDelimiters); - } - - /// - /// Returns the position of the beginning of the next word as delimited by white space and delimiters. - /// - private int ViFindNextWordPoint(int i, string wordDelimiters) - { - if (IsAtEndOfLine(i)) - { - return i; - } - if (InWord(i, wordDelimiters)) - { - return ViFindNextWordFromWord(i, wordDelimiters); - } - if (IsDelimiter(i, wordDelimiters)) - { - return ViFindNextWordFromDelimiter(i, wordDelimiters); - } - return ViFindNextWordFromWhiteSpace(i, wordDelimiters); - } - - private int ViFindNextWordFromWhiteSpace(int i, string wordDelimiters) - { - while (!IsAtEndOfLine(i) && IsWhiteSpace(i)) - { - i++; - } - return i; - } - - private int ViFindNextWordFromDelimiter(int i, string wordDelimiters) - { - while (!IsAtEndOfLine(i) && IsDelimiter(i, wordDelimiters)) - { - i++; - } - if (IsAtEndOfLine(i)) - { - if (IsDelimiter(i, wordDelimiters)) - { - _shouldAppend = true; - return i + 1; - } - return i; - } - while (!IsAtEndOfLine(i) && IsWhiteSpace(i)) - { - i++; - } - return i; - } - - private bool IsAtEndOfLine(int i) - { - return i >= (_buffer.Length - 1); - } - - private bool IsPastEndOfLine(int i) - { - return i > (_buffer.Length - 1); - } - - private int ViFindNextWordFromWord(int i, string wordDelimiters) - { - while (!IsAtEndOfLine(i) && InWord(i, wordDelimiters)) - { - i++; - } - if (IsAtEndOfLine(i) && InWord(i, wordDelimiters)) - { - _shouldAppend = true; - return i + 1; - } - if (IsDelimiter(i, wordDelimiters)) - { - _lastWordDelimiter = _buffer[i]; - return i; - } - while (!IsAtEndOfLine(i) && IsWhiteSpace(i)) - { - i++; - } - if (IsAtEndOfLine(i) && !InWord(i, wordDelimiters)) - { - return i + 1; - } - _lastWordDelimiter = _buffer[i-1]; - return i; - } - - /// - /// Returns true of the character at the given position is white space. - /// - private bool IsWhiteSpace(int i) - { - return char.IsWhiteSpace(_buffer[i]); - } - - /// - /// Returns the beginning of the current/next word as defined by wordDelimiters and whitespace. - /// - private int ViFindPreviousWordPoint(string wordDelimiters) - { - return ViFindPreviousWordPoint(_current, wordDelimiters); - } - - /// - /// Returns the beginning of the current/next word as defined by wordDelimiters and whitespace. - /// - /// Current cursor location. - /// Characters used to delimit words. - /// Location of the beginning of the previous word. - private int ViFindPreviousWordPoint(int i, string wordDelimiters) - { - if (i == 0) - { - return i; - } - - if (IsWhiteSpace(i)) - { - return FindPreviousWordFromWhiteSpace(i, wordDelimiters); - } - else if (InWord(i, wordDelimiters)) - { - return FindPreviousWordFromWord(i, wordDelimiters); - } - return FindPreviousWordFromDelimiter(i, wordDelimiters); - } - - /// - /// Knowing that you're starting with a word, find the previous start of the next word. - /// - private int FindPreviousWordFromWord(int i, string wordDelimiters) - { - i--; - if (InWord(i, wordDelimiters)) - { - while (i > 0 && InWord(i, wordDelimiters)) - { - i--; - } - if (i == 0 && InWord(i, wordDelimiters)) - { - return i; - } - return i + 1; - } - if (IsWhiteSpace(i)) - { - while (i > 0 && IsWhiteSpace(i)) - { - i--; - } - if (i == 0) - { - return i; - } - if (InWord(i, wordDelimiters) && InWord(i-1, wordDelimiters)) - { - return FindPreviousWordFromWord(i, wordDelimiters); - } - if (IsDelimiter(i - 1, wordDelimiters)) - { - FindPreviousWordFromDelimiter(i, wordDelimiters); - } - return i; - } - while (i > 0 && IsDelimiter(i, wordDelimiters)) - { - i--; - } - if (i == 0 && IsDelimiter(i, wordDelimiters)) - { - return i; - } - return i + 1; - } - - /// - /// Returns true if the cursor is on a word delimiter - /// - private bool IsDelimiter(int i, string wordDelimiters) - { - return wordDelimiters.IndexOf(_buffer[i]) >= 0; - } - - /// - /// Returns true if is in the set of . - /// - private bool IsDelimiter(char c, string wordDelimiters) - { - foreach (char delimiter in wordDelimiters) - { - if (c == delimiter) - { - return true; - } - } - return false; - } - - /// - /// Returns the cursor position of the beginning of the previous word when starting on a delimiter - /// - private int FindPreviousWordFromDelimiter(int i, string wordDelimiters) - { - i--; - if (IsDelimiter(i, wordDelimiters)) - { - while (i > 0 && IsDelimiter(i, wordDelimiters)) - { - i--; - } - if (i == 0 && !IsDelimiter(i, wordDelimiters)) - { - return i + 1; - } - if (!IsWhiteSpace(i)) - { - return i + 1; - } - return i; - } - return ViFindPreviousWordPoint(i, wordDelimiters); - } - - - /// - /// Returns the cursor position of the beginning of the previous word when starting on white space - /// - private int FindPreviousWordFromWhiteSpace(int i, string wordDelimiters) - { - while (IsWhiteSpace(i) && i > 0) - { - i--; - } - int j = i - 1; - if (j < 0 || !InWord(i, wordDelimiters) || char.IsWhiteSpace(_buffer[j])) - { - return i; - } - return (ViFindPreviousWordPoint(i, wordDelimiters)); - } - - /// - /// Returns the cursor position of the previous word, ignoring all delimiters other what white space - /// - private int ViFindPreviousGlob() - { - int i = _current; - if (i == 0) - { - return 0; - } - i--; - - return ViFindPreviousGlob(i); - } - - /// - /// Returns the cursor position of the previous word from i, ignoring all delimiters other what white space - /// - private int ViFindPreviousGlob(int i) - { - if (i <= 0) - { - return 0; - } - - if (!IsWhiteSpace(i)) - { - while (i > 0 && !IsWhiteSpace(i)) - { - i--; - } - if (!IsWhiteSpace(i)) - { - return i; - } - return i + 1; - } - while (i > 0 && IsWhiteSpace(i)) - { - i--; - } - if (i == 0) - { - return i; - } - return ViFindPreviousGlob(i); - } - - /// - /// Finds the next work, using only white space as the word delimiter. - /// - private int ViFindNextGlob() - { - int i = _current; - return ViFindNextGlob(i); - } - - private int ViFindNextGlob(int i) - { - if (i >= _buffer.Length) - { - return i; - } - while (!IsAtEndOfLine(i) && !IsWhiteSpace(i)) - { - i++; - } - if (IsAtEndOfLine(i) && !IsWhiteSpace(i)) - { - return i + 1; - } - while (!IsAtEndOfLine(i) && IsWhiteSpace(i)) - { - i++; - } - if (IsAtEndOfLine(i) && IsWhiteSpace(i)) - { - return i + 1; - } - return i; - } - - /// - /// Finds the end of the current/next word as defined by whitespace. - /// - private int ViFindEndOfGlob() - { - return ViFindGlobEnd(_current); - } - - /// - /// Find the end of the current/next word as defined by wordDelimiters and whitespace. - /// - private int ViFindNextWordEnd(string wordDelimiters) - { - int i = _current; - - return ViFindNextWordEnd(i, wordDelimiters); - } - - /// - /// Find the end of the current/next word as defined by wordDelimiters and whitespace. - /// - private int ViFindNextWordEnd(int i, string wordDelimiters) - { - if (IsAtEndOfLine(i)) - { - return i; - } - - if (IsDelimiter(i, wordDelimiters) && !IsDelimiter(i + 1, wordDelimiters)) - { - i++; - if (IsAtEndOfLine(i)) - { - return i; - } - } - else if (InWord(i, wordDelimiters) && !InWord(i + 1, wordDelimiters)) - { - i++; - if (IsAtEndOfLine(i)) - { - return i; - } - } - - while (!IsAtEndOfLine(i) && IsWhiteSpace(i)) - { - i++; - } - - if (IsAtEndOfLine(i)) - { - return i; - } - - if (IsDelimiter(i, wordDelimiters)) - { - while (!IsAtEndOfLine(i) && IsDelimiter(i, wordDelimiters)) - { - i++; - } - if (!IsDelimiter(i, wordDelimiters)) - { - return i - 1; - } - } - else - { - while (!IsAtEndOfLine(i) && InWord(i, wordDelimiters)) - { - i++; - } - if (!InWord(i, wordDelimiters)) - { - return i - 1; - } - } - - return i; - } - - /// - /// Return the last character in a white space defined word after skipping contiguous white space. - /// - private int ViFindGlobEnd(int i) - { - if (IsAtEndOfLine(i)) - { - return i; - } - i++; - if (IsAtEndOfLine(i)) - { - return i; - } - while (!IsAtEndOfLine(i) && IsWhiteSpace(i)) - { - i++; - } - if (IsAtEndOfLine(i)) - { - return i; - } - while (!IsAtEndOfLine(i) && !IsWhiteSpace(i)) - { - i++; - } - if (IsWhiteSpace(i)) - { - return i - 1; - } - return i; - } - - private int ViFindEndOfPreviousGlob() - { - int i = _current; - - return ViFindEndOfPreviousGlob(i); - } - - private int ViFindEndOfPreviousGlob(int i) - { - if (IsWhiteSpace(i)) - { - while (i > 0 && IsWhiteSpace(i)) - { - i--; - } - return i; - } - - while (i > 0 && !IsWhiteSpace(i)) - { - i--; - } - return ViFindEndOfPreviousGlob(i); - } - } -} diff --git a/src/Microsoft.PowerShell.PSReadLine/YankPaste.vi.cs b/src/Microsoft.PowerShell.PSReadLine/YankPaste.vi.cs deleted file mode 100644 index 93e383cb08f..00000000000 --- a/src/Microsoft.PowerShell.PSReadLine/YankPaste.vi.cs +++ /dev/null @@ -1,338 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -using System; - -namespace Microsoft.PowerShell -{ - public partial class PSConsoleReadLine - { - private string _clipboard = string.Empty; - - /// - /// Paste the clipboard after the cursor, moving the cursor to the end of the pasted text. - /// - public static void PasteAfter(ConsoleKeyInfo? key = null, object arg = null) - { - if (string.IsNullOrEmpty(_singleton._clipboard)) - { - Ding(); - return; - } - - _singleton.PasteAfterImpl(); - } - - /// - /// Paste the clipboard before the cursor, moving the cursor to the end of the pasted text. - /// - public static void PasteBefore(ConsoleKeyInfo? key = null, object arg = null) - { - if (string.IsNullOrEmpty(_singleton._clipboard)) - { - Ding(); - return; - } - _singleton.PasteBeforeImpl(); - } - - private void PasteAfterImpl() - { - if (_current < _buffer.Length) - { - _current++; - } - Insert(_clipboard); - _current--; - Render(); - } - - private void PasteBeforeImpl() - { - Insert(_clipboard); - _current--; - Render(); - } - - private void SaveToClipboard(int startIndex, int length) - { - _clipboard = _buffer.ToString(startIndex, length); - } - - /// - /// Yank the entire buffer. - /// - public static void ViYankLine(ConsoleKeyInfo? key = null, object arg = null) - { - _singleton.SaveToClipboard(0, _singleton._buffer.Length); - } - - /// - /// Yank character(s) under and to the right of the cursor. - /// - public static void ViYankRight(ConsoleKeyInfo? key = null, object arg = null) - { - int numericArg; - if (!TryGetArgAsInt(arg, out numericArg, 1)) - { - return; - } - - int start = _singleton._current; - int length = 0; - - while (numericArg-- > 0) - { - length++; - } - - _singleton.SaveToClipboard(start, length); - } - - /// - /// Yank character(s) to the left of the cursor. - /// - public static void ViYankLeft(ConsoleKeyInfo? key = null, object arg = null) - { - int numericArg; - if (!TryGetArgAsInt(arg, out numericArg, 1)) - { - return; - } - - int start = _singleton._current; - if (start == 0) - { - _singleton.SaveToClipboard(start, 1); - return; - } - - int length = 0; - - while (numericArg-- > 0) - { - if (start > 0) - { - start--; - length++; - } - } - - _singleton.SaveToClipboard(start, length); - } - - /// - /// Yank from the cursor to the end of the buffer. - /// - public static void ViYankToEndOfLine(ConsoleKeyInfo? key = null, object arg = null) - { - int start = _singleton._current; - int length = _singleton._buffer.Length - _singleton._current; - _singleton.SaveToClipboard(start, length); - } - - /// - /// Yank the word(s) before the cursor. - /// - public static void ViYankPreviousWord(ConsoleKeyInfo? key = null, object arg = null) - { - int numericArg; - if (!TryGetArgAsInt(arg, out numericArg, 1)) - { - return; - } - - int start = _singleton._current; - - while (numericArg-- > 0) - { - start = _singleton.ViFindPreviousWordPoint(start, _singleton.Options.WordDelimiters); - } - - int length = _singleton._current - start; - if (length > 0) - { - _singleton.SaveToClipboard(start, length); - } - } - - /// - /// Yank the word(s) after the cursor. - /// - public static void ViYankNextWord(ConsoleKeyInfo? key = null, object arg = null) - { - int numericArg; - if (!TryGetArgAsInt(arg, out numericArg, 1)) - { - return; - } - - int end = _singleton._current; - - while (numericArg-- > 0) - { - end = _singleton.ViFindNextWordPoint(end, _singleton.Options.WordDelimiters); - } - - int length = end - _singleton._current; - //if (_singleton.IsAtEndOfLine(end)) - //{ - // length++; - //} - if (length > 0) - { - _singleton.SaveToClipboard(_singleton._current, length); - } - } - - /// - /// Yank from the cursor to the end of the word(s). - /// - public static void ViYankEndOfWord(ConsoleKeyInfo? key = null, object arg = null) - { - int numericArg; - if (!TryGetArgAsInt(arg, out numericArg, 1)) - { - return; - } - - int end = _singleton._current; - - while (numericArg-- > 0) - { - end = _singleton.ViFindNextWordEnd(end, _singleton.Options.WordDelimiters); - } - - int length = 1 + end - _singleton._current; - if (length > 0) - { - _singleton.SaveToClipboard(_singleton._current, length); - } - } - - /// - /// Yank from the cursor to the end of the WORD(s). - /// - public static void ViYankEndOfGlob(ConsoleKeyInfo? key = null, object arg = null) - { - int numericArg; - if (!TryGetArgAsInt(arg, out numericArg, 1)) - { - return; - } - - int end = _singleton._current; - - while (numericArg-- > 0) - { - end = _singleton.ViFindGlobEnd(end); - } - - int length = 1 + end - _singleton._current; - if (length > 0) - { - _singleton.SaveToClipboard(_singleton._current, length); - } - } - - /// - /// Yank from the beginning of the buffer to the cursor. - /// - public static void ViYankBeginningOfLine(ConsoleKeyInfo? key = null, object arg = null) - { - int length = _singleton._current; - if (length > 0) - { - _singleton.SaveToClipboard(0, length); - } - } - - /// - /// Yank from the first non-whitespace character to the cursor. - /// - public static void ViYankToFirstChar(ConsoleKeyInfo? key = null, object arg = null) - { - int start = 0; - while (_singleton.IsWhiteSpace(start)) - { - start++; - } - if (start == _singleton._current) - { - return; - } - - int length = _singleton._current - start; - if (length > 0) - { - _singleton.SaveToClipboard(start, length); - } - } - - /// - /// Yank to/from matching brace. - /// - public static void ViYankPercent(ConsoleKeyInfo? key = null, object arg = null) - { - int start = _singleton.ViFindBrace(_singleton._current); - if (_singleton._current < start) - { - _singleton.SaveToClipboard(_singleton._current, start - _singleton._current + 1); - } - else if (start < _singleton._current) - { - _singleton.SaveToClipboard(start, _singleton._current - start + 1); - } - else - { - Ding(); - } - } - - /// - /// Yank from beginning of the WORD(s) to cursor. - /// - public static void ViYankPreviousGlob(ConsoleKeyInfo? key = null, object arg = null) - { - int numericArg; - if (!TryGetArgAsInt(arg, out numericArg, 1)) - { - return; - } - - int start = _singleton._current; - while (numericArg-- > 0) - { - start = _singleton.ViFindPreviousGlob(start - 1); - } - if (start < _singleton._current) - { - _singleton.SaveToClipboard(start, _singleton._current - start); - } - else - { - Ding(); - } - } - - /// - /// Yank from cursor to the start of the next WORD(s). - /// - public static void ViYankNextGlob(ConsoleKeyInfo? key = null, object arg = null) - { - int numericArg; - if (!TryGetArgAsInt(arg, out numericArg, 1)) - { - return; - } - - int end = _singleton._current; - while (numericArg-- > 0) - { - end = _singleton.ViFindNextGlob(end); - } - _singleton.SaveToClipboard(_singleton._current, end - _singleton._current); - } - } -} diff --git a/src/Microsoft.PowerShell.PSReadLine/en-US/PSReadline.md b/src/Microsoft.PowerShell.PSReadLine/en-US/PSReadline.md deleted file mode 100644 index c92c4872641..00000000000 --- a/src/Microsoft.PowerShell.PSReadLine/en-US/PSReadline.md +++ /dev/null @@ -1,513 +0,0 @@ -# Get-PSReadlineKeyHandler - -## SYNOPSIS -Gets the key bindings for the PSReadline module. - -## DESCRIPTION -Gets the key bindings for the PSReadline module. - -If neither -Bound nor -Unbound is specified, returns all bound keys and unbound functions. - -If -Bound is specified and -Unbound is not specified, only bound keys are returned. - -If -Unbound is specified and -Bound is not specified, only unbound keys are returned. - -If both -Bound and -Unbound are specified, returns all bound keys and unbound functions. - -## PARAMETERS - -### Bound [switch] = True - -```powershell -[Parameter(ParameterSetName = 'Set 1')] -``` - -Include functions that are bound. - - -### Unbound [switch] = True - -```powershell -[Parameter(ParameterSetName = 'Set 1')] -``` - -Include functions that are unbound. - - - -## INPUTS -### None -You cannot pipe objects to Get-PSReadlineKeyHandler - -## OUTPUTS -### Microsoft.PowerShell.KeyHandler - -Returns one entry for each key binding (or chord) for bound functions and/or one entry for each unbound function - - - -## RELATED LINKS - -[about_PSReadline]() - -# Get-PSReadlineOption - -## SYNOPSIS -Returns the values for the options that can be configured. - -## DESCRIPTION -Get-PSReadlineOption returns the current state of the settings that can be configured by Set-PSReadlineOption. - -The object returned can be used to change PSReadline options. -This provides a slightly simpler way of setting syntax coloring options for multiple kinds of tokens. - -## PARAMETERS - - -## INPUTS -### None -You cannot pipe objects to Get-PSReadlineOption - -## OUTPUTS -### - - - - - -## RELATED LINKS - -[about_PSReadline]() - -# Set-PSReadlineKeyHandler - -## SYNOPSIS -Binds or rebinds keys to user defined or PSReadline provided key handlers. - -## DESCRIPTION -This cmdlet is used to customize what happens when a particular key or sequence of keys is pressed while PSReadline is reading input. - -With user defined key bindings, you can do nearly anything that is possible from a PowerShell script. -Typically you might just edit the command line in some novel way, but because the handlers are just PowerShell scripts, you can do interesting things like change directories, launch programs, etc. - -## PARAMETERS - -### Chord [String[]] - -```powershell -[Parameter( - Mandatory = $true, - Position = 0)] -``` - -The key or sequence of keys to be bound to a Function or ScriptBlock. -A single binding is specified with a single string. -If the binding is a sequence of keys, the keys are separated with a comma, e.g. "Ctrl+X,Ctrl+X". -Note that this parameter accepts multiple strings. -Each string is a separate binding, not a sequence of keys for a single binding. - - -### ScriptBlock [ScriptBlock] - -```powershell -[Parameter( - Mandatory = $true, - Position = 1, - ParameterSetName = 'Set 1')] -``` - -The ScriptBlock is called when the Chord is entered. -The ScriptBlock is passed one or sometimes two arguments. -The first argument is the key pressed (a ConsoleKeyInfo.) The second argument could be any object depending on the context. - - -### BriefDescription [String] - -```powershell -[Parameter(ParameterSetName = 'Set 1')] -``` - -A brief description of the key binding. -Used in the output of cmdlet Get-PSReadlineKeyHandler. - - -### Description [String] - -```powershell -[Parameter(ParameterSetName = 'Set 1')] -``` - -A more verbose description of the key binding. -Used in the output of the cmdlet Get-PSReadlineKeyHandler. - - -### Function [String] - -```powershell -[Parameter( - Mandatory = $true, - Position = 1, - ParameterSetName = 'Set 2')] -``` - -The name of an existing key handler provided by PSReadline. -This parameter allows one to rebind existing key bindings or to bind a handler provided by PSReadline that is currently unbound. - -Using the ScriptBlock parameter, one can achieve equivalent functionality by calling the method directly from the ScriptBlock. -This parameter is preferred though - it makes it easier to determine which functions are bound and unbound. - - - -## INPUTS -### None -You cannot pipe objects to Set-PSReadlineKeyHandler - -## OUTPUTS -### - - - - -## EXAMPLES -### -------------- Example 1 -------------- - -```powershell -PS C:\> Set-PSReadlineKeyHandler -Key UpArrow -Function HistorySearchBackward -``` -This command binds the up arrow key to the function HistorySearchBackward which will use the currently entered command line as the beginning of the search string when searching through history. -### -------------- Example 2 -------------- - -```powershell -PS C:\> Set-PSReadlineKeyHandler -Chord Shift+Ctrl+B -ScriptBlock { - [PSConsoleUtilities.PSConsoleReadLine]::RevertLine() - [PSConsoleUtilities.PSConsoleReadLine]::Insert('build') ->>> [PSConsoleUtilities.PSConsoleReadLine]::AcceptLine() -} -``` -This example binds the key Ctrl+Shift+B to a script block that clears the line, inserts build, then accepts the line. -This example shows how a single key can be used to execute a command. - -## RELATED LINKS - -[about_PSReadline]() - -# Set-PSReadlineOption - -## SYNOPSIS -Customizes the behavior of command line editing in PSReadline. - -## DESCRIPTION -The Set-PSReadlineOption cmdlet is used to customize the behavior of the PSReadline module when editing the command line. - -## PARAMETERS - -### EditMode [EditMode] = Windows - -```powershell -[Parameter(ParameterSetName = 'Set 1')] -``` - -Specifies the command line editing mode. -This will reset any key bindings set by Set-PSReadlineKeyHandler. - -Valid values are: - --- Windows: Key bindings emulate PowerShell/cmd with some bindings emulating Visual Studio. - --- Emacs: Key bindings emulate Bash or Emacs. - - -### ContinuationPrompt [String] = >>> - -```powershell -[Parameter(ParameterSetName = 'Set 1')] -``` - -Specifies the string displayed at the beginning of the second and subsequent lines when multi-line input is being entered. -Defaults to '\>\>\> '. -The empty string is valid. - - -### ContinuationPromptForegroundColor [ConsoleColor] - -```powershell -[Parameter(ParameterSetName = 'Set 1')] -``` - -Specifies the foreground color of the continuation prompt. - - -### ContinuationPromptBackgroundColor [ConsoleColor] - -```powershell -[Parameter(ParameterSetName = 'Set 1')] -``` - -Specifies the background color of the continuation prompt. - - -### EmphasisForegroundColor [ConsoleColor] = Cyan - -```powershell -[Parameter(ParameterSetName = 'Set 1')] -``` - -Specifies the foreground color used for emphasis, e.g. -to highlight search text. - - -### EmphasisBackgroundColor [ConsoleColor] - -```powershell -[Parameter(ParameterSetName = 'Set 1')] -``` - -Specifies the background color used for emphasis, e.g. -to highlight search text. - - -### ErrorForegroundColor [ConsoleColor] = Red - -```powershell -[Parameter(ParameterSetName = 'Set 1')] -``` - -Specifies the foreground color used for errors. - - -### ErrorBackgroundColor [ConsoleColor] - -```powershell -[Parameter(ParameterSetName = 'Set 1')] -``` - -Specifies the background color used for errors. - - -### HistoryNoDuplicates [switch] - -```powershell -[Parameter(ParameterSetName = 'Set 1')] -``` - -Specifies that duplicate commands should not be added to PSReadline history. - - -### AddToHistoryHandler [Func[String, Boolean]] - -```powershell -[Parameter(ParameterSetName = 'Set 1')] -``` - -Specifies a ScriptBlock that can be used to control which commands get added to PSReadline history. - - -### ValidationHandler [Func[String, Object]] - -```powershell -[Parameter(ParameterSetName = 'Set 1')] -``` - -Specifies a ScriptBlock that is called from ValidateAndAcceptLine. -If a non-null object is returned or an exception is thrown, validation fails and the error is reported. -If the object returned/thrown has a Message property, it's value is used in the error message, and if there is an Offset property, the cursor is moved to that offset after reporting the error. -If there is no Message property, the ToString method is called to report the error. - - -### HistorySearchCursorMovesToEnd [switch] - -```powershell -[Parameter(ParameterSetName = 'Set 1')] -``` - - - - -### MaximumHistoryCount [Int32] = 1024 - -```powershell -[Parameter(ParameterSetName = 'Set 1')] -``` - -Specifies the maximum number of commands to save in PSReadline history. -Note that PSReadline history is separate from PowerShell history. - - -### MaximumKillRingCount [Int32] = 10 - -```powershell -[Parameter(ParameterSetName = 'Set 1')] -``` - -Specifies the maximum number of items stored in the kill ring. - - -### ResetTokenColors [switch] - -```powershell -[Parameter(ParameterSetName = 'Set 1')] -``` - -Restore the token colors to the default settings. - - -### ShowToolTips [switch] - -```powershell -[Parameter(ParameterSetName = 'Set 1')] -``` - -When displaying possible completions, show tooltips in the list of completions. - - -### ExtraPromptLineCount [Int32] = 0 - -```powershell -[Parameter(ParameterSetName = 'Set 1')] -``` - -Use this option if your prompt spans more than one line and you want the extra lines to appear when PSReadline displays the prompt after showing some output, e.g. -when showing a list of completions. - - -### DingTone [Int32] = 1221 - -```powershell -[Parameter(ParameterSetName = 'Set 1')] -``` - -When BellStyle is set to Audible, specifies the tone of the beep. - - -### DingDuration [Int32] = 50ms - -```powershell -[Parameter(ParameterSetName = 'Set 1')] -``` - -When BellStyle is set to Audible, specifies the duration of the beep. - - -### BellStyle [BellStyle] = Audible - -```powershell -[Parameter(ParameterSetName = 'Set 1')] -``` - -Specifies how PSReadline should respond to various error and ambiguous conditions. - -Valid values are: - --- Audible: a short beep - --- Visible: a brief flash is performed - --- None: no feedback - - -### CompletionQueryItems [Int32] = 100 - -```powershell -[Parameter(ParameterSetName = 'Set 1')] -``` - -Specifies the maximum number of completion items that will be shown without prompting. -If the number of items to show is greater than this value, PSReadline will prompt y/n before displaying the completion items. - - -### WordDelimiters [string] = ;:,.[]{}()/\|^&*-=+ - -```powershell -[Parameter(ParameterSetName = 'Set 1')] -``` - -Specifies the characters that delimit words for functions like ForwardWord or KillWord. - - -### HistorySearchCaseSensitive [switch] - -```powershell -[Parameter(ParameterSetName = 'Set 1')] -``` - -Specifies the searching history is case sensitive in functions like ReverseSearchHistory or HistorySearchBackward. - - -### HistorySaveStyle [HistorySaveStyle] = SaveIncrementally - -```powershell -[Parameter(ParameterSetName = 'Set 1')] -``` - -Specifies how PSReadline should save history. - -Valid values are: - --- SaveIncrementally: save history after each command is executed - and share across multiple instances of PowerShell - --- SaveAtExit: append history file when PowerShell exits - --- SaveNothing: don't use a history file - - -### HistorySavePath [String] = ~\AppData\Roaming\PSReadline\$($host.Name)_history.txt - -```powershell -[Parameter(ParameterSetName = 'Set 1')] -``` - -Specifies the path to the history file. - - -### TokenKind [TokenClassification] - -```powershell -[Parameter( - Mandatory = $true, - Position = 0, - ParameterSetName = 'Set 2')] -``` - -Specifies the kind of token when setting token coloring options with the -ForegroundColor and -BackgroundColor parameters. - - -### ForegroundColor [ConsoleColor] - -```powershell -[Parameter( - Position = 1, - ParameterSetName = 'Set 2')] -``` - -Specifies the foreground color for the token kind specified by the parameter -TokenKind. - - -### BackgroundColor [ConsoleColor] - -```powershell -[Parameter( - Position = 2, - ParameterSetName = 'Set 2')] -``` - -Specifies the background color for the token kind specified by the parameter -TokenKind. - - - -## INPUTS -### None -You cannot pipe objects to Set-PSReadlineOption - - -## OUTPUTS -### None -This cmdlet does not generate any output. - - - - -## RELATED LINKS - -[about_PSReadline]() - - diff --git a/src/Microsoft.PowerShell.PSReadLine/en-US/about_PSReadline.help.txt b/src/Microsoft.PowerShell.PSReadLine/en-US/about_PSReadline.help.txt deleted file mode 100644 index 3e1ff65b0c7..00000000000 --- a/src/Microsoft.PowerShell.PSReadLine/en-US/about_PSReadline.help.txt +++ /dev/null @@ -1,628 +0,0 @@ -TOPIC - about_PSReadline - -SHORT DESCRIPTION - - PSReadline provides an improved command line editing experience in - the PowerShell console. - -LONG DESCRIPTION - - PSReadline provides a powerful command line editing experience for the - PowerShell console. It provides: - - * Syntax coloring of the command line - * A visual indication of syntax errors - * A better multi-line experience (both editing and history) - * Customizable key bindings - * Cmd and Emacs modes - * Many configuration options - * Bash style completion (optional in Cmd mode, default in Emacs mode) - * Emacs yank/kill ring - * PowerShell token based "word" movement and kill - - The following functions are available in the class [Microsoft.PowerShell.PSConsoleReadLine]: - - Cursor movement - --------------- - - EndOfLine (Cmd: Emacs: or ) - - If the input has multiple lines, move to the end of the current line, - or if already at the end of the line, move to the end of the input. - If the input has a single line, move to the end of the input. - - BeginningOfLine (Cmd: Emacs: or ) - - If the input has multiple lines, move to the start of the current line, - or if already at the start of the line, move to the start of the input. - If the input has a single line, move to the start of the input. - - NextLine (Cmd: unbound Emacs: unbound) - - Move the cursor to the next line if the input has multiple lines. - - PreviousLine (Cmd: unbound Emacs: unbound) - - Move the cursor to the previous line if the input has multiple lines. - - ForwardChar (Cmd: Emacs: or ) - - Move the cursor one character to the right. This may move the cursor to the next - line of multi-line input. - - BackwardChar (Cmd: Emacs: or ) - - Move the cursor one character to the left. This may move the cursor to the previous - line of multi-line input. - - ForwardWord (Cmd: unbound Emacs: ) - - Move the cursor forward to the end of the current word, or if between words, - to the end of the next word. Word delimiter characters can be set with: - - Set-PSReadlineOption -WordDelimiters - - NextWord (Cmd: Emacs: unbound) - - Move the cursor forward to the start of the next word. Word delimiter characters - can be set with: - - Set-PSReadlineOption -WordDelimiters - - BackwardWord (Cmd: Emacs: ) - - Move the cursor back to the start of the current word, or if between words, - the start of the previous word. Word delimiter characters - can be set with: - - Set-PSReadlineOption -WordDelimiters - - ShellForwardWord (Cmd: unbound Emacs: unbound) - - Like ForwardWord except word boundaries are defined by PowerShell token boundaries. - - ShellNextWord (Cmd: unbound Emacs: unbound) - - Like NextWord except word boundaries are defined by PowerShell token boundaries. - - ShellBackwardWord (Cmd: unbound Emacs: unbound) - - Like BackwardWord except word boundaries are defined by PowerShell token boundaries. - - GotoBrace (Cmd: Emacs: unbound) - - Go to the matching parenthesis, curly brace, or square bracket. - - AddLine (Cmd: Emacs: ) - - The continuation prompt is displayed on the next line and PSReadline waits for - keys to edit the current input. This is useful to enter multi-line input as - a single command even when a single line is complete input by itself. - - Basic editing - ------------- - - CancelLine (Cmd: unbound Emacs: unbound) - - Cancel all editing to the line, leave the line of input on the screen but - return from PSReadline without executing the input. - - RevertLine (Cmd: Emacs: ) - - Reverts all of the input since the last input was accepted and executed. - This is equivalent to doing Undo until there is nothing left to undo. - - BackwardDeleteChar (Cmd: Emacs: or ) - - Delete the character before the cursor. - - DeleteChar (Cmd: Emacs: ) - - Delete the character under the cursor. - - DeleteCharOrExit (Cmd: unbound Emacs: ) - - Like DeleteChar, unless the line is empty, in which case exit the process. - - AcceptLine (Cmd: Emacs: or ) - - Attempt to execute the current input. If the current input is incomplete (for - example there is a missing closing parenthesis, bracket, or quote, then the - continuation prompt is displayed on the next line and PSReadline waits for - keys to edit the current input. - - AcceptAndGetNext (Cmd: unbound Emacs: ) - - Like AcceptLine, but after the line completes, start editing the next line - from history. - - ValidateAndAcceptLine (Cmd: unbound Emacs: unbound) - - Like AcceptLine but performs additional validation including: - - * Check for additional parse errors - * Validate command names are all found - * If using PowerShell V4 or greater, validate the parameters and arguments - - If there are any errors, the error message is displayed and not accepted nor added - to the history unless you either correct the command line or execute AcceptLine or - ValidateAndAcceptLine again while the error message is displayed. - - BackwardDeleteLine (Cmd: Emacs: unbound) - - Delete the text from the start of the input to the cursor. - - ForwardDeleteLine (Cmd: Emacs: unbound) - - Delete the text from the cursor to the end of the input. - - SelectBackwardChar (Cmd: Emacs: ) - - Adjust the current selection to include the previous character. - - SelectForwardChar (Cmd: Emacs: ) - - Adjust the current selection to include the next character. - - SelectBackwardWord (Cmd: Emacs: ) - - Adjust the current selection to include the previous word. - - SelectForwardWord (Cmd: unbound Emacs: ) - - Adjust the current selection to include the next word using ForwardWord. - - SelectNextWord (Cmd: Emacs: unbound) - - Adjust the current selection to include the next word using NextWord. - - SelectShellForwardWord (Cmd: unbound Emacs: unbound) - - Adjust the current selection to include the next word using ShellForwardWord. - - SelectShellNextWord (Cmd: unbound Emacs: unbound) - - Adjust the current selection to include the next word using ShellNextWord. - - SelectShellBackwardWord (Cmd: unbound Emacs: unbound) - - Adjust the current selection to include the previous word using ShellBackwardWord. - - SelectBackwardsLine (Cmd: Emacs: ) - - Adjust the current selection to include from the cursor to the start of the line. - - SelectLine (Cmd: Emacs: ) - - Adjust the current selection to include from the cursor to the end of the line. - - SelectAll (Cmd: Emacs: unbound) - - Select the entire line. Moves the cursor to the end of the line. - - SelfInsert (Cmd: , , ... Emacs: , , ...) - - Insert the key entered. - - Redo (Cmd: Emacs: unbound) - - Redo an insertion or deletion that was undone by Undo. - - Undo (Cmd: Emacs: ) - - Undo a previous insertion or deletion. - - History - ------- - - ClearHistory (Cmd: Alt+F7 Emacs: unbound) - - Clears history in PSReadline. This does not affect PowerShell history. - - PreviousHistory (Cmd: Emacs: or ) - - Replace the current input with the 'previous' item from PSReadline history. - - NextHistory (Cmd: Emacs: or ) - - Replace the current input with the 'next' item from PSReadline history. - - ForwardSearchHistory (Cmd: Emacs: ) - - Search forward from the current history line interactively. - - ReverseSearchHistory (Cmd: Emacs: ) - - Search backward from the current history line interactively. - - HistorySearchBackward (Cmd: Emacs: unbound) - - Replace the current input with the 'previous' item from PSReadline history - that matches the characters between the start and the input and the cursor. - - HistorySearchForward (Cmd: Emacs: unbound) - - Replace the current input with the 'next' item from PSReadline history - that matches the characters between the start and the input and the cursor. - - BeginningOfHistory (Cmd: unbound Emacs: ) - - Replace the current input with the last item from PSReadline history. - - EndOfHistory (Cmd: unbound Emacs: >) - - Replace the current input with the last item in PSReadline history, which - is the possibly empty input that was entered before any history commands. - - Tab Completion - -------------- - - TabCompleteNext (Cmd: Emacs: unbound) - - Attempt to complete the text surrounding the cursor with the next - available completion. - - TabCompletePrevious (Cmd: Emacs: unbound) - - Attempt to complete the text surrounding the cursor with the next - previous completion. - - Complete (Cmd: unbound Emacs: ) - - Attempt to perform completion on the text surrounding the cursor. - If there are multiple possible completions, the longest unambiguous - prefix is used for completion. If trying to complete the longest - unambiguous completion, a list of possible completions is displayed. - - MenuComplete (Cmd: Emacs: ) - - Attempt to perform completion on the text surrounding the cursor. - If there are multiple possible completions, a list of possible - completions is displayed and you can select the correct completion - using the arrow keys or Tab/Shift+Tab. Escape and Ctrl+G cancel - the menu selection and reverts the line to the state before invoking - MenuComplete. - - PossibleCompletions (Cmd: unbound Emacs: ) - - Display the list of possible completions. - - SetMark (Cmd: unbound Emacs: ) - - Mark the current location of the cursor for use in a subsequent editing command. - - ExchangePointAndMark (Cmd: unbound Emacs: ) - - The cursor is placed at the location of the mark and the mark is moved - to the location of the cursor. - - Kill/Yank - --------- - - Kill and yank operate on a clipboard in the PSReadline module. There is a ring - buffer called the kill ring - killed text will be added to the kill ring up - and yank will copy text from the most recent kill. YankPop will cycle through - items in the kill ring. When the kill ring is full, new items will replace the - oldest items. A kill operation that is immediately preceded by another kill operation - will append the previous kill instead of adding a new item or replacing an item - in the kill ring. This is how you can cut a part of a line, say for example with multiple - KillWord operations, then yank them back elsewhere as a single yank. - - KillLine (Cmd: unbound Emacs: ) - - Clear the input from the cursor to the end of the line. The cleared text is placed - in the kill ring. - - BackwardKillLine (Cmd: unbound Emacs: or ) - - Clear the input from the start of the input to the cursor. The cleared text is placed - in the kill ring. - - KillWord (Cmd: unbound Emacs: ) - - Clear the input from the cursor to the end of the current word. If the cursor - is between words, the input is cleared from the cursor to the end of the next word. - The cleared text is placed in the kill ring. - - BackwardKillWord (Cmd: unbound Emacs: ) - - Clear the input from the start of the current word to the cursor. If the cursor - is between words, the input is cleared from the start of the previous word to the - cursor. The cleared text is placed in the kill ring. - - ShellKillWord (Cmd: unbound Emacs: unbound) - - Like KillWord except word boundaries are defined by PowerShell token boundaries. - - ShellBackwardKillWord (Cmd: unbound Emacs: unbound) - - Like BackwardKillWord except word boundaries are defined by PowerShell token boundaries. - - UnixWordRubout (Cmd: unbound Emacs: ) - - Like BackwardKillWord except word boundaries are defined by whitespace. - - KillRegion (Cmd: unbound Emacs: unbound) - - Kill the text between the cursor and the mark. - - Copy (Cmd: Emacs: unbound) - - Copy selected region to the system clipboard. If no region is selected, copy the whole line. - - CopyOrCancelLine (Cmd: Emacs: ) - - Either copy selected text to the clipboard, or if no text is selected, cancel editing - the line with CancelLine. - - Cut (Cmd: Emacs: unbound) - - Delete selected region placing deleted text in the system clipboard. - - Yank (Cmd: unbound Emacs: ) - - Add the most recently killed text to the input. - - YankPop (Cmd: unbound Emacs: ) - - If the previous operation was Yank or YankPop, replace the previously yanked - text with the next killed text from the kill ring. - - ClearKillRing (Cmd: unbound Emacs: unbound) - - The contents of the kill ring are cleared. - - Paste (Cmd: Emacs: unbound) - - This is similar to Yank, but uses the system clipboard instead of the kill ring. - - YankLastArg (Cmd: Emacs: , ) - - Insert the last argument from the previous command in history. Repeated operations - will replace the last inserted argument with the last argument from the previous - command (so Alt+. Alt+. will insert the last argument of the second to last history - line.) - - With an argument, the first time YankLastArg behaves like YankNthArg. A negative - argument on subsequent YankLastArg calls will change the direction while going - through history. For example, if you hit Alt+. one too many times, you can type - Alt+- Alt+. to reverse the direction. - - Arguments are based on PowerShell tokens. - - YankNthArg (Cmd: unbound Emacs: ) - - Insert the first argument (not the command name) of the previous command in history. - - With an argument, insert the nth argument where 0 is typically the command. Negative - arguments start from the end. - - Arguments are based on PowerShell tokens. - - Miscellaneous - ------------- - - Abort (Cmd: unbound Emacs: ) - - Abort the current action, e.g. stop interactive history search. - Does not cancel input like CancelLine. - - CharacterSearch (Cmd: Emacs: ) - - Read a key and search forwards for that character. With an argument, search - forwards for the nth occurrence of that argument. With a negative argument, - searches backwards. - - CharacterSearchBackward (Cmd: Emacs: ) - - Like CharacterSearch, but searches backwards. With a negative argument, searches - forwards. - - ClearScreen (Cmd: Emacs: ) - - Clears the screen and displays the current prompt and input at the top of the screen. - - DigitArgument (Cmd: unbound Emacs: ,,) - - Used to pass numeric arguments to functions like CharacterSearch or YankNthArg. - Alt+- toggles the argument to be negative/non-negative. To enter 80 '*' characters, - you could type Alt+8 Alt+0 *. - - CaptureScreen (Cmd: unbound Emacs: unbound) - - Copies selected lines to the clipboard in both text and rtf formats. Use up/down - arrow keys to the first line to select, then Shift+UpArrow/Shift+DownArrow to select - multiple lines. After selecting, press Enter to copy the text. Escape/Ctrl+C/Ctrl+G - will cancel so nothing is copied to the clipboard. - - InvokePrompt (Cmd: unbound Emacs: unbound) - - Erases the current prompt and calls the prompt function to redisplay - the prompt. Useful for custom key handlers that change state, e.g. - change the current directory. - - WhatIsKey (Cmd: Emacs: ) - - Read a key or chord and display the key binding. - - ShowKeyBindings (Cmd: Emacs: ) - - Show all of the currently bound keys. - - ScrollDisplayUp (Cmd: Emacs: ) - - Scroll the display up one screen. - - ScrollDisplayUpLine (Cmd: Emacs: ) - - Scroll the display up one line. - - ScrollDisplayDown (Cmd: Emacs: ) - - Scroll the display down one screen. - - ScrollDisplayDownLine (Cmd: Emacs: ) - - Scroll the display down one line. - - ScrollDisplayTop (Cmd: unbound Emacs: ) - - Scroll the display to the top. - - ScrollDisplayToCursor (Cmd: unbound Emacs: ) - - Scroll the display to the cursor. - - Custom Key Bindings - ------------------- - - PSReadline supports custom key bindings using the cmdlet Set-PSReadlineKeyHandler. Most - custom key bindings will call one of the above functions, for example: - - Set-PSReadlineKeyHandler -Key UpArrow -Function HistorySearchBackward - - You can bind a ScriptBlock to a key. The ScriptBlock can do pretty much anything you want. - Some useful examples include: - - * edit the command line - * opening a new window (e.g. help) - * change directories without changing the command line - - The ScriptBlock is passed two arguments: - - * $key - A [ConsoleKeyInfo] that is the key that triggered the custom binding. If you bind - the same ScriptBlock to multiple keys and need to perform different actions depending - on the key, you can check $key. Many custom bindings ignore this argument. - * $arg - An arbitrary argument. Most often, this would be an integer argument that the user - passes from the key bindings DigitArgument. If your binding doesn't accept arguments, - it's reasonable to ignore this argument. - - Let's take a look at an example that adds a command line to history without executing it. This is - useful when you realize you forgot to do something, but don't want to re-enter the command line - you've already entered. - - Set-PSReadlineKeyHandler -Key Alt+w ` - -BriefDescription SaveInHistory ` - -LongDescription "Save current line in history but do not execute" ` - -ScriptBlock { - param($key, $arg) # The arguments are ignored in this example - - # We need the command line, GetBufferState gives us that (with the cursor position) - $line = $null - $cursor = $null - [Microsoft.PowerShell.PSConsoleReadLine]::GetBufferState([ref]$line, [ref]$cursor) - - # AddToHistory saves the line in history, but does not execute the line. - [Microsoft.PowerShell.PSConsoleReadLine]::AddToHistory($line) - - # RevertLine is like pressing Escape. - [Microsoft.PowerShell.PSConsoleReadLine]::RevertLine() - } - - You can see many more examples in the file SamplePSReadlineProfile.ps1 which is installed in the - PSReadline module folder. - - Most key bindings will want to take advantage of some helper functions for editing the command - line those APIs are documented in the next section. - - Custom Key Binding Support APIs - ------------------------------- - - The following functions are public in Microsoft.PowerShell.PSConsoleReadline, but cannot be directly - bound to a key. Most are useful in custom key bindings. - - void AddToHistory(string command) - - Add a command line to history without executing it. - - void ClearKillRing() - - Clear the kill ring. This is mostly used for testing. - - void Delete(int start, int length) - - Delete length characters from start. This operation supports undo/redo. - - void Ding() - - Perform the Ding action based on the users preference. - - void GetBufferState([ref] string input, [ref] int cursor) - void GetBufferState([ref] Ast ast, [ref] Token[] tokens, [ref] ParseError[] parseErrors, [ref] int cursor) - - These two functions retrieve useful information about the current state of - the input buffer. The first is more commonly used for simple cases. The - second is used if your binding is doing something more advanced with the Ast. - - IEnumerable[Microsoft.PowerShell.KeyHandler] GetKeyHandlers(bool includeBound, bool includeUnbound) - - This function is used by Get-PSReadlineKeyHandler and probably isn't useful in a custom - key binding. - - Microsoft.PowerShell.PSConsoleReadlineOptions GetOptions() - - This function is used by Get-PSReadlineOption and probably isn't too useful in a custom - key binding. - - void GetSelectionState([ref] int start, [ref] int length) - - If there is no selection on the command line, -1 will be returned in both start and length. - If there is a selection on the command line, the start and length of the selection are returned. - - void Insert(char c) - void Insert(string s) - - Insert a character or string at the cursor. This operation supports undo/redo. - - string ReadLine(runspace remoteRunspace, System.Management.Automation.EngineIntrinsics engineIntrinsics) - - This is the main entry point to PSReadline. It does not support recursion, so is not useful - in a custom key binding. - - void RemoveKeyHandler(string[] key) - - This function is used by Remove-PSReadlineKeyHandler and probably isn't too useful in a - custom key binding. - - void Replace(int start, int length, string replacement) - - Replace some of the input. This operation supports undo/redo. - This is preferred over Delete followed by Insert because it is treated as a single action - for undo. - - void SetCursorPosition(int cursor) - - Move the cursor to the given offset. Cursor movement is not tracked for undo. - - void SetOptions(Microsoft.PowerShell.SetPSReadlineOption options) - - This function is a helper method used by the cmdlet Set-PSReadlineOption, but might be - useful to a custom key binding that wants to temporarily change a setting. - - bool TryGetArgAsInt(System.Object arg, [ref] int numericArg, int defaultNumericArg) - - This helper method is used for custom bindings that honor DigitArgument. A typical call - looks like: - - [int]$numericArg = 0 - [Microsoft.PowerShell.PSConsoleReadLine]::TryGetArgAsInt($arg, [ref]$numericArg, 1) - -POWERSHELL COMPATIBILITY - - PSReadline requires PowerShell version 3 or greater and the console host. It - will not work in the ISE. - -FEEDBACK - - https://github.com/lzybkr/PSReadline - -CONTRIBUTING TO PSREADLINE - - Feel free to submit a pull request or submit feedback on the github page. - -SEE ALSO - - PSReadline is heavily influenced by the GNU Readline library: - - http://tiswww.case.edu/php/chet/readline/rltop.html diff --git a/src/Microsoft.PowerShell.PSReadLine/packages.config b/src/Microsoft.PowerShell.PSReadLine/packages.config deleted file mode 100644 index 0dc84546ba9..00000000000 --- a/src/Microsoft.PowerShell.PSReadLine/packages.config +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/src/Microsoft.PowerShell.SDK/Microsoft.PowerShell.SDK.csproj b/src/Microsoft.PowerShell.SDK/Microsoft.PowerShell.SDK.csproj index 97d99943bac..d3446d28c98 100644 --- a/src/Microsoft.PowerShell.SDK/Microsoft.PowerShell.SDK.csproj +++ b/src/Microsoft.PowerShell.SDK/Microsoft.PowerShell.SDK.csproj @@ -1,7 +1,5 @@ - - - - + + PowerShell SDK metapackage Microsoft.PowerShell.SDK @@ -17,18 +15,20 @@ - - - - - - - - - - - - + + + + + + + + + + + + + + diff --git a/src/Microsoft.PowerShell.ScheduledJob/AssemblyInfo.cs b/src/Microsoft.PowerShell.ScheduledJob/AssemblyInfo.cs deleted file mode 100644 index 8be32293508..00000000000 --- a/src/Microsoft.PowerShell.ScheduledJob/AssemblyInfo.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Reflection; -using System.Resources; - -[assembly:AssemblyFileVersionAttribute("3.0.0.0")] -[assembly:AssemblyVersion("3.0.0.0")] - -[assembly:AssemblyCulture("")] -[assembly:NeutralResourcesLanguage("en-US")] diff --git a/src/Microsoft.PowerShell.ScheduledJob/ScheduledJob.cs b/src/Microsoft.PowerShell.ScheduledJob/ScheduledJob.cs deleted file mode 100644 index c9835bde440..00000000000 --- a/src/Microsoft.PowerShell.ScheduledJob/ScheduledJob.cs +++ /dev/null @@ -1,1279 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Text; -using System.Runtime.Serialization; -using System.Management.Automation; -using System.Management.Automation.Runspaces; -using System.IO; -using System.ComponentModel; -using System.Security.Permissions; -using System.Management.Automation.Host; - -namespace Microsoft.PowerShell.ScheduledJob -{ - /// - /// This is a Job2 derived class that contains a DefinitionJob for - /// running job definition based jobs but can also save and load job - /// results data from file. This class is used to load job result - /// data from previously run jobs so that a user can view results of - /// scheduled job runs. This class also contains the definition of - /// the scheduled job and so can run an instance of the scheduled - /// job and optionally save results to file. - /// - [Serializable] - public sealed class ScheduledJob : Job2, ISerializable - { - #region Private Members - - private ScheduledJobDefinition _jobDefinition; - private Runspace _runspace; - private System.Management.Automation.PowerShell _powerShell; - private Job _job = null; - private bool _asyncJobStop; - private bool _allowSetShouldExit; - private PSHost _host; - - private const string AllowHostSetShouldExit = "AllowSetShouldExitFromRemote"; - - private StatusInfo _statusInfo; - - #endregion - - #region Public Properties - - /// - /// ScheduledJobDefinition. - /// - public ScheduledJobDefinition Definition - { - get { return _jobDefinition; } - internal set { _jobDefinition = value; } - } - - /// - /// Location of job being run. - /// - public override string Location - { - get - { - return Status.Location; - } - } - - /// - /// Status Message associated with the Job. - /// - public override string StatusMessage - { - get - { - return Status.StatusMessage; - } - } - - /// - /// Indicates whether more data is available from Job. - /// - public override bool HasMoreData - { - get - { - return (_job != null) ? - _job.HasMoreData - : - (Output.Count > 0 || - Error.Count > 0 || - Warning.Count > 0 || - Verbose.Count > 0 || - Progress.Count > 0 || - Debug.Count > 0 || - Information.Count > 0 - ); - } - } - - /// - /// Job command string. - /// - public new string Command - { - get - { - return Status.Command; - } - } - - /// - /// Internal property indicating whether a SetShouldExit is honored - /// while running the scheduled job script. - /// - internal bool AllowSetShouldExit - { - get { return _allowSetShouldExit; } - set { _allowSetShouldExit = value; } - } - - #endregion - - #region Constructors - - /// - /// Constructor. - /// - /// Job command string for display - /// Name of job - /// ScheduledJobDefinition defining job to run - public ScheduledJob( - string command, - string name, - ScheduledJobDefinition jobDefinition) : - base(command, name) - { - if (command == null) - { - throw new PSArgumentNullException("command"); - } - if (name == null) - { - throw new PSArgumentNullException("name"); - } - if (jobDefinition == null) - { - throw new PSArgumentNullException("jobDefinition"); - } - - _jobDefinition = jobDefinition; - - PSJobTypeName = ScheduledJobSourceAdapter.AdapterTypeName; - } - - #endregion - - #region Public Overrides - - /// - /// Starts a job as defined by the contained ScheduledJobDefinition object. - /// - public override void StartJob() - { - lock (SyncRoot) - { - if (_job != null && !IsFinishedState(_job.JobStateInfo.State)) - { - string msg = StringUtil.Format(ScheduledJobErrorStrings.JobAlreadyRunning, _jobDefinition.Name); - throw new PSInvalidOperationException(msg); - } - - _statusInfo = null; - _asyncJobStop = false; - PSBeginTime = DateTime.Now; - - if (_powerShell == null) - { - InitialSessionState iss = InitialSessionState.CreateDefault2(); - iss.Commands.Clear(); - iss.Formats.Clear(); - iss.Commands.Add( - new SessionStateCmdletEntry("Start-Job", typeof(Microsoft.PowerShell.Commands.StartJobCommand), null)); - - // Get the default host from the default runspace. - _host = GetDefaultHost(); - _runspace = RunspaceFactory.CreateRunspace(_host, iss); - _runspace.Open(); - _powerShell = System.Management.Automation.PowerShell.Create(); - _powerShell.Runspace = _runspace; - - // Indicate SetShouldExit to host. - AddSetShouldExitToHost(); - } - else - { - _powerShell.Commands.Clear(); - } - - _job = StartJobCommand(_powerShell); - - _job.StateChanged += new EventHandler(HandleJobStateChanged); - SetJobState(_job.JobStateInfo.State); - - // Add all child jobs to this object's list so that - // the user and Receive-Job can retrieve results. - foreach (Job childJob in _job.ChildJobs) - { - this.ChildJobs.Add(childJob); - } - - // Add this job to the local repository. - ScheduledJobSourceAdapter.AddToRepository(this); - } // lock - } - - /// - /// Start job asynchronously. - /// - public override void StartJobAsync() - { - //StartJob(); - throw new PSNotSupportedException(); - } - - /// - /// Stop the job. - /// - public override void StopJob() - { - Job job; - JobState state; - lock (SyncRoot) - { - job = _job; - state = Status.State; - _asyncJobStop = false; - } - - if (IsFinishedState(state)) - { - return; - } - - if (job == null) - { - // Set job state to failed so that it can be removed from the - // cache using Remove-Job. - SetJobState(JobState.Failed); - } - else - { - job.StopJob(); - } - } - - /// - /// Stop the job asynchronously. - /// - public override void StopJobAsync() - { - Job job; - JobState state; - lock (SyncRoot) - { - job = _job; - state = Status.State; - _asyncJobStop = true; - } - - if (IsFinishedState(state)) - { - return; - } - - if (job == null) - { - // Set job state to failed so that it can be removed from the - // cache using Remove-Job. - SetJobState(JobState.Failed); - HandleJobStateChanged(this, - new JobStateEventArgs( - new JobStateInfo(JobState.Failed))); - } - else - { - job.StopJob(); - } - } - - /// - /// SuspendJob. - /// - public override void SuspendJob() - { - throw new PSNotSupportedException(); - } - - /// - /// SuspendJobAsync. - /// - public override void SuspendJobAsync() - { - throw new PSNotSupportedException(); - } - - /// - /// ResumeJob. - /// - public override void ResumeJob() - { - throw new PSNotSupportedException(); - } - - /// - /// ResumeJobAsync. - /// - public override void ResumeJobAsync() - { - throw new PSNotSupportedException(); - } - - /// - /// UnblockJob. - /// - public override void UnblockJob() - { - throw new PSNotSupportedException(); - } - - /// - /// UnblockJobAsync. - /// - public override void UnblockJobAsync() - { - throw new PSNotSupportedException(); - } - - - /// - /// StopJob - /// - /// - /// - public override void StopJob(bool force, string reason) - { - throw new PSNotSupportedException(); - } - - /// - /// StopJobAsync - /// - /// - /// - public override void StopJobAsync(bool force, string reason) - { - throw new PSNotSupportedException(); - } - /// - /// SuspendJob - /// - /// - /// - public override void SuspendJob(bool force, string reason) - { - throw new PSNotSupportedException(); - } - - /// - /// SuspendJobAsync - /// - /// - /// - public override void SuspendJobAsync(bool force, string reason) - { - throw new PSNotSupportedException(); - } - - - #endregion - - #region Implementation of ISerializable - - /// - /// Deserialize constructor. - /// - /// SerializationInfo - /// StreamingContext - [SecurityPermissionAttribute(SecurityAction.Demand, SerializationFormatter = true)] - private ScheduledJob( - SerializationInfo info, - StreamingContext context) - { - if (info == null) - { - throw new PSArgumentNullException("info"); - } - - DeserializeStatusInfo(info); - DeserializeResultsInfo(info); - PSJobTypeName = ScheduledJobSourceAdapter.AdapterTypeName; - } - - /// - /// Serialize method. - /// - /// SerializationInfo - /// StreamingContext - [SecurityPermissionAttribute(SecurityAction.Demand, SerializationFormatter = true)] - public void GetObjectData( - SerializationInfo info, - StreamingContext context) - { - if (info == null) - { - throw new PSArgumentException("info"); - } - - SerializeStatusInfo(info); - SerializeResultsInfo(info); - } - - private void SerializeStatusInfo(SerializationInfo info) - { - StatusInfo statusInfo = new StatusInfo( - InstanceId, - Name, - Location, - Command, - StatusMessage, - (_job != null) ? _job.JobStateInfo.State : JobStateInfo.State, - HasMoreData, - PSBeginTime, - PSEndTime, - _jobDefinition); - - info.AddValue("StatusInfo", statusInfo); - } - - private void SerializeResultsInfo(SerializationInfo info) - { - // All other job information is in the child jobs. - Collection output = new Collection(); - Collection error = new Collection(); - Collection warning = new Collection(); - Collection verbose = new Collection(); - Collection progress = new Collection(); - Collection debug = new Collection(); - Collection information = new Collection(); - - if (_job != null) - { - // Collect data from "live" job. - - if (JobStateInfo.Reason != null) - { - error.Add(new ErrorRecord(JobStateInfo.Reason, "ScheduledJobFailedState", ErrorCategory.InvalidResult, null)); - } - - foreach (var item in _job.Error) - { - error.Add(item); - } - - foreach (Job childJob in ChildJobs) - { - if (childJob.JobStateInfo.Reason != null) - { - error.Add(new ErrorRecord(childJob.JobStateInfo.Reason, "ScheduledJobFailedState", ErrorCategory.InvalidResult, null)); - } - - foreach (var item in childJob.Output) - { - output.Add(item); - } - - foreach (var item in childJob.Error) - { - error.Add(item); - } - - foreach (var item in childJob.Warning) - { - warning.Add(item); - } - - foreach (var item in childJob.Verbose) - { - verbose.Add(item); - } - - foreach (var item in childJob.Progress) - { - progress.Add(item); - } - - foreach (var item in childJob.Debug) - { - debug.Add(item); - } - - foreach (var item in childJob.Information) - { - information.Add(item); - } - } - } - else - { - // Collect data from object collections. - - foreach (var item in Output) - { - // Wrap the base object in a new PSObject. This is necessary because the - // source deserialized PSObject doesn't serialize again correctly and breaks - // PS F&O. Not sure if this is a PSObject serialization bug or not. - output.Add(new PSObject(item.BaseObject)); - } - - foreach (var item in Error) - { - error.Add(item); - } - - foreach (var item in Warning) - { - warning.Add(item); - } - - foreach (var item in Verbose) - { - verbose.Add(item); - } - - foreach (var item in Progress) - { - progress.Add(item); - } - - foreach (var item in Debug) - { - debug.Add(item); - } - - foreach (var item in Information) - { - information.Add(item); - } - } - - ResultsInfo resultsInfo = new ResultsInfo( - output, error, warning, verbose, progress, debug, information); - - info.AddValue("ResultsInfo", resultsInfo); - } - - private void DeserializeStatusInfo(SerializationInfo info) - { - StatusInfo statusInfo = (StatusInfo)info.GetValue("StatusInfo", typeof(StatusInfo)); - - Name = statusInfo.Name; - PSBeginTime = statusInfo.StartTime; - PSEndTime = statusInfo.StopTime; - _jobDefinition = statusInfo.Definition; - SetJobState(statusInfo.State, null); - - lock (SyncRoot) - { - _statusInfo = statusInfo; - } - } - - private void DeserializeResultsInfo(SerializationInfo info) - { - ResultsInfo resultsInfo = (ResultsInfo)info.GetValue("ResultsInfo", typeof(ResultsInfo)); - - // Output - CopyOutput(resultsInfo.Output); - - // Error - CopyError(resultsInfo.Error); - - // Warning - CopyWarning(resultsInfo.Warning); - - // Verbose - CopyVerbose(resultsInfo.Verbose); - - // Progress - CopyProgress(resultsInfo.Progress); - - // Debug - CopyDebug(resultsInfo.Debug); - - // Information - CopyInformation(resultsInfo.Information); - } - - #endregion - - #region Internal Methods - - /// - /// Method to update a ScheduledJob based on new state and - /// result data from a provided Job. - /// - /// ScheduledJob to update from. - internal void Update(ScheduledJob fromJob) - { - // We do not update "live" jobs. - if (_job != null || fromJob == null) - { - return; - } - - // - // Update status. - // - PSEndTime = fromJob.PSEndTime; - JobState state = fromJob.JobStateInfo.State; - if (Status.State != state) - { - SetJobState(state, null); - } - - lock (SyncRoot) - { - _statusInfo = new StatusInfo( - fromJob.InstanceId, - fromJob.Name, - fromJob.Location, - fromJob.Command, - fromJob.StatusMessage, - state, - fromJob.HasMoreData, - fromJob.PSBeginTime, - fromJob.PSEndTime, - fromJob._jobDefinition); - } - - // - // Update results. - // - CopyOutput(fromJob.Output); - CopyError(fromJob.Error); - CopyWarning(fromJob.Warning); - CopyVerbose(fromJob.Verbose); - CopyProgress(fromJob.Progress); - CopyDebug(fromJob.Debug); - CopyInformation(fromJob.Information); - } - - #endregion - - #region Private Methods - - private System.Management.Automation.Host.PSHost GetDefaultHost() - { - System.Management.Automation.PowerShell ps = System.Management.Automation.PowerShell.Create(RunspaceMode.CurrentRunspace).AddScript("$host"); - Collection hosts = ps.Invoke(); - if (hosts == null || hosts.Count == 0) - { - System.Diagnostics.Debug.Assert(false, "Current runspace should always return default host."); - return null; - } - - return hosts[0]; - } - - private Job StartJobCommand(System.Management.Automation.PowerShell powerShell) - { - Job job = null; - - // Use PowerShell Start-Job cmdlet to run job. - powerShell.AddCommand("Start-Job"); - - powerShell.AddParameter("Name", _jobDefinition.Name); - - // Add job parameters from the JobInvocationInfo object. - CommandParameterCollection parameters = _jobDefinition.InvocationInfo.Parameters[0]; - foreach (CommandParameter parameter in parameters) - { - switch (parameter.Name) - { - case "ScriptBlock": - powerShell.AddParameter("ScriptBlock", parameter.Value as ScriptBlock); - break; - - case "FilePath": - powerShell.AddParameter("FilePath", parameter.Value as string); - break; - - case "RunAs32": - powerShell.AddParameter("RunAs32", (bool)parameter.Value); - break; - - case "Authentication": - powerShell.AddParameter("Authentication", (AuthenticationMechanism)parameter.Value); - break; - - case "InitializationScript": - powerShell.AddParameter("InitializationScript", parameter.Value as ScriptBlock); - break; - - case "ArgumentList": - powerShell.AddParameter("ArgumentList", parameter.Value as object[]); - break; - } - } - - // Start the job. - Collection rtn = powerShell.Invoke(); - if (rtn != null && rtn.Count == 1) - { - job = rtn[0].BaseObject as Job; - } - - return job; - } - - private void HandleJobStateChanged(object sender, JobStateEventArgs e) - { - SetJobState(e.JobStateInfo.State); - - if (IsFinishedState(e.JobStateInfo.State)) - { - PSEndTime = DateTime.Now; - - // Dispose the PowerShell and Runspace objects. - System.Management.Automation.PowerShell disposePowerShell = null; - Runspace disposeRunspace = null; - lock (SyncRoot) - { - if (_job != null && - IsFinishedState(_job.JobStateInfo.State)) - { - disposePowerShell = _powerShell; - _powerShell = null; - disposeRunspace = _runspace; - _runspace = null; - } - } - if (disposePowerShell != null) - { - disposePowerShell.Dispose(); - } - if (disposeRunspace != null) - { - disposeRunspace.Dispose(); - } - - // Raise async job stopped event, if needed. - if (_asyncJobStop) - { - _asyncJobStop = false; - OnStopJobCompleted(new AsyncCompletedEventArgs(null, false, null)); - } - - // Remove AllowSetShouldExit from host. - RemoveSetShouldExitFromHost(); - } - } - - internal bool IsFinishedState(JobState state) - { - return (state == JobState.Completed || state == JobState.Failed || state == JobState.Stopped); - } - - private StatusInfo Status - { - get - { - StatusInfo statusInfo; - lock (SyncRoot) - { - if (_statusInfo != null) - { - // Pass back static status. - statusInfo = _statusInfo; - } - else if (_job != null) - { - // Create current job status. - statusInfo = new StatusInfo( - _job.InstanceId, - _job.Name, - _job.Location, - _job.Command, - _job.StatusMessage, - _job.JobStateInfo.State, - _job.HasMoreData, - PSBeginTime, - PSEndTime, - _jobDefinition); - } - else - { - // Create default static empty status. - _statusInfo = new StatusInfo( - Guid.Empty, - string.Empty, - string.Empty, - string.Empty, - string.Empty, - JobState.NotStarted, - false, - PSBeginTime, - PSEndTime, - _jobDefinition); - - statusInfo = _statusInfo; - } - } - - return statusInfo; - } - } - - private void CopyOutput(ICollection fromOutput) - { - PSDataCollection output = CopyResults(fromOutput); - if (output != null) - { - try - { - Output = output; - } - catch (InvalidJobStateException) { } - } - } - - private void CopyError(ICollection fromError) - { - PSDataCollection error = CopyResults(fromError); - if (error != null) - { - try - { - Error = error; - } - catch (InvalidJobStateException) { } - } - } - - private void CopyWarning(ICollection fromWarning) - { - PSDataCollection warning = CopyResults(fromWarning); - if (warning != null) - { - try - { - Warning = warning; - } - catch (InvalidJobStateException) { } - } - } - - private void CopyVerbose(ICollection fromVerbose) - { - PSDataCollection verbose = CopyResults(fromVerbose); - if (verbose != null) - { - try - { - Verbose = verbose; - } - catch (InvalidJobStateException) { } - } - } - - private void CopyProgress(ICollection fromProgress) - { - PSDataCollection progress = CopyResults(fromProgress); - if (progress != null) - { - try - { - Progress = progress; - } - catch (InvalidJobStateException) { } - } - } - - private void CopyDebug(ICollection fromDebug) - { - PSDataCollection debug = CopyResults(fromDebug); - if (debug != null) - { - try - { - Debug = debug; - } - catch (InvalidJobStateException) { } - } - } - - private void CopyInformation(ICollection fromInformation) - { - PSDataCollection information = CopyResults(fromInformation); - if (information != null) - { - try - { - Information = information; - } - catch (InvalidJobStateException) { } - } - } - - private PSDataCollection CopyResults(ICollection fromResults) - { - if (fromResults != null && fromResults.Count > 0) - { - PSDataCollection returnResults = new PSDataCollection(); - foreach (var item in fromResults) - { - returnResults.Add(item); - } - - return returnResults; - } - - return null; - } - - private void AddSetShouldExitToHost() - { - if (!_allowSetShouldExit || _host == null) { return; } - - PSObject hostPrivateData = _host.PrivateData as PSObject; - if (hostPrivateData != null) - { - // Adds or replaces. - hostPrivateData.Properties.Add(new PSNoteProperty(AllowHostSetShouldExit, true)); - } - } - - private void RemoveSetShouldExitFromHost() - { - if (!_allowSetShouldExit || _host == null) { return; } - - PSObject hostPrivateData = _host.PrivateData as PSObject; - if (hostPrivateData != null) - { - // Removes if exists. - hostPrivateData.Properties.Remove(AllowHostSetShouldExit); - } - } - - #endregion - - #region Private ResultsInfo class - - [Serializable] - private class ResultsInfo : ISerializable - { - // Private Members - private Collection _output; - private Collection _error; - private Collection _warning; - private Collection _verbose; - private Collection _progress; - private Collection _debug; - private Collection _information; - - // Properties - internal Collection Output - { - get { return _output; } - } - - internal Collection Error - { - get { return _error; } - } - - internal Collection Warning - { - get { return _warning; } - } - - internal Collection Verbose - { - get { return _verbose; } - } - - internal Collection Progress - { - get { return _progress; } - } - - internal Collection Debug - { - get { return _debug; } - } - - internal Collection Information - { - get { return _information; } - } - - // Constructors - internal ResultsInfo( - Collection output, - Collection error, - Collection warning, - Collection verbose, - Collection progress, - Collection debug, - Collection information - ) - { - if (output == null) - { - throw new PSArgumentNullException("output"); - } - if (error == null) - { - throw new PSArgumentNullException("error"); - } - if (warning == null) - { - throw new PSArgumentNullException("warning"); - } - if (verbose == null) - { - throw new PSArgumentNullException("verbose"); - } - if (progress == null) - { - throw new PSArgumentNullException("progress"); - } - if (debug == null) - { - throw new PSArgumentNullException("debug"); - } - if (information == null) - { - throw new PSArgumentNullException("information"); - } - - _output = output; - _error = error; - _warning = warning; - _verbose = verbose; - _progress = progress; - _debug = debug; - _information = information; - } - - // ISerializable - [SecurityPermissionAttribute(SecurityAction.Demand, SerializationFormatter = true)] - private ResultsInfo( - SerializationInfo info, - StreamingContext context) - { - if (info == null) - { - throw new PSArgumentNullException("info"); - } - - _output = (Collection)info.GetValue("Results_Output", typeof(Collection)); - _error = (Collection)info.GetValue("Results_Error", typeof(Collection)); - _warning = (Collection)info.GetValue("Results_Warning", typeof(Collection)); - _verbose = (Collection)info.GetValue("Results_Verbose", typeof(Collection)); - _progress = (Collection)info.GetValue("Results_Progress", typeof(Collection)); - _debug = (Collection)info.GetValue("Results_Debug", typeof(Collection)); - - try - { - _information = (Collection)info.GetValue("Results_Information", typeof(Collection)); - } - catch(SerializationException) - { - // The job might not have the info stream. Ignore. - _information = new Collection(); - } - } - - [SecurityPermissionAttribute(SecurityAction.Demand, SerializationFormatter = true)] - public void GetObjectData( - SerializationInfo info, - StreamingContext context) - { - if (info == null) - { - throw new PSArgumentException("info"); - } - - info.AddValue("Results_Output", _output); - info.AddValue("Results_Error", _error); - info.AddValue("Results_Warning", _warning); - info.AddValue("Results_Verbose", _verbose); - info.AddValue("Results_Progress", _progress); - info.AddValue("Results_Debug", _debug); - info.AddValue("Results_Information", _information); - } - } - - #endregion - } - - #region Internal StatusInfo Class - - [Serializable] - internal class StatusInfo : ISerializable - { - // Private Members - private Guid _instanceId; - private string _name; - private string _location; - private string _command; - private string _statusMessage; - private JobState _jobState; - private bool _hasMoreData; - private DateTime? _startTime; - private DateTime? _stopTime; - private ScheduledJobDefinition _definition; - - // Properties - internal Guid InstanceId - { - get { return _instanceId; } - } - - internal string Name - { - get { return _name; } - } - - internal string Location - { - get { return _location; } - } - - internal string Command - { - get { return _command; } - } - - internal string StatusMessage - { - get { return _statusMessage; } - } - - internal JobState State - { - get { return _jobState; } - } - - internal bool HasMoreData - { - get { return _hasMoreData; } - } - - internal DateTime? StartTime - { - get { return _startTime; } - } - - internal DateTime? StopTime - { - get { return _stopTime; } - } - - internal ScheduledJobDefinition Definition - { - get { return _definition; } - } - - // Constructors - internal StatusInfo( - Guid instanceId, - string name, - string location, - string command, - string statusMessage, - JobState jobState, - bool hasMoreData, - DateTime? startTime, - DateTime? stopTime, - ScheduledJobDefinition definition) - { - if (definition == null) - { - throw new PSArgumentNullException("definition"); - } - - _instanceId = instanceId; - _name = name; - _location = location; - _command = command; - _statusMessage = statusMessage; - _jobState = jobState; - _hasMoreData = hasMoreData; - _startTime = startTime; - _stopTime = stopTime; - _definition = definition; - } - - // ISerializable - [SecurityPermissionAttribute(SecurityAction.Demand, SerializationFormatter = true)] - private StatusInfo( - SerializationInfo info, - StreamingContext context) - { - if (info == null) - { - throw new PSArgumentNullException("info"); - } - - _instanceId = Guid.Parse(info.GetString("Status_InstanceId")); - _name = info.GetString("Status_Name"); - _location = info.GetString("Status_Location"); - _command = info.GetString("Status_Command"); - _statusMessage = info.GetString("Status_Message"); - _jobState = (JobState)info.GetValue("Status_State", typeof(JobState)); - _hasMoreData = info.GetBoolean("Status_MoreData"); - _definition = (ScheduledJobDefinition)info.GetValue("Status_Definition", typeof(ScheduledJobDefinition)); - - DateTime startTime = info.GetDateTime("Status_StartTime"); - if (startTime != DateTime.MinValue) - { - _startTime = startTime; - } - else - { - _startTime = null; - } - DateTime stopTime = info.GetDateTime("Status_StopTime"); - if (stopTime != DateTime.MinValue) - { - _stopTime = stopTime; - } - else - { - _stopTime = null; - } - } - - [SecurityPermissionAttribute(SecurityAction.Demand, SerializationFormatter = true)] - public void GetObjectData( - SerializationInfo info, - StreamingContext context) - { - if (info == null) - { - throw new PSArgumentNullException("info"); - } - - info.AddValue("Status_InstanceId", _instanceId); - info.AddValue("Status_Name", _name); - info.AddValue("Status_Location", _location); - info.AddValue("Status_Command", _command); - info.AddValue("Status_Message", _statusMessage); - info.AddValue("Status_State", _jobState); - info.AddValue("Status_MoreData", _hasMoreData); - info.AddValue("Status_Definition", _definition); - - if (_startTime != null) - { - info.AddValue("Status_StartTime", _startTime); - } - else - { - info.AddValue("Status_StartTime", DateTime.MinValue); - } - if (_stopTime != null) - { - info.AddValue("Status_StopTime", _stopTime); - } - else - { - info.AddValue("Status_StopTime", DateTime.MinValue); - } - } - } - - #endregion -} diff --git a/src/Microsoft.PowerShell.ScheduledJob/ScheduledJobDefinition.cs b/src/Microsoft.PowerShell.ScheduledJob/ScheduledJobDefinition.cs deleted file mode 100644 index 9f678ec597f..00000000000 --- a/src/Microsoft.PowerShell.ScheduledJob/ScheduledJobDefinition.cs +++ /dev/null @@ -1,2580 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ -using System; -using System.Collections.ObjectModel; -using System.Collections.Generic; -using System.Runtime.Serialization; -using System.Management.Automation; -using System.Management.Automation.Runspaces; -using System.Globalization; -using System.IO; -using System.Management.Automation.Tracing; -using System.Text.RegularExpressions; -using System.Security.Permissions; - -namespace Microsoft.PowerShell.ScheduledJob -{ - /// - /// This class contains all information needed to define a PowerShell job that - /// can be scheduled to run through either stand-alone or through the Windows - /// Task Scheduler. - /// - [Serializable] - public sealed class ScheduledJobDefinition : ISerializable, IDisposable - { - #region Private Members - - private JobInvocationInfo _invocationInfo; - private ScheduledJobOptions _options; - private PSCredential _credential; - private Guid _globalId = Guid.NewGuid(); - private string _name = string.Empty; - private int _id = GetCurrentId(); - private int _executionHistoryLength = DefaultExecutionHistoryLength; - private bool _enabled = true; - private Dictionary _triggers = new Dictionary(); - private Int32 _currentTriggerId; - - private string _definitionFilePath; - private string _definitionOutputPath; - - private bool _isDisposed; - - // Task Action strings. - private const string TaskExecutionPath = @"powershell.exe"; - private const string TaskArguments = @"-NoLogo -NonInteractive -WindowStyle Hidden -Command ""Import-Module PSScheduledJob; $jobDef = [Microsoft.PowerShell.ScheduledJob.ScheduledJobDefinition]::LoadFromStore('{0}', '{1}'); $jobDef.Run()"""; - private static object LockObject = new object(); - private static int CurrentId = 0; - private static int DefaultExecutionHistoryLength = 32; - - internal static ScheduledJobDefinitionRepository Repository = new ScheduledJobDefinitionRepository(); - - // Task Scheduler COM error codes. - private const int TSErrorDisabledTask = -2147216602; - - #endregion - - #region Public Properties - - /// - /// Contains information needed to run the job such as script parameters, - /// job definition, user credentials, etc. - /// - public JobInvocationInfo InvocationInfo - { - get { return _invocationInfo; } - } - - /// - /// Contains the script commands that define the job. - /// - public JobDefinition Definition - { - get { return _invocationInfo.Definition; } - } - - /// - /// Specifies Task Scheduler options for the scheduled job. - /// - public ScheduledJobOptions Options - { - get { return new ScheduledJobOptions(_options); } - } - - /// - /// Credential. - /// - public PSCredential Credential - { - get { return _credential; } - internal set { _credential = value; } - } - - /// - /// An array of trigger objects that specify a time/condition - /// for when the job is run. - /// - public List JobTriggers - { - get - { - List notFoundIds; - return GetTriggers(null, out notFoundIds); - } - } - - /// - /// Local instance Id for object instance. - /// - public int Id - { - get { return _id; } - } - - /// - /// Global Id for scheduled job definition. - /// - public Guid GlobalId - { - get { return _globalId; } - } - - /// - /// Name of scheduled job definition. - /// - public string Name - { - get { return _name; } - } - - /// - /// Job command. - /// - public string Command - { - get { return _invocationInfo.Command; } - } - - /// - /// Returns the maximum number of job execution data - /// allowed in the job store. - /// - public int ExecutionHistoryLength - { - get { return _executionHistoryLength; } - } - - /// - /// Determines whether this scheduled job definition is enabled - /// in Task Scheduler. - /// - public bool Enabled - { - get { return _enabled; } - } - - /// - /// Returns the PowerShell command line execution path. - /// - public string PSExecutionPath - { - get { return TaskExecutionPath; } - } - - /// - /// Returns PowerShell command line arguments to run - /// the scheduled job. - /// - public string PSExecutionArgs - { - get - { - // Escape single quotes in name. Double quotes are not allowed - // and are caught during name validation. - string nameEscapeQuotes = _invocationInfo.Name.Replace("'", "''"); - - return string.Format(CultureInfo.InvariantCulture, TaskArguments, nameEscapeQuotes, _definitionFilePath); - } - } - - /// - /// Returns the job run output path for this job definition. - /// - internal string OutputPath - { - get { return _definitionOutputPath; } - } - - #endregion - - #region Constructors - - /// - /// Default constructor is not accessible. - /// - private ScheduledJobDefinition() - { } - - /// - /// Constructor. - /// - /// Information to invoke Job - /// ScheduledJobTriggers - /// ScheduledJobOptions - /// Credential - public ScheduledJobDefinition( - JobInvocationInfo invocationInfo, - IEnumerable triggers, - ScheduledJobOptions options, - PSCredential credential) - { - if (invocationInfo == null) - { - throw new PSArgumentNullException("invocationInfo"); - } - - _name = invocationInfo.Name; - _invocationInfo = invocationInfo; - - SetTriggers(triggers, false); - _options = (options != null) ? new ScheduledJobOptions(options) : - new ScheduledJobOptions(); - _options.JobDefinition = this; - - _credential = credential; - } - - #endregion - - #region ISerializable Implementation - - /// - /// Serialization constructor. - /// - /// SerializationInfo - /// StreamingContext - [SecurityPermissionAttribute(SecurityAction.Demand, SerializationFormatter = true)] - private ScheduledJobDefinition( - SerializationInfo info, - StreamingContext context) - { - if (info == null) - { - throw new PSArgumentNullException("info"); - } - - _options = (ScheduledJobOptions)info.GetValue("Options_Member", typeof(ScheduledJobOptions)); - _globalId = Guid.Parse(info.GetString("GlobalId_Member")); - _name = info.GetString("Name_Member"); - _executionHistoryLength = info.GetInt32("HistoryLength_Member"); - _enabled = info.GetBoolean("Enabled_Member"); - _triggers = (Dictionary)info.GetValue("Triggers_Member", typeof(Dictionary)); - _currentTriggerId = info.GetInt32("CurrentTriggerId_Member"); - _definitionFilePath = info.GetString("FilePath_Member"); - _definitionOutputPath = info.GetString("OutputPath_Member"); - - object invocationObject = info.GetValue("InvocationInfo_Member", typeof(object)); - _invocationInfo = invocationObject as JobInvocationInfo; - - // Set the JobDefinition reference for the ScheduledJobTrigger and - // ScheduledJobOptions objects. - _options.JobDefinition = this; - foreach (ScheduledJobTrigger trigger in _triggers.Values) - { - trigger.JobDefinition = this; - } - - // Instance information. - _isDisposed = false; - } - - /// - /// Serialization constructor. - /// - /// SerializationInfo - /// StreamingContext - [SecurityPermissionAttribute(SecurityAction.Demand, SerializationFormatter = true)] - public void GetObjectData(SerializationInfo info, StreamingContext context) - { - if (info == null) - { - throw new PSArgumentNullException("info"); - } - - info.AddValue("Options_Member", _options); - info.AddValue("GlobalId_Member", _globalId.ToString()); - info.AddValue("Name_Member", _name); - info.AddValue("HistoryLength_Member", _executionHistoryLength); - info.AddValue("Enabled_Member", _enabled); - info.AddValue("Triggers_Member", _triggers); - info.AddValue("CurrentTriggerId_Member", _currentTriggerId); - info.AddValue("FilePath_Member", _definitionFilePath); - info.AddValue("OutputPath_Member", _definitionOutputPath); - - info.AddValue("InvocationInfo_Member", _invocationInfo); - } - - #endregion - - #region Private Methods - - /// - /// Updates existing information if scheduled job already exists. - /// WTS entry includes command line, options, and trigger conditions. - /// - private void UpdateWTSFromDefinition() - { - using (ScheduledJobWTS taskScheduler = new ScheduledJobWTS()) - { - taskScheduler.UpdateTask(this); - } - } - - /// - /// Compares the current ScheduledJobDefinition task scheduler information - /// with the corresponding information stored in Task Scheduler. If the - /// information is different then the task scheduler information in this - /// object is updated to match what is in Task Scheduler, since that information - /// takes precedence. - /// - /// Task Scheduler information: - /// - Triggers - /// - Options - /// - Enabled state - /// - /// Boolean if this object data is modified. - private bool UpdateDefinitionFromWTS() - { - bool dataModified = false; - - // Get information from Task Scheduler. - using (ScheduledJobWTS taskScheduler = new ScheduledJobWTS()) - { - bool wtsEnabled = taskScheduler.GetTaskEnabled(_name); - ScheduledJobOptions wtsOptions = taskScheduler.GetJobOptions(_name); - Collection wtsTriggers = taskScheduler.GetJobTriggers(_name); - - // - // Compare with existing object data and modify if necessary. - // - - // Enabled. - if (wtsEnabled != _enabled) - { - _enabled = wtsEnabled; - dataModified = true; - } - - // Options. - if (wtsOptions.DoNotAllowDemandStart != _options.DoNotAllowDemandStart || - wtsOptions.IdleDuration != _options.IdleDuration || - wtsOptions.IdleTimeout != _options.IdleTimeout || - wtsOptions.MultipleInstancePolicy != _options.MultipleInstancePolicy || - wtsOptions.RestartOnIdleResume != _options.RestartOnIdleResume || - wtsOptions.RunElevated != _options.RunElevated || - wtsOptions.RunWithoutNetwork != _options.RunWithoutNetwork || - wtsOptions.ShowInTaskScheduler != _options.ShowInTaskScheduler || - wtsOptions.StartIfNotIdle != _options.StartIfNotIdle || - wtsOptions.StartIfOnBatteries != _options.StartIfOnBatteries || - wtsOptions.StopIfGoingOffIdle != _options.StopIfGoingOffIdle || - wtsOptions.StopIfGoingOnBatteries != _options.StopIfGoingOnBatteries || - wtsOptions.WakeToRun != _options.WakeToRun) - { - // Keep the current scheduled job definition reference. - wtsOptions.JobDefinition = _options.JobDefinition; - _options = wtsOptions; - dataModified = true; - } - - // Triggers. - if (_triggers.Count != wtsTriggers.Count) - { - SetTriggers(wtsTriggers, false); - dataModified = true; - } - else - { - bool foundTriggerDiff = false; - - // Compare each trigger object. - foreach (var wtsTrigger in wtsTriggers) - { - if (_triggers.ContainsKey(wtsTrigger.Id) == false) - { - foundTriggerDiff = true; - break; - } - - ScheduledJobTrigger trigger = _triggers[wtsTrigger.Id]; - if (trigger.DaysOfWeek != wtsTrigger.DaysOfWeek || - trigger.Enabled != wtsTrigger.Enabled || - trigger.Frequency != wtsTrigger.Frequency || - trigger.Interval != wtsTrigger.Interval || - trigger.RandomDelay != wtsTrigger.RandomDelay || - trigger.At != wtsTrigger.At || - trigger.User != wtsTrigger.User) - { - foundTriggerDiff = true; - break; - } - } - - if (foundTriggerDiff) - { - SetTriggers(wtsTriggers, false); - dataModified = true; - } - } - } - - return dataModified; - } - - /// - /// Adds this scheduled job definition to the Task Scheduler. - /// - private void AddToWTS() - { - using (ScheduledJobWTS taskScheduler = new ScheduledJobWTS()) - { - taskScheduler.CreateTask(this); - } - } - - /// - /// Removes this scheduled job definition from the Task Scheduler. - /// This operation will fail if a current instance of this job definition - /// is running. - /// If force == true then all current instances will be stopped. - /// - /// Force removal and stop all running instances. - private void RemoveFromWTS(bool force) - { - using (ScheduledJobWTS taskScheduler = new ScheduledJobWTS()) - { - taskScheduler.RemoveTask(this, force); - } - } - - /// - /// Adds this scheduled job definition to the job definition store. - /// - private void AddToJobStore() - { - FileStream fs = null; - try - { - fs = ScheduledJobStore.CreateFileForJobDefinition(Name); - _definitionFilePath = ScheduledJobStore.GetJobDefinitionLocation(); - _definitionOutputPath = ScheduledJobStore.GetJobRunOutputDirectory(Name); - - XmlObjectSerializer serializer = new System.Runtime.Serialization.NetDataContractSerializer(); - serializer.WriteObject(fs, this); - fs.Flush(); - } - finally - { - if (fs != null) - { - fs.Close(); - } - } - - // If credentials are provided then update permissions. - if (Credential != null) - { - UpdateFilePermissions(Credential.UserName); - } - } - - /// - /// Updates existing file with this definition information. - /// - private void UpdateJobStore() - { - FileStream fs = null; - try - { - // Overwrite the existing file. - fs = GetFileStream( - Name, - _definitionFilePath, - FileMode.Create, - FileAccess.Write, - FileShare.None); - - XmlObjectSerializer serializer = new System.Runtime.Serialization.NetDataContractSerializer(); - serializer.WriteObject(fs, this); - fs.Flush(); - } - finally - { - if (fs != null) - { - fs.Close(); - } - } - - // If credentials are provided then update permissions. - if (Credential != null) - { - UpdateFilePermissions(Credential.UserName); - } - } - - /// - /// Updates definition file permissions for provided user account. - /// - /// Account user name - private void UpdateFilePermissions(string user) - { - Exception ex = null; - try - { - // Add user for read access to the job definition file. - ScheduledJobStore.SetReadAccessOnDefinitionFile(Name, user); - - // Add user for write access to the job run Output directory. - ScheduledJobStore.SetWriteAccessOnJobRunOutput(Name, user); - } - catch (System.Security.Principal.IdentityNotMappedException e) - { - ex = e; - } - catch (IOException e) - { - ex = e; - } - catch (UnauthorizedAccessException e) - { - ex = e; - } - catch (ArgumentNullException e) - { - ex = e; - } - - if (ex != null) - { - string msg = StringUtil.Format(ScheduledJobErrorStrings.ErrorSettingAccessPermissions, this.Name, Credential.UserName); - throw new ScheduledJobException(msg, ex); - } - } - - /// - /// Removes this scheduled job definition from the job definition store. - /// - private void RemoveFromJobStore() - { - ScheduledJobStore.RemoveJobDefinition(Name); - } - - /// - /// Throws exception if object is disposed. - /// - private void IsDisposed() - { - if (_isDisposed == true) - { - string msg = StringUtil.Format(ScheduledJobErrorStrings.DefinitionObjectDisposed, Name); - throw new RuntimeException(msg); - } - } - - /// - /// If repository is empty try refreshing it from the store. - /// - private void LoadRepository() - { - ScheduledJobDefinition.RefreshRepositoryFromStore(); - } - - /// - /// Validates all triggers in collection. An exception is thrown - /// for invalid triggers. - /// - /// - private void ValidateTriggers(IEnumerable triggers) - { - if (triggers != null) - { - foreach (var trigger in triggers) - { - trigger.Validate(); - } - } - } - - /// - /// Validates the job definition name. Since the job definition - /// name is used in the job store as a directory name, make sure - /// it does not contain any invalid characters. - /// - private static void ValidateName(string name) - { - if (name.IndexOfAny(System.IO.Path.GetInvalidFileNameChars()) != -1) - { - string msg = StringUtil.Format(ScheduledJobErrorStrings.InvalidJobDefName, name); - throw new ScheduledJobException(msg); - } - } - - /// - /// Iterates through all job run files, opens each job - /// run and renames it to the provided new name. - /// - /// New job run name. - private void UpdateJobRunNames( - string newDefName) - { - // Job run results will be under the new scheduled job definition name. - Collection jobRuns = ScheduledJobSourceAdapter.GetJobRuns(newDefName); - if (jobRuns == null) - { - return; - } - - // Load and rename each job. - ScheduledJobDefinition definition = ScheduledJobDefinition.LoadFromStore(newDefName, null); - foreach (DateTime jobRun in jobRuns) - { - ScheduledJob job = null; - try - { - job = ScheduledJobSourceAdapter.LoadJobFromStore(definition.Name, jobRun) as ScheduledJob; - } - catch (ScheduledJobException) - { - continue; - } - catch (DirectoryNotFoundException) - { - continue; - } - catch (FileNotFoundException) - { - continue; - } - catch (UnauthorizedAccessException) - { - continue; - } - catch (IOException) - { - continue; - } - - if (job != null) - { - job.Name = newDefName; - job.Definition = definition; - ScheduledJobSourceAdapter.SaveJobToStore(job); - } - } - } - - /// - /// Handles known Task Scheduler COM error codes. - /// - /// COMException - /// Error message - private string ConvertCOMErrorCode(System.Runtime.InteropServices.COMException e) - { - string msg = null; - switch (e.ErrorCode) - { - case TSErrorDisabledTask: - msg = ScheduledJobErrorStrings.ReasonTaskDisabled; - break; - } - - return msg; - } - - #endregion - - #region Internal Methods - - /// - /// Save object to store. - /// - internal void SaveToStore() - { - IsDisposed(); - - UpdateJobStore(); - } - - /// - /// Compares the task scheduler information in this object with - /// what is stored in Task Scheduler. If there is a difference - /// then this object is updated with the information from Task - /// Scheduler and saved to the job store. - /// - internal void SyncWithWTS() - { - Exception notFoundEx = null; - try - { - if (UpdateDefinitionFromWTS()) - { - SaveToStore(); - } - } - catch (DirectoryNotFoundException e) - { - notFoundEx = e; - } - catch (FileNotFoundException e) - { - notFoundEx = e; - } - - if (notFoundEx != null) - { - // There is no corresponding Task Scheduler item for this - // scheduled job definition. Remove this definition from - // the job store for consistency. - Remove(true); - throw notFoundEx; - } - } - - /// - /// Renames scheduled job definition, store directory and task scheduler task. - /// - /// New name of job definition - internal void RenameAndSave(string newName) - { - if (InvocationInfo.Name.Equals(newName, StringComparison.OrdinalIgnoreCase)) - { - return; - } - - ValidateName(newName); - - // Attempt to rename job store directory. Detect if new name - // is not unique. - string oldName = InvocationInfo.Name; - Exception ex = null; - try - { - ScheduledJobStore.RenameScheduledJobDefDir(oldName, newName); - } - catch (ArgumentException e) - { - ex = e; - } - catch (DirectoryNotFoundException e) - { - ex = e; - } - catch (FileNotFoundException e) - { - ex = e; - } - catch (UnauthorizedAccessException e) - { - ex = e; - } - catch (IOException e) - { - ex = e; - } - if (ex != null) - { - string msg; - if (!string.IsNullOrEmpty(ex.Message)) - { - msg = StringUtil.Format(ScheduledJobErrorStrings.ErrorRenamingScheduledJobWithMessage, oldName, newName, ex.Message); - } - else - { - msg = StringUtil.Format(ScheduledJobErrorStrings.ErrorRenamingScheduledJob, oldName, newName); - } - throw new ScheduledJobException(msg, ex); - } - - try - { - // Remove old named Task Scheduler task. - // This also stops any existing running job. - RemoveFromWTS(true); - - // Update job definition names. - _name = newName; - InvocationInfo.Name = newName; - InvocationInfo.Definition.Name = newName; - _definitionOutputPath = ScheduledJobStore.GetJobRunOutputDirectory(Name); - - // Update job definition in new job store location. - UpdateJobStore(); - - // Add new Task Scheduler task with new name. - // Jobs can start running again. - AddToWTS(); - - // Update any existing job run names. - UpdateJobRunNames(newName); - } - catch (ArgumentException e) - { - ex = e; - } - catch (DirectoryNotFoundException e) - { - ex = e; - } - catch (FileNotFoundException e) - { - ex = e; - } - catch (UnauthorizedAccessException e) - { - ex = e; - } - catch (IOException e) - { - ex = e; - } - finally - { - // Clear job run cache since job runs now appear in new directory location. - ScheduledJobSourceAdapter.ClearRepository(); - } - - // If any part of renaming the various scheduled job components fail, - // aggressively remove scheduled job corrupted state and inform user. - if (ex != null) - { - try - { - Remove(true); - } - catch (ScheduledJobException e) - { - ex.Data.Add("SchedJobRemoveError", e); - } - - string msg; - if (!string.IsNullOrEmpty(ex.Message)) - { - msg = StringUtil.Format(ScheduledJobErrorStrings.BrokenRenamingScheduledJobWithMessage, oldName, newName, ex.Message); - } - else - { - msg = StringUtil.Format(ScheduledJobErrorStrings.BrokenRenamingScheduledJob, oldName, newName); - } - throw new ScheduledJobException(msg, ex); - } - } - - #endregion - - #region Public Methods - - /// - /// Registers this scheduled job definition object by doing the - /// following: - /// a) Writing this object to the scheduled job object store. - /// b) Registering this job as a Windows Task Scheduler task. - /// c) Adding this object to the local repository. - /// - public void Register() - { - IsDisposed(); - - LoadRepository(); - - ValidateName(Name); - - // First add to the job store. If an exception occurs here - // then this method fails with no clean up. - Exception ex = null; - bool corruptedFile = false; - try - { - AddToJobStore(); - } - catch (ArgumentException e) - { - ex = e; - } - catch (DirectoryNotFoundException e) - { - ex = e; - } - catch (FileNotFoundException e) - { - ex = e; - } - catch (UnauthorizedAccessException e) - { - ex = e; - } - catch (IOException e) - { - ex = e; - } - catch (System.Runtime.Serialization.SerializationException e) - { - corruptedFile = true; - ex = e; - } - catch (System.Runtime.Serialization.InvalidDataContractException e) - { - corruptedFile = true; - ex = e; - } - catch (ScheduledJobException e) - { - // Can be thrown for error setting file access permissions with supplied credentials. - // But file is not considered corrupted if it already exists. - corruptedFile = !(e.FQEID.Equals(ScheduledJobStore.ScheduledJobDefExistsFQEID, StringComparison.OrdinalIgnoreCase)); - ex = e; - } - - if (ex != null) - { - if (corruptedFile) - { - // Remove from store. - try - { - ScheduledJobStore.RemoveJobDefinition(Name); - } - catch (DirectoryNotFoundException) - { } - catch (FileNotFoundException) - { } - catch (UnauthorizedAccessException) - { } - catch (IOException) - { } - } - - if (!(ex is ScheduledJobException)) - { - // Wrap in ScheduledJobException type. - string msg = StringUtil.Format(ScheduledJobErrorStrings.ErrorRegisteringDefinitionStore, this.Name); - throw new ScheduledJobException(msg, ex); - } - else - { - // Otherwise just re-throw. - throw ex; - } - } - - // Next register with the Task Scheduler. - ex = null; - try - { - AddToWTS(); - } - catch (ArgumentException e) - { - ex = e; - } - catch (DirectoryNotFoundException e) - { - ex = e; - } - catch (FileNotFoundException e) - { - ex = e; - } - catch (UnauthorizedAccessException e) - { - ex = e; - } - catch (IOException e) - { - ex = e; - } - catch (System.Runtime.InteropServices.COMException e) - { - ex = e; - } - if (ex != null) - { - // Clean up job store. - RemoveFromJobStore(); - - string msg = StringUtil.Format(ScheduledJobErrorStrings.ErrorRegisteringDefinitionTask, - this.Name, - (string.IsNullOrEmpty(ex.Message) == false) ? ex.Message : string.Empty); - throw new ScheduledJobException(msg, ex); - } - - // Finally add to the local repository. - Repository.AddOrReplace(this); - } - - /// - /// Saves this scheduled job definition object: - /// a) Rewrites this object to the scheduled job object store. - /// b) Updates the Windows Task Scheduler task. - /// - public void Save() - { - IsDisposed(); - - LoadRepository(); - - ValidateName(Name); - - // First update the Task Scheduler. If an exception occurs here then - // we fail with no clean up. - Exception ex = null; - try - { - UpdateWTSFromDefinition(); - } - catch (ArgumentException e) - { - ex = e; - } - catch (DirectoryNotFoundException e) - { - ex = e; - } - catch (FileNotFoundException e) - { - ex = e; - } - catch (UnauthorizedAccessException e) - { - ex = e; - } - catch (IOException e) - { - ex = e; - } - catch (System.Runtime.InteropServices.COMException e) - { - ex = e; - } - if (ex != null) - { - // We want this object to remain synchronized with what is in WTS. - SyncWithWTS(); - - string msg = StringUtil.Format(ScheduledJobErrorStrings.ErrorUpdatingDefinitionTask, this.Name); - throw new ScheduledJobException(msg, ex); - } - - // Next save to job store. - ex = null; - try - { - UpdateJobStore(); - } - catch (ArgumentException e) - { - ex = e; - } - catch (DirectoryNotFoundException e) - { - ex = e; - } - catch (FileNotFoundException e) - { - ex = e; - } - catch (UnauthorizedAccessException e) - { - ex = e; - } - catch (IOException e) - { - ex = e; - } - if (ex != null) - { - // Remove this from WTS for consistency. - RemoveFromWTS(true); - - string msg = StringUtil.Format(ScheduledJobErrorStrings.ErrorUpdatingDefinitionStore, this.Name); - throw new ScheduledJobException(msg, ex); - } - - // Finally update this object in the local repository. - ScheduledJobDefinition.RefreshRepositoryFromStore(); - Repository.AddOrReplace(this); - } - - /// - /// Removes this definition object: - /// a) Removes from the Task Scheduler - /// or fails if an instance is currently running. - /// or stops any running instances if force is true. - /// b) Removes from the scheduled job definition store. - /// c) Removes from the local repository. - /// d) Disposes this object. - /// - public void Remove(bool force) - { - IsDisposed(); - - // First remove from Task Scheduler. Catch not found - // exceptions and continue. - try - { - RemoveFromWTS(force); - } - catch (System.IO.DirectoryNotFoundException) - { - // Continue with removal. - } - catch (System.IO.FileNotFoundException) - { - // Continue with removal. - } - - // Remove from the Job Store. Catch exceptions and continue - // with removal. - Exception ex = null; - try - { - RemoveFromJobStore(); - } - catch (DirectoryNotFoundException) - { - } - catch (FileNotFoundException) - { - } - catch (ArgumentException e) - { - ex = e; - } - catch (UnauthorizedAccessException e) - { - ex = e; - } - catch (IOException e) - { - ex = e; - } - finally - { - // Remove from the local repository. - Repository.Remove(this); - - // Remove job runs for this definition from local repository. - ScheduledJobSourceAdapter.ClearRepositoryForDefinition(this.Name); - - // Dispose this object. - Dispose(); - } - - if (ex != null) - { - string msg = StringUtil.Format(ScheduledJobErrorStrings.ErrorRemovingDefinitionStore, this.Name); - throw new ScheduledJobException(msg, ex); - } - } - - /// - /// Starts the scheduled job immediately. A ScheduledJob object is - /// returned that represents the running command, and this returned - /// job is also added to the local job repository. Job results are - /// not written to the job store. - /// - /// ScheduledJob object for running job - public ScheduledJob StartJob() - { - IsDisposed(); - - ScheduledJob job = new ScheduledJob(_invocationInfo.Command, _invocationInfo.Name, this); - job.StartJob(); - - return job; - } - - /// - /// Starts registered job definition running from the Task Scheduler. - /// - public void RunAsTask() - { - IsDisposed(); - - Exception ex = null; - string reason = null; - try - { - using (ScheduledJobWTS taskScheduler = new ScheduledJobWTS()) - { - taskScheduler.RunTask(this); - } - } - catch (System.IO.DirectoryNotFoundException e) - { - reason = ScheduledJobErrorStrings.reasonJobNotFound; - ex = e; - } - catch (System.IO.FileNotFoundException e) - { - reason = ScheduledJobErrorStrings.reasonJobNotFound; - ex = e; - } - catch (System.Runtime.InteropServices.COMException e) - { - reason = ConvertCOMErrorCode(e); - ex = e; - } - - if (ex != null) - { - string msg; - if (reason != null) - { - msg = StringUtil.Format(ScheduledJobErrorStrings.ErrorRunningAsTaskWithReason, this.Name, reason); - } - else - { - msg = StringUtil.Format(ScheduledJobErrorStrings.ErrorRunningAsTask, this.Name); - } - - throw new ScheduledJobException(msg, ex); - } - } - - #endregion - - #region Public Trigger Methods - - /// - /// Adds new ScheduledJobTriggers. - /// - /// Collection of ScheduledJobTrigger objects - /// Update Windows Task Scheduler and save to store - public void AddTriggers( - IEnumerable triggers, - bool save) - { - IsDisposed(); - - if (triggers == null) - { - throw new PSArgumentNullException("triggers"); - } - - // First validate all triggers. - ValidateTriggers(triggers); - - Collection newTriggerIds = new Collection(); - foreach (ScheduledJobTrigger trigger in triggers) - { - ScheduledJobTrigger newTrigger = new ScheduledJobTrigger(trigger); - - newTrigger.Id = ++_currentTriggerId; - newTriggerIds.Add(newTrigger.Id); - newTrigger.JobDefinition = this; - _triggers.Add(newTrigger.Id, newTrigger); - } - - if (save) - { - Save(); - } - } - - /// - /// Removes triggers matching passed in trigger Ids. - /// - /// Trigger Ids to remove - /// Update Windows Task Scheduler and save to store - /// Trigger Ids not found. - public List RemoveTriggers( - IEnumerable triggerIds, - bool save) - { - IsDisposed(); - - List idsNotFound = new List(); - bool triggerFound = false; - - // triggerIds is null then remove all triggers. - if (triggerIds == null) - { - _currentTriggerId = 0; - if (_triggers.Count > 0) - { - triggerFound = true; - - foreach (ScheduledJobTrigger trigger in _triggers.Values) - { - trigger.Id = 0; - trigger.JobDefinition = null; - } - - // Create new empty trigger collection. - _triggers = new Dictionary(); - } - } - else - { - foreach (Int32 removeId in triggerIds) - { - if (_triggers.ContainsKey(removeId)) - { - _triggers[removeId].JobDefinition = null; - _triggers[removeId].Id = 0; - _triggers.Remove(removeId); - triggerFound = true; - } - else - { - idsNotFound.Add(removeId); - } - } - } - - if (save && triggerFound) - { - Save(); - } - - return idsNotFound; - } - - /// - /// Updates triggers with provided trigger objects, matching passed in - /// trigger Id with existing trigger Id. - /// - /// Collection of ScheduledJobTrigger objects to update - /// Update Windows Task Scheduler and save to store - /// Trigger Ids not found. - public List UpdateTriggers( - IEnumerable triggers, - bool save) - { - IsDisposed(); - - if (triggers == null) - { - throw new PSArgumentNullException("triggers"); - } - - // First validate all triggers. - ValidateTriggers(triggers); - - List idsNotFound = new List(); - bool triggerFound = false; - foreach (ScheduledJobTrigger updateTrigger in triggers) - { - if (_triggers.ContainsKey(updateTrigger.Id)) - { - // Disassociate old trigger from this definition. - _triggers[updateTrigger.Id].JobDefinition = null; - - // Replace older trigger object with new updated one. - ScheduledJobTrigger newTrigger = new ScheduledJobTrigger(updateTrigger); - newTrigger.Id = updateTrigger.Id; - newTrigger.JobDefinition = this; - _triggers[newTrigger.Id] = newTrigger; - triggerFound = true; - } - else - { - idsNotFound.Add(updateTrigger.Id); - } - } - - if (save && triggerFound) - { - Save(); - } - - return idsNotFound; - } - - /// - /// Creates a new set of ScheduledJobTriggers for this object. - /// - /// Array of ScheduledJobTrigger objects to set - /// Update Windows Task Scheduler and save to store - public void SetTriggers( - IEnumerable newTriggers, - bool save) - { - IsDisposed(); - - // First validate all triggers. - ValidateTriggers(newTriggers); - - // Disassociate any old trigger objects from this definition. - foreach (ScheduledJobTrigger trigger in _triggers.Values) - { - trigger.JobDefinition = null; - } - - _currentTriggerId = 0; - _triggers = new Dictionary(); - if (newTriggers != null) - { - foreach (ScheduledJobTrigger trigger in newTriggers) - { - ScheduledJobTrigger newTrigger = new ScheduledJobTrigger(trigger); - - newTrigger.Id = ++_currentTriggerId; - newTrigger.JobDefinition = this; - _triggers.Add(newTrigger.Id, newTrigger); - } - } - - if (save) - { - Save(); - } - } - - /// - /// Returns a list of new ScheduledJobTrigger objects corresponding - /// to the passed in trigger Ids. Also returns an array of trigger Ids - /// that were not found in an out parameter. - /// - /// List of trigger Ids - /// List of not found trigger Ids - /// List of ScheduledJobTrigger objects - public List GetTriggers( - IEnumerable triggerIds, - out List notFoundIds) - { - IsDisposed(); - - List newTriggers; - List notFoundList = new List(); - if (triggerIds == null) - { - // Return all triggers. - newTriggers = new List(); - foreach (ScheduledJobTrigger trigger in _triggers.Values) - { - newTriggers.Add(new ScheduledJobTrigger(trigger)); - } - } - else - { - // Filter returned triggers to match requested. - newTriggers = new List(); - foreach (Int32 triggerId in triggerIds) - { - if (_triggers.ContainsKey(triggerId)) - { - newTriggers.Add(new ScheduledJobTrigger(_triggers[triggerId])); - } - else - { - notFoundList.Add(triggerId); - } - } - } - - notFoundIds = notFoundList; - - // Return array of ScheduledJobTrigger objects sorted by Id. - newTriggers.Sort((firstTrigger, secondTrigger) => - { - return ((int)firstTrigger.Id - (int)secondTrigger.Id); - }); - - return newTriggers; - } - - /// - /// Finds and returns a copy of the ScheduledJobTrigger corresponding to - /// the passed in trigger Id. - /// - /// Trigger Id - /// ScheduledJobTrigger object - public ScheduledJobTrigger GetTrigger( - Int32 triggerId) - { - IsDisposed(); - - if (_triggers.ContainsKey(triggerId)) - { - return new ScheduledJobTrigger(_triggers[triggerId]); - } - - return null; - } - - #endregion - - #region Public Update Methods - - /// - /// Updates scheduled job options. - /// - /// ScheduledJobOptions or null for default - /// Update Windows Task Scheduler and save to store - public void UpdateOptions( - ScheduledJobOptions options, - bool save) - { - IsDisposed(); - - // Disassociate current options object from this definition. - _options.JobDefinition = null; - - // options == null is allowed and signals the use default - // Task Scheduler options. - _options = (options != null) ? new ScheduledJobOptions(options) : - new ScheduledJobOptions(); - _options.JobDefinition = this; - - if (save) - { - Save(); - } - } - - /// - /// Sets the execution history length property. - /// - /// execution history length - /// Save to store - public void SetExecutionHistoryLength( - int executionHistoryLength, - bool save) - { - IsDisposed(); - - _executionHistoryLength = executionHistoryLength; - - if (save) - { - SaveToStore(); - } - } - - /// - /// Clears all execution results in the job store. - /// - public void ClearExecutionHistory() - { - IsDisposed(); - - ScheduledJobStore.RemoveAllJobRuns(Name); - ScheduledJobSourceAdapter.ClearRepositoryForDefinition(Name); - } - - /// - /// Updates the JobInvocationInfo object. - /// - /// JobInvocationInfo - /// Save to store - public void UpdateJobInvocationInfo( - JobInvocationInfo jobInvocationInfo, - bool save) - { - IsDisposed(); - - if (jobInvocationInfo == null) - { - throw new PSArgumentNullException("jobInvocationInfo"); - } - - _invocationInfo = jobInvocationInfo; - _name = jobInvocationInfo.Name; - - if (save) - { - SaveToStore(); - } - } - - /// - /// Sets the enabled state of this object. - /// - /// True if enabled - /// Update Windows Task Scheduler and save to store - public void SetEnabled( - bool enabled, - bool save) - { - IsDisposed(); - - _enabled = enabled; - - if (save) - { - Save(); - } - } - - /// - /// Sets the name of this scheduled job definition. - /// - /// Name - /// Update Windows Task Scheduler and save to store - public void SetName( - string name, - bool save) - { - IsDisposed(); - - _name = (name != null) ? name : string.Empty; - - if (save) - { - Save(); - } - } - - #endregion - - #region IDisposable - - /// - /// Dispose. - /// - public void Dispose() - { - _isDisposed = true; - - GC.SuppressFinalize(this); - } - - #endregion - - #region Static Methods - - /// - /// Synchronizes the local ScheduledJobDefinition repository with the - /// scheduled job definitions in the job store. - /// - /// Callback delegate for each discovered item. - /// Dictionary of errors. - internal static Dictionary RefreshRepositoryFromStore( - Action itemFound = null) - { - Dictionary errors = new Dictionary(); - - // Get current list of job definition files in store, and create hash - // table for quick look up. - IEnumerable jobDefinitionPathNames = ScheduledJobStore.GetJobDefinitions(); - HashSet jobDefinitionNamesHash = new HashSet(); - foreach (string pathName in jobDefinitionPathNames) - { - // Remove path information and use job definition name only. - int indx = pathName.LastIndexOf('\\'); - string jobDefName = (indx != -1) ? pathName.Substring(indx + 1) : pathName; - jobDefinitionNamesHash.Add(jobDefName); - } - - // First remove definition objects not in store. - // Repository.Definitions returns a *copy* of current repository items. - foreach (ScheduledJobDefinition jobDef in Repository.Definitions) - { - if (jobDefinitionNamesHash.Contains(jobDef.Name) == false) - { - Repository.Remove(jobDef); - } - else - { - jobDefinitionNamesHash.Remove(jobDef.Name); - - if (itemFound != null) - { - itemFound(jobDef); - } - } - } - - // Next add definition items not in local repository. - foreach (string jobDefinitionName in jobDefinitionNamesHash) - { - try - { - // Read the job definition object from file and add to local repository. - ScheduledJobDefinition jobDefinition = ScheduledJobDefinition.LoadDefFromStore(jobDefinitionName, null); - Repository.AddOrReplace(jobDefinition); - - if (itemFound != null) - { - itemFound(jobDefinition); - } - } - catch (System.IO.IOException e) - { - errors.Add(jobDefinitionName, e); - } - catch (System.Xml.XmlException e) - { - errors.Add(jobDefinitionName, e); - } - catch (System.TypeInitializationException e) - { - errors.Add(jobDefinitionName, e); - } - catch (System.Runtime.Serialization.SerializationException e) - { - errors.Add(jobDefinitionName, e); - } - catch (System.ArgumentNullException e) - { - errors.Add(jobDefinitionName, e); - } - catch (System.UnauthorizedAccessException e) - { - errors.Add(jobDefinitionName, e); - } - } - - return errors; - } - - /// - /// Reads a ScheduledJobDefinition object from file and - /// returns object. - /// - /// Name of definition to load - /// Path to definition file - /// ScheduledJobDefinition object - internal static ScheduledJobDefinition LoadDefFromStore( - string definitionName, - string definitionPath) - { - ScheduledJobDefinition definition = null; - FileStream fs = null; - try - { - fs = GetFileStream( - definitionName, - definitionPath, - FileMode.Open, - FileAccess.Read, - FileShare.Read); - - XmlObjectSerializer serializer = new System.Runtime.Serialization.NetDataContractSerializer(); - definition = serializer.ReadObject(fs) as ScheduledJobDefinition; - } - finally - { - if (fs != null) - { - fs.Close(); - } - } - - return definition; - } - - /// - /// Creates a new ScheduledJobDefinition object from a file. - /// - /// Name of definition to load. - /// Path to definition file. - /// ScheduledJobDefinition object. - public static ScheduledJobDefinition LoadFromStore( - string definitionName, - string definitionPath) - { - if (string.IsNullOrEmpty(definitionName)) - { - throw new PSArgumentNullException("definitionName"); - } - - ScheduledJobDefinition definition = null; - bool corruptedFile = false; - Exception ex = null; - - try - { - definition = LoadDefFromStore(definitionName, definitionPath); - } - catch (DirectoryNotFoundException e) - { - ex = e; - } - catch (FileNotFoundException e) - { - ex = e; - corruptedFile = true; - } - catch (UnauthorizedAccessException e) - { - ex = e; - } - catch (IOException e) - { - ex = e; - } - catch (System.Xml.XmlException e) - { - ex = e; - corruptedFile = true; - } - catch (System.TypeInitializationException e) - { - ex = e; - corruptedFile = true; - } - catch (System.ArgumentNullException e) - { - ex = e; - corruptedFile = true; - } - catch (System.Runtime.Serialization.SerializationException e) - { - ex = e; - corruptedFile = true; - } - - if (ex != null) - { - // - // Remove definition if corrupted. - // But only if the corrupted file is in the default scheduled jobs - // path for the current user. - // - if (corruptedFile && - (definitionPath == null || - ScheduledJobStore.IsDefaultUserPath(definitionPath))) - { - // Remove corrupted scheduled job definition. - RemoveDefinition(definitionName); - - // Throw exception for corrupted/removed job definition. - throw new ScheduledJobException( - StringUtil.Format(ScheduledJobErrorStrings.CantLoadDefinitionFromStore, definitionName), - ex); - } - - // Throw exception for not found job definition. - throw new ScheduledJobException( - StringUtil.Format(ScheduledJobErrorStrings.CannotFindJobDefinition, definitionName), - ex); - } - - // Make sure the deserialized ScheduledJobDefinition object contains the same - // Task Scheduler information that is stored in Task Scheduler. - definition.SyncWithWTS(); - - return definition; - } - - /// - /// Internal helper method to remove a scheduled job definition - /// by name from job store and Task Scheduler. - /// - /// Scheduled job definition name - internal static void RemoveDefinition( - string definitionName) - { - // Remove from store. - try - { - ScheduledJobStore.RemoveJobDefinition(definitionName); - } - catch (DirectoryNotFoundException) - { } - catch (FileNotFoundException) - { } - catch (UnauthorizedAccessException) - { } - catch (IOException) - { } - - // Check and remove from Task Scheduler. - using (ScheduledJobWTS taskScheduler = new ScheduledJobWTS()) - { - try - { - taskScheduler.RemoveTaskByName(definitionName, true, true); - } - catch (UnauthorizedAccessException) - { } - catch (IOException) - { } - } - } - - private static int GetCurrentId() - { - lock (LockObject) - { - return ++CurrentId; - } - } - - /// - /// Starts a scheduled job based on definition name and returns the - /// running job object. Returned job is also added to the local - /// job repository. Job results are not written to store. - /// - /// ScheduledJobDefinition name. - public static Job2 StartJob( - string DefinitionName) - { - // Load scheduled job definition. - ScheduledJobDefinition jobDefinition = ScheduledJobDefinition.LoadFromStore(DefinitionName, null); - - // Start job. - return jobDefinition.StartJob(); - } - - private static FileStream GetFileStream( - string definitionName, - string definitionPath, - FileMode fileMode, - FileAccess fileAccess, - FileShare fileShare) - { - FileStream fs; - - if (definitionPath == null) - { - // Look for definition in default current user location. - fs = ScheduledJobStore.GetFileForJobDefinition( - definitionName, - fileMode, - fileAccess, - fileShare); - } - else - { - // Look for definition in known path. - fs = ScheduledJobStore.GetFileForJobDefinition( - definitionName, - definitionPath, - fileMode, - fileAccess, - fileShare); - } - - return fs; - } - - #endregion - - #region Running Job - - /// - /// Create a Job2 job, runs it and waits for it to complete. - /// Job status and results are written to the job store. - /// - /// Job2 job object that was run - public Job2 Run() - { - Job2 job = null; - - using (PowerShellTraceSource _tracer = PowerShellTraceSourceFactory.GetTraceSource()) - { - Exception ex = null; - try - { - JobManager jobManager = Runspace.DefaultRunspace.JobManager; - - job = jobManager.NewJob(InvocationInfo); - - // If this is a scheduled job type then include this object so - // so that ScheduledJobSourceAdapter knows where the results are - // to be stored. - ScheduledJob schedJob = job as ScheduledJob; - if (schedJob != null) - { - schedJob.Definition = this; - schedJob.AllowSetShouldExit = true; - } - - // Update job store data when job begins. - job.StateChanged += (object sender, JobStateEventArgs e) => - { - if (e.JobStateInfo.State == JobState.Running) - { - // Write job to store with this running state. - jobManager.PersistJob(job, Definition); - } - }; - - job.StartJob(); - - // Log scheduled job start. - _tracer.WriteScheduledJobStartEvent( - job.Name, - job.PSBeginTime.ToString()); - - // Wait for job to finish. - job.Finished.WaitOne(); - - // Ensure that the job run results are persisted to store. - jobManager.PersistJob(job, Definition); - - // Perform a Receive-Job on the job object. Output data will be dropped - // but we do this to execute any client method calls, in particular we - // want SetShouldExit to set the correct exit code on the process for - // use inside Task Scheduler. - using (System.Management.Automation.PowerShell ps = System.Management.Automation.PowerShell.Create()) - { - // Run on the default runspace. - ps.AddCommand("Receive-Job").AddParameter("Job", job).AddParameter("Keep", true); - ps.Invoke(); - } - - // Log scheduled job finish. - _tracer.WriteScheduledJobCompleteEvent( - job.Name, - job.PSEndTime.ToString(), - job.JobStateInfo.State.ToString()); - } - catch (RuntimeException e) - { - ex = e; - } - catch (InvalidOperationException e) - { - ex = e; - } - catch (System.Security.SecurityException e) - { - ex = e; - } - catch (System.Threading.ThreadAbortException e) - { - ex = e; - } - catch (IOException e) - { - ex = e; - } - catch (UnauthorizedAccessException e) - { - ex = e; - } - catch (ArgumentException e) - { - ex = e; - } - catch (ScriptCallDepthException e) - { - ex = e; - } - catch (System.Runtime.Serialization.SerializationException e) - { - ex = e; - } - catch (System.Runtime.Serialization.InvalidDataContractException e) - { - ex = e; - } - catch (System.Xml.XmlException e) - { - ex = e; - } - catch (Microsoft.PowerShell.ScheduledJob.ScheduledJobException e) - { - ex = e; - } - - if (ex != null) - { - // Log error. - _tracer.WriteScheduledJobErrorEvent( - this.Name, - ex.Message, - ex.StackTrace.ToString(), - (ex.InnerException != null) ? ex.InnerException.Message : string.Empty); - - throw ex; - } - } - - return job; - } - - #endregion - } - - #region ScheduledJobDefinition Repository - - /// - /// Collection of ScheduledJobDefinition objects. - /// - internal class ScheduledJobDefinitionRepository - { - #region Private Members - - private object _syncObject = new object(); - private Dictionary _definitions = new Dictionary(); - - #endregion - - #region Public Properties - - /// - /// Returns all definition objects in the repository as a List. - /// - public List Definitions - { - get - { - lock (_syncObject) - { - // Sort returned list by Ids. - List rtnList = - new List(_definitions.Values); - - rtnList.Sort((firstJob, secondJob) => - { - if (firstJob.Id > secondJob.Id) - { - return 1; - } - else if (firstJob.Id < secondJob.Id) - { - return -1; - } - else - { - return 0; - } - }); - - return rtnList; - } - } - } - - /// - /// Returns count of object in repository. - /// - public int Count - { - get - { - lock (_syncObject) - { - return _definitions.Count; - } - } - } - - #endregion - - #region Public Methods - - /// - /// Add ScheduledJobDefinition to repository. - /// - /// - public void Add(ScheduledJobDefinition jobDef) - { - if (jobDef == null) - { - throw new PSArgumentNullException("jobDef"); - } - - lock (_syncObject) - { - if (_definitions.ContainsKey(jobDef.Name)) - { - string msg = StringUtil.Format(ScheduledJobErrorStrings.DefinitionAlreadyExistsInLocal, jobDef.Name, jobDef.GlobalId); - throw new ScheduledJobException(msg); - } - _definitions.Add(jobDef.Name, jobDef); - } - } - - /// - /// Add or replace passed in ScheduledJobDefinition object to repository. - /// - /// - public void AddOrReplace(ScheduledJobDefinition jobDef) - { - if (jobDef == null) - { - throw new PSArgumentNullException("jobDef"); - } - - lock (_syncObject) - { - if (_definitions.ContainsKey(jobDef.Name)) - { - _definitions.Remove(jobDef.Name); - } - _definitions.Add(jobDef.Name, jobDef); - } - } - - /// - /// Remove ScheduledJobDefinition from repository. - /// - /// - public void Remove(ScheduledJobDefinition jobDef) - { - if (jobDef == null) - { - throw new PSArgumentNullException("jobDef"); - } - - lock (_syncObject) - { - if (_definitions.ContainsKey(jobDef.Name)) - { - _definitions.Remove(jobDef.Name); - } - } - } - - /// - /// Checks to see if a ScheduledJobDefinition object exists with - /// the provided definition name. - /// - /// Definition name - /// True if definition exists - public bool Contains(string jobDefName) - { - lock (_syncObject) - { - return _definitions.ContainsKey(jobDefName); - } - } - - /// - /// Clears all ScheduledJobDefinition items from the repository. - /// - public void Clear() - { - lock (_syncObject) - { - _definitions.Clear(); - } - } - - #endregion - } - - #endregion - - #region Exceptions - - /// - /// Exception thrown for errors in Scheduled Jobs. - /// - [Serializable] - public class ScheduledJobException : SystemException - { - /// - /// Creates a new instance of ScheduledJobException class. - /// - public ScheduledJobException() - : base - ( - StringUtil.Format(ScheduledJobErrorStrings.GeneralWTSError) - ) - { - } - - /// - /// Creates a new instance of ScheduledJobException class. - /// - /// - /// The error message that explains the reason for the exception. - /// - public ScheduledJobException(string message) - : base(message) - { - } - - /// - /// Creates a new instance of ScheduledJobException class. - /// - /// - /// The error message that explains the reason for the exception. - /// - /// - /// The exception that is the cause of the current exception. - /// - public ScheduledJobException(string message, Exception innerException) - : base(message, innerException) - { - } - - /// - /// Fully qualified error id for exception. - /// - internal string FQEID - { - get { return _fqeid; } - set { _fqeid = value ?? string.Empty; } - } - private string _fqeid = string.Empty; - } - - #endregion - - #region Utilities - - /// - /// Simple string formatting helper. - /// - internal class StringUtil - { - internal static string Format(string formatSpec, object o) - { - return String.Format(System.Threading.Thread.CurrentThread.CurrentCulture, formatSpec, o); - } - - internal static string Format(string formatSpec, params object[] o) - { - return String.Format(System.Threading.Thread.CurrentThread.CurrentCulture, formatSpec, o); - } - } - - #endregion - - #region ScheduledJobInvocationInfo Class - - /// - /// This class defines the JobInvocationInfo class for PowerShell jobs - /// for job scheduling. The following parameters are supported: - /// - /// "ScriptBlock" -> ScriptBlock - /// "FilePath" -> String - /// "InitializationScript" -> ScriptBlock - /// "ArgumentList" -> object[] - /// "RunAs32" -> Boolean - /// "Authentication" -> AuthenticationMechanism - /// - [Serializable] - public sealed class ScheduledJobInvocationInfo : JobInvocationInfo - { - #region Constructors - - /// - /// Constructor. - /// - /// JobDefinition - /// Dictionary of parameters - public ScheduledJobInvocationInfo(JobDefinition definition, Dictionary parameters) - : base(definition, parameters) - { - if (definition == null) - { - throw new PSArgumentNullException("definition"); - } - - Name = definition.Name; - } - - #endregion - - #region Public Strings - - /// - /// ScriptBlock parameter. - /// - public const string ScriptBlockParameter = "ScriptBlock"; - - /// - /// FilePath parameter. - /// - public const string FilePathParameter = "FilePath"; - - /// - /// RunAs32 parameter. - /// - public const string RunAs32Parameter = "RunAs32"; - - /// - /// Authentication parameter. - /// - public const string AuthenticationParameter = "Authentication"; - - /// - /// InitializationScript parameter. - /// - public const string InitializationScriptParameter = "InitializationScript"; - - /// - /// ArgumentList parameter. - /// - public const string ArgumentListParameter = "ArgumentList"; - - #endregion - - #region ISerializable Implementation - - /// - /// Serialization constructor. - /// - /// SerializationInfo - /// StreamingContext - internal ScheduledJobInvocationInfo( - SerializationInfo info, - StreamingContext context) - { - if (info == null) - { - throw new PSArgumentNullException("info"); - } - - DeserializeInvocationInfo(info); - } - - /// - /// Serialization implementation. - /// - /// SerializationInfo - /// StreamingContext - public override void GetObjectData(SerializationInfo info, StreamingContext context) - { - if (info == null) - { - throw new PSArgumentNullException("info"); - } - - SerializeInvocationInfo(info); - } - - #endregion - - #region Private Methods - - private void SerializeInvocationInfo(SerializationInfo info) - { - info.AddValue("InvocationInfo_Command", this.Command); - info.AddValue("InvocationInfo_Name", this.Name); - info.AddValue("InvocationInfo_AdapterType", this.Definition.JobSourceAdapterType); - info.AddValue("InvocationInfo_ModuleName", this.Definition.ModuleName); - info.AddValue("InvocationInfo_AdapterTypeName", this.Definition.JobSourceAdapterTypeName); - - // Get the job parameters. - Dictionary parameters = new Dictionary(); - foreach (var commandParam in this.Parameters[0]) - { - if (!parameters.ContainsKey(commandParam.Name)) - { - parameters.Add(commandParam.Name, commandParam.Value); - } - } - - // - // Serialize only parameters that scheduled job knows about. - // - - // ScriptBlock - if (parameters.ContainsKey(ScriptBlockParameter)) - { - ScriptBlock scriptBlock = (ScriptBlock)parameters[ScriptBlockParameter]; - info.AddValue("InvocationParam_ScriptBlock", scriptBlock.ToString()); - } - else - { - info.AddValue("InvocationParam_ScriptBlock", null); - } - - // FilePath - if (parameters.ContainsKey(FilePathParameter)) - { - string filePath = (string)parameters[FilePathParameter]; - info.AddValue("InvocationParam_FilePath", filePath); - } - else - { - info.AddValue("InvocationParam_FilePath", string.Empty); - } - - // InitializationScript - if (parameters.ContainsKey(InitializationScriptParameter)) - { - ScriptBlock scriptBlock = (ScriptBlock)parameters[InitializationScriptParameter]; - info.AddValue("InvocationParam_InitScript", scriptBlock.ToString()); - } - else - { - info.AddValue("InvocationParam_InitScript", string.Empty); - } - - // RunAs32 - if (parameters.ContainsKey(RunAs32Parameter)) - { - bool runAs32 = (bool)parameters[RunAs32Parameter]; - info.AddValue("InvocationParam_RunAs32", runAs32); - } - else - { - info.AddValue("InvocationParam_RunAs32", false); - } - - // Authentication - if (parameters.ContainsKey(AuthenticationParameter)) - { - AuthenticationMechanism authentication = (AuthenticationMechanism)parameters[AuthenticationParameter]; - info.AddValue("InvocationParam_Authentication", authentication); - } - else - { - info.AddValue("InvocationParam_Authentication", AuthenticationMechanism.Default); - } - - // ArgumentList - if (parameters.ContainsKey(ArgumentListParameter)) - { - object[] argList = (object[])parameters[ArgumentListParameter]; - info.AddValue("InvocationParam_ArgList", argList); - } - else - { - info.AddValue("InvocationParam_ArgList", null); - } - } - - private void DeserializeInvocationInfo(SerializationInfo info) - { - string command = info.GetString("InvocationInfo_Command"); - string name = info.GetString("InvocationInfo_Name"); - string moduleName = info.GetString("InvocationInfo_ModuleName"); - string adapterTypeName = info.GetString("InvocationInfo_AdapterTypeName"); - - // - // Parameters - Dictionary parameters = new Dictionary(); - - // ScriptBlock - string script = info.GetString("InvocationParam_ScriptBlock"); - if (script != null) - { - parameters.Add(ScriptBlockParameter, ScriptBlock.Create(script)); - } - - // FilePath - string filePath = info.GetString("InvocationParam_FilePath"); - if (!string.IsNullOrEmpty(filePath)) - { - parameters.Add(FilePathParameter, filePath); - } - - // InitializationScript - script = info.GetString("InvocationParam_InitScript"); - if (!string.IsNullOrEmpty(script)) - { - parameters.Add(InitializationScriptParameter, ScriptBlock.Create(script)); - } - - // RunAs32 - bool runAs32 = info.GetBoolean("InvocationParam_RunAs32"); - parameters.Add(RunAs32Parameter, runAs32); - - // Authentication - AuthenticationMechanism authentication = (AuthenticationMechanism)info.GetValue("InvocationParam_Authentication", - typeof(AuthenticationMechanism)); - parameters.Add(AuthenticationParameter, authentication); - - // ArgumentList - object[] argList = (object[])info.GetValue("InvocationParam_ArgList", typeof(object[])); - if (argList != null) - { - parameters.Add(ArgumentListParameter, argList); - } - - JobDefinition jobDefinition = new JobDefinition(null, command, name); - jobDefinition.ModuleName = moduleName; - jobDefinition.JobSourceAdapterTypeName = adapterTypeName; - - // Convert to JobInvocationParameter collection - CommandParameterCollection paramCollection = new CommandParameterCollection(); - foreach (KeyValuePair param in parameters) - { - CommandParameter paramItem = new CommandParameter(param.Key, param.Value); - paramCollection.Add(paramItem); - } - - this.Definition = jobDefinition; - this.Name = name; - this.Command = command; - this.Parameters.Add(paramCollection); - } - - #endregion - } - - #endregion -} diff --git a/src/Microsoft.PowerShell.ScheduledJob/ScheduledJobOptions.cs b/src/Microsoft.PowerShell.ScheduledJob/ScheduledJobOptions.cs deleted file mode 100644 index 2fa6bd0505c..00000000000 --- a/src/Microsoft.PowerShell.ScheduledJob/ScheduledJobOptions.cs +++ /dev/null @@ -1,393 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Runtime.Serialization; -using System.Management.Automation; -using System.Security.Permissions; - -namespace Microsoft.PowerShell.ScheduledJob -{ - /// - /// This class contains Windows Task Scheduler options. - /// - [Serializable] - public sealed class ScheduledJobOptions : ISerializable - { - #region Private Members - - // Power settings - private bool _startIfOnBatteries; - private bool _stopIfGoingOnBatteries; - private bool _wakeToRun; - - // Idle settings - private bool _startIfNotIdle; - private bool _stopIfGoingOffIdle; - private bool _restartOnIdleResume; - private TimeSpan _idleDuration; - private TimeSpan _idleTimeout; - - // Security settings - private bool _showInTaskScheduler; - private bool _runElevated; - - // Misc - private bool _runWithoutNetwork; - private bool _donotAllowDemandStart; - private TaskMultipleInstancePolicy _multipleInstancePolicy; - - // ScheduledJobDefinition object associated with this options object. - private ScheduledJobDefinition _jobDefAssociation; - - #endregion - - #region Public Properties - - /// - /// Start task if on batteries. - /// - public bool StartIfOnBatteries - { - get { return _startIfOnBatteries; } - set { _startIfOnBatteries = value; } - } - - /// - /// Stop task if computer is going on batteries. - /// - public bool StopIfGoingOnBatteries - { - get { return _stopIfGoingOnBatteries; } - set { _stopIfGoingOnBatteries = value; } - } - - /// - /// Wake computer to run task. - /// - public bool WakeToRun - { - get { return _wakeToRun; } - set { _wakeToRun = value; } - } - - /// - /// Start task only if computer is not idle. - /// - public bool StartIfNotIdle - { - get { return _startIfNotIdle; } - set { _startIfNotIdle = value; } - } - - /// - /// Stop task if computer is no longer idle. - /// - public bool StopIfGoingOffIdle - { - get { return _stopIfGoingOffIdle; } - set { _stopIfGoingOffIdle = value; } - } - /// - /// Restart task on idle resuming. - /// - public bool RestartOnIdleResume - { - get { return _restartOnIdleResume; } - set { _restartOnIdleResume = value; } - } - - /// - /// How long computer must be idle before task starts. - /// - public TimeSpan IdleDuration - { - get { return _idleDuration; } - set { _idleDuration = value; } - } - - /// - /// How long task manager will wait for required idle duration. - /// - public TimeSpan IdleTimeout - { - get { return _idleTimeout; } - set { _idleTimeout = value; } - } - - /// - /// When true task is not shown in Task Scheduler UI. - /// - public bool ShowInTaskScheduler - { - get { return _showInTaskScheduler; } - set { _showInTaskScheduler = value; } - } - - /// - /// Run task with elevated privileges. - /// - public bool RunElevated - { - get { return _runElevated; } - set { _runElevated = value; } - } - - /// - /// Run task even if network is not available. - /// - public bool RunWithoutNetwork - { - get { return _runWithoutNetwork; } - set { _runWithoutNetwork = value; } - } - - /// - /// Do not allow a task to be started on demand. - /// - public bool DoNotAllowDemandStart - { - get { return _donotAllowDemandStart; } - set { _donotAllowDemandStart = value; } - } - - /// - /// Multiple task instance policy. - /// - public TaskMultipleInstancePolicy MultipleInstancePolicy - { - get { return _multipleInstancePolicy; } - set { _multipleInstancePolicy = value; } - } - - /// - /// ScheduledJobDefinition object associated with this options object. - /// - public ScheduledJobDefinition JobDefinition - { - get { return _jobDefAssociation; } - internal set { _jobDefAssociation = value; } - } - - #endregion - - #region Constructors - - /// - /// Default constructor. - /// - public ScheduledJobOptions() - { - _startIfOnBatteries = false; - _stopIfGoingOnBatteries = true; - _wakeToRun = false; - _startIfNotIdle = true; - _stopIfGoingOffIdle = false; - _restartOnIdleResume = false; - _idleDuration = new TimeSpan(0, 10, 0); - _idleTimeout = new TimeSpan(1, 0, 0); - _showInTaskScheduler = true; - _runElevated = false; - _runWithoutNetwork = true; - _donotAllowDemandStart = false; - _multipleInstancePolicy = TaskMultipleInstancePolicy.IgnoreNew; - } - - /// - /// Constructor. - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - /// - internal ScheduledJobOptions( - bool startIfOnBatteries, - bool stopIfGoingOnBatters, - bool wakeToRun, - bool startIfNotIdle, - bool stopIfGoingOffIdle, - bool restartOnIdleResume, - TimeSpan idleDuration, - TimeSpan idleTimeout, - bool showInTaskScheduler, - bool runElevated, - bool runWithoutNetwork, - bool donotAllowDemandStart, - TaskMultipleInstancePolicy multipleInstancePolicy) - { - _startIfOnBatteries = startIfOnBatteries; - _stopIfGoingOnBatteries = stopIfGoingOnBatters; - _wakeToRun = wakeToRun; - _startIfNotIdle = startIfNotIdle; - _stopIfGoingOffIdle = stopIfGoingOffIdle; - _restartOnIdleResume = restartOnIdleResume; - _idleDuration = idleDuration; - _idleTimeout = idleTimeout; - _showInTaskScheduler = showInTaskScheduler; - _runElevated = runElevated; - _runWithoutNetwork = runWithoutNetwork; - _donotAllowDemandStart = donotAllowDemandStart; - _multipleInstancePolicy = multipleInstancePolicy; - } - - /// - /// Copy Constructor. - /// - /// Copy from - internal ScheduledJobOptions( - ScheduledJobOptions copyOptions) - { - if (copyOptions == null) - { - throw new PSArgumentNullException("copyOptions"); - } - - _startIfOnBatteries = copyOptions.StartIfOnBatteries; - _stopIfGoingOnBatteries = copyOptions.StopIfGoingOnBatteries; - _wakeToRun = copyOptions.WakeToRun; - _startIfNotIdle = copyOptions.StartIfNotIdle; - _stopIfGoingOffIdle = copyOptions.StopIfGoingOffIdle; - _restartOnIdleResume = copyOptions.RestartOnIdleResume; - _idleDuration = copyOptions.IdleDuration; - _idleTimeout = copyOptions.IdleTimeout; - _showInTaskScheduler = copyOptions.ShowInTaskScheduler; - _runElevated = copyOptions.RunElevated; - _runWithoutNetwork = copyOptions.RunWithoutNetwork; - _donotAllowDemandStart = copyOptions.DoNotAllowDemandStart; - _multipleInstancePolicy = copyOptions.MultipleInstancePolicy; - - _jobDefAssociation = copyOptions.JobDefinition; - } - - #endregion - - #region ISerializable Implementation - - /// - /// Serialization constructor. - /// - /// SerializationInfo - /// StreamingContext - [SecurityPermissionAttribute(SecurityAction.Demand, SerializationFormatter = true)] - private ScheduledJobOptions( - SerializationInfo info, - StreamingContext context) - { - if (info == null) - { - throw new PSArgumentNullException("info"); - } - - _startIfOnBatteries = info.GetBoolean("StartIfOnBatteries_Value"); - _stopIfGoingOnBatteries = info.GetBoolean("StopIfGoingOnBatteries_Value"); - _wakeToRun = info.GetBoolean("WakeToRun_Value"); - _startIfNotIdle = info.GetBoolean("StartIfNotIdle_Value"); - _stopIfGoingOffIdle = info.GetBoolean("StopIfGoingOffIdle_Value"); - _restartOnIdleResume = info.GetBoolean("RestartOnIdleResume_Value"); - _idleDuration = (TimeSpan)info.GetValue("IdleDuration_Value", typeof(TimeSpan)); - _idleTimeout = (TimeSpan)info.GetValue("IdleTimeout_Value", typeof(TimeSpan)); - _showInTaskScheduler = info.GetBoolean("ShowInTaskScheduler_Value"); - _runElevated = info.GetBoolean("RunElevated_Value"); - _runWithoutNetwork = info.GetBoolean("RunWithoutNetwork_Value"); - _donotAllowDemandStart = info.GetBoolean("DoNotAllowDemandStart_Value"); - _multipleInstancePolicy = (TaskMultipleInstancePolicy)info.GetValue("TaskMultipleInstancePolicy_Value", typeof(TaskMultipleInstancePolicy)); - - // Runtime reference and not saved to store. - _jobDefAssociation = null; - } - - /// - /// GetObjectData for ISerializable implementation. - /// - /// SerializationInfo - /// StreamingContext - [SecurityPermissionAttribute(SecurityAction.Demand, SerializationFormatter = true)] - public void GetObjectData(SerializationInfo info, StreamingContext context) - { - if (info == null) - { - throw new PSArgumentNullException("info"); - } - - info.AddValue("StartIfOnBatteries_Value", _startIfOnBatteries); - info.AddValue("StopIfGoingOnBatteries_Value", _stopIfGoingOnBatteries); - info.AddValue("WakeToRun_Value", _wakeToRun); - info.AddValue("StartIfNotIdle_Value", _startIfNotIdle); - info.AddValue("StopIfGoingOffIdle_Value", _stopIfGoingOffIdle); - info.AddValue("RestartOnIdleResume_Value", _restartOnIdleResume); - info.AddValue("IdleDuration_Value", _idleDuration); - info.AddValue("IdleTimeout_Value", _idleTimeout); - info.AddValue("ShowInTaskScheduler_Value", _showInTaskScheduler); - info.AddValue("RunElevated_Value", _runElevated); - info.AddValue("RunWithoutNetwork_Value", _runWithoutNetwork); - info.AddValue("DoNotAllowDemandStart_Value", _donotAllowDemandStart); - info.AddValue("TaskMultipleInstancePolicy_Value", _multipleInstancePolicy); - } - - #endregion - - #region Public Methods - - /// - /// Update the associated ScheduledJobDefinition object with the - /// current properties of this object. - /// - public void UpdateJobDefinition() - { - if (_jobDefAssociation == null) - { - string msg = StringUtil.Format(ScheduledJobErrorStrings.NoAssociatedJobDefinitionForOption); - - throw new RuntimeException(msg); - } - - _jobDefAssociation.UpdateOptions(this, true); - } - - #endregion - } - - #region Public Enums - - /// - /// Enumerates Task Scheduler options for multiple instance polices of - /// scheduled tasks (jobs). - /// - public enum TaskMultipleInstancePolicy - { - /// - /// None - /// - None = 0, - /// - /// Ignore a new instance of the task (job) - /// - IgnoreNew = 1, - /// - /// Allow parallel running of a task (job) - /// - Parallel = 2, - /// - /// Queue up multiple instances of a task (job) - /// - Queue = 3, - /// - /// Stop currently running task (job) and start a new one - /// - StopExisting = 4 - } - - #endregion -} diff --git a/src/Microsoft.PowerShell.ScheduledJob/ScheduledJobSourceAdapter.cs b/src/Microsoft.PowerShell.ScheduledJob/ScheduledJobSourceAdapter.cs deleted file mode 100644 index 328625d49fc..00000000000 --- a/src/Microsoft.PowerShell.ScheduledJob/ScheduledJobSourceAdapter.cs +++ /dev/null @@ -1,1084 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Linq; -using System.Text; -using System.Management.Automation; -using System.Management.Automation.Runspaces; -using System.IO; -using System.Globalization; -using System.Runtime.Serialization; - -namespace Microsoft.PowerShell.ScheduledJob -{ - /// - /// This class provides functionality for retrieving scheduled job run results - /// from the scheduled job store. An instance of this object will be registered - /// with the PowerShell JobManager so that GetJobs commands will retrieve schedule - /// job runs from the file based scheduled job store. This allows scheduled job - /// runs to be managed from PowerShell in the same way workflow jobs are managed. - /// - public sealed class ScheduledJobSourceAdapter : JobSourceAdapter - { - #region Private Members - - private static FileSystemWatcher StoreWatcher; - private static object SyncObject = new object(); - private static ScheduledJobRepository JobRepository = new ScheduledJobRepository(); - internal const string AdapterTypeName = "PSScheduledJob"; - - #endregion - - #region Public Strings - - /// - /// BeforeFilter - /// - public const string BeforeFilter = "Before"; - - /// - /// AfterFilter - /// - public const string AfterFilter = "After"; - - /// - /// NewestFilter - /// - public const string NewestFilter = "Newest"; - - #endregion - - #region Constructor - - /// - /// Constructor. - /// - public ScheduledJobSourceAdapter() - { - Name = AdapterTypeName; - } - - #endregion - - #region JobSourceAdapter Implementation - - /// - /// Create a new Job2 results instance. - /// - /// Job specification - /// Job2 - public override Job2 NewJob(JobInvocationInfo specification) - { - if (specification == null) - { - throw new PSArgumentNullException("specification"); - } - - ScheduledJobDefinition scheduledJobDef = new ScheduledJobDefinition( - specification, null, null, null); - - return new ScheduledJob( - specification.Command, - specification.Name, - scheduledJobDef); - } - - /// - /// Creates a new Job2 object based on a definition name - /// that can be run manually. If the path parameter is - /// null then a default location will be used to find the - /// job definition by name. - /// - /// ScheduledJob definition name - /// ScheduledJob definition file path - /// Job2 object - public override Job2 NewJob(string definitionName, string definitionPath) - { - if (string.IsNullOrEmpty(definitionName)) - { - throw new PSArgumentException("definitionName"); - } - - Job2 rtnJob = null; - try - { - ScheduledJobDefinition scheduledJobDef = - ScheduledJobDefinition.LoadFromStore(definitionName, definitionPath); - - rtnJob = new ScheduledJob( - scheduledJobDef.Command, - scheduledJobDef.Name, - scheduledJobDef); - } - catch (FileNotFoundException) - { - // Return null if no job definition exists. - } - - return rtnJob; - } - - /// - /// Get the list of jobs that are currently available in this - /// store - /// - /// Collection of job objects - public override IList GetJobs() - { - RefreshRepository(); - - List rtnJobs = new List(); - foreach (var job in JobRepository.Jobs) - { - rtnJobs.Add(job); - } - - return rtnJobs; - } - - /// - /// Get list of jobs that matches the specified names - /// - /// names to match, can support - /// wildcard if the store supports - /// - /// collection of jobs that match the specified - /// criteria - public override IList GetJobsByName(string name, bool recurse) - { - if (string.IsNullOrEmpty(name)) - { - throw new PSArgumentException("name"); - } - - RefreshRepository(); - - WildcardPattern namePattern = new WildcardPattern(name, WildcardOptions.IgnoreCase); - List rtnJobs = new List(); - foreach (var job in JobRepository.Jobs) - { - if (namePattern.IsMatch(job.Name)) - { - rtnJobs.Add(job); - } - } - - return rtnJobs; - } - - /// - /// Get list of jobs that run the specified command - /// - /// command to match - /// - /// collection of jobs that match the specified - /// criteria - public override IList GetJobsByCommand(string command, bool recurse) - { - if (string.IsNullOrEmpty(command)) - { - throw new PSArgumentException("command"); - } - - RefreshRepository(); - - WildcardPattern commandPattern = new WildcardPattern(command, WildcardOptions.IgnoreCase); - List rtnJobs = new List(); - foreach (var job in JobRepository.Jobs) - { - if (commandPattern.IsMatch(job.Command)) - { - rtnJobs.Add(job); - } - } - - return rtnJobs; - } - - /// - /// Get job that has the specified id - /// - /// Guid to match - /// - /// job with the specified guid - public override Job2 GetJobByInstanceId(Guid instanceId, bool recurse) - { - RefreshRepository(); - - foreach (var job in JobRepository.Jobs) - { - if (Guid.Equals(job.InstanceId, instanceId)) - { - return job; - } - } - - return null; - } - - /// - /// Get job that has specific session id - /// - /// Id to match - /// - /// Job with the specified id - public override Job2 GetJobBySessionId(int id, bool recurse) - { - RefreshRepository(); - - foreach (var job in JobRepository.Jobs) - { - if (id == job.Id) - { - return job; - } - } - - return null; - } - - /// - /// Get list of jobs that are in the specified state - /// - /// state to match - /// - /// collection of jobs with the specified - /// state - public override IList GetJobsByState(JobState state, bool recurse) - { - RefreshRepository(); - - List rtnJobs = new List(); - foreach (var job in JobRepository.Jobs) - { - if (state == job.JobStateInfo.State) - { - rtnJobs.Add(job); - } - } - - return rtnJobs; - } - - /// - /// Get list of jobs based on the adapter specific - /// filter parameters - /// - /// dictionary containing name value - /// pairs for adapter specific filters - /// - /// collection of jobs that match the - /// specified criteria - public override IList GetJobsByFilter(Dictionary filter, bool recurse) - { - if (filter == null) - { - throw new PSArgumentNullException("filter"); - } - - List rtnJobs = new List(); - foreach (var filterItem in filter) - { - switch (filterItem.Key) - { - case BeforeFilter: - GetJobsBefore((DateTime)filterItem.Value, ref rtnJobs); - break; - - case AfterFilter: - GetJobsAfter((DateTime)filterItem.Value, ref rtnJobs); - break; - - case NewestFilter: - GetNewestJobs((int)filterItem.Value, ref rtnJobs); - break; - } - } - - return rtnJobs; - } - - /// - /// Remove a job from the store - /// - /// job object to remove - public override void RemoveJob(Job2 job) - { - if (job == null) - { - throw new PSArgumentNullException("job"); - } - - RefreshRepository(); - - try - { - JobRepository.Remove(job); - ScheduledJobStore.RemoveJobRun( - job.Name, - job.PSBeginTime ?? DateTime.MinValue); - } - catch (DirectoryNotFoundException) - { - } - catch (FileNotFoundException) - { - } - } - - /// - /// Saves job to scheduled job run store. - /// - /// ScheduledJob - public override void PersistJob(Job2 job) - { - if (job == null) - { - throw new PSArgumentNullException("job"); - } - - SaveJobToStore(job as ScheduledJob); - } - - #endregion - - #region Save Job - - /// - /// Serializes a ScheduledJob and saves it to store. - /// - /// ScheduledJob - internal static void SaveJobToStore(ScheduledJob job) - { - string outputPath = job.Definition.OutputPath; - if (string.IsNullOrEmpty(outputPath)) - { - string msg = StringUtil.Format(ScheduledJobErrorStrings.CantSaveJobNoFilePathSpecified, - job.Name); - throw new ScheduledJobException(msg); - } - - FileStream fsStatus = null; - FileStream fsResults = null; - try - { - // Check the job store results and if maximum number of results exist - // remove the oldest results folder to make room for these new results. - CheckJobStoreResults(outputPath, job.Definition.ExecutionHistoryLength); - - fsStatus = ScheduledJobStore.CreateFileForJobRunItem( - outputPath, - job.PSBeginTime ?? DateTime.MinValue, - ScheduledJobStore.JobRunItem.Status); - - // Save status only in status file stream. - SaveStatusToFile(job, fsStatus); - - fsResults = ScheduledJobStore.CreateFileForJobRunItem( - outputPath, - job.PSBeginTime ?? DateTime.MinValue, - ScheduledJobStore.JobRunItem.Results); - - // Save entire job in results file stream. - SaveResultsToFile(job, fsResults); - } - finally - { - if (fsStatus != null) - { - fsStatus.Close(); - } - - if (fsResults != null) - { - fsResults.Close(); - } - } - } - - /// - /// Writes the job status information to the provided - /// file stream. - /// - /// ScheduledJob job to save - /// FileStream - private static void SaveStatusToFile(ScheduledJob job, FileStream fs) - { - StatusInfo statusInfo = new StatusInfo( - job.InstanceId, - job.Name, - job.Location, - job.Command, - job.StatusMessage, - job.JobStateInfo.State, - job.HasMoreData, - job.PSBeginTime, - job.PSEndTime, - job.Definition); - - XmlObjectSerializer serializer = new System.Runtime.Serialization.NetDataContractSerializer(); - serializer.WriteObject(fs, statusInfo); - fs.Flush(); - } - - /// - /// Writes the job (which implements ISerializable) to the provided - /// file stream. - /// - /// ScheduledJob job to save - /// FileStream - private static void SaveResultsToFile(ScheduledJob job, FileStream fs) - { - XmlObjectSerializer serializer = new System.Runtime.Serialization.NetDataContractSerializer(); - serializer.WriteObject(fs, job); - fs.Flush(); - } - - /// - /// Check the job store results and if maximum number of results exist - /// remove the oldest results folder to make room for these new results. - /// - /// Output path - /// Maximum size of stored job results - private static void CheckJobStoreResults(string outputPath, int executionHistoryLength) - { - // Get current results for this job definition. - Collection jobRuns = ScheduledJobStore.GetJobRunsForDefinitionPath(outputPath); - if (jobRuns.Count <= executionHistoryLength) - { - // There is room for another job run in the store. - return; - } - - // Remove the oldest job run from the store. - DateTime jobRunToRemove = DateTime.MaxValue; - foreach (DateTime jobRun in jobRuns) - { - jobRunToRemove = (jobRun < jobRunToRemove) ? jobRun : jobRunToRemove; - } - - try - { - ScheduledJobStore.RemoveJobRunFromOutputPath(outputPath, jobRunToRemove); - } - catch (UnauthorizedAccessException) - { } - } - - #endregion - - #region Retrieve Job - - /// - /// Finds and load the Job associated with this ScheduledJobDefinition object - /// having the job run date time provided. - /// - /// DateTime of job run to load - /// ScheduledJobDefinition name - /// Job2 job loaded from store - internal static Job2 LoadJobFromStore(string definitionName, DateTime jobRun) - { - FileStream fsResults = null; - Exception ex = null; - bool corruptedFile = false; - Job2 job = null; - - try - { - // Results - fsResults = ScheduledJobStore.GetFileForJobRunItem( - definitionName, - jobRun, - ScheduledJobStore.JobRunItem.Results, - FileMode.Open, - FileAccess.Read, - FileShare.Read); - - job = LoadResultsFromFile(fsResults); - } - catch (ArgumentException e) - { - ex = e; - } - catch (DirectoryNotFoundException e) - { - ex = e; - } - catch (FileNotFoundException e) - { - ex = e; - corruptedFile = true; - } - catch (UnauthorizedAccessException e) - { - ex = e; - } - catch (IOException e) - { - ex = e; - } - catch (System.Runtime.Serialization.SerializationException) - { - corruptedFile = true; - } - catch (System.Runtime.Serialization.InvalidDataContractException) - { - corruptedFile = true; - } - catch (System.Xml.XmlException) - { - corruptedFile = true; - } - catch (System.TypeInitializationException) - { - corruptedFile = true; - } - finally - { - if (fsResults != null) - { - fsResults.Close(); - } - } - - if (corruptedFile) - { - // Remove the corrupted job results file. - ScheduledJobStore.RemoveJobRun(definitionName, jobRun); - } - - if (ex != null) - { - string msg = StringUtil.Format(ScheduledJobErrorStrings.CantLoadJobRunFromStore, definitionName, jobRun); - throw new ScheduledJobException(msg, ex); - } - - return job; - } - - /// - /// Loads the Job2 object from provided files stream. - /// - /// FileStream from which to read job object - /// Created Job2 from file stream - private static Job2 LoadResultsFromFile(FileStream fs) - { - XmlObjectSerializer serializer = new System.Runtime.Serialization.NetDataContractSerializer(); - return (Job2)serializer.ReadObject(fs); - } - - #endregion - - #region Static Methods - - /// - /// Adds a Job2 object to the repository. - /// - /// Job2 - internal static void AddToRepository(Job2 job) - { - if (job == null) - { - throw new PSArgumentNullException("job"); - } - - JobRepository.AddOrReplace(job); - } - - /// - /// Clears all items in the repository. - /// - internal static void ClearRepository() - { - JobRepository.Clear(); - } - - /// - /// Clears all items for given job definition name in the - /// repository. - /// - /// Scheduled job definition name. - internal static void ClearRepositoryForDefinition(string definitionName) - { - if (string.IsNullOrEmpty(definitionName)) - { - throw new PSArgumentException("definitionName"); - } - - // This returns a new list object of repository jobs. - List jobList = JobRepository.Jobs; - foreach (var job in jobList) - { - if (string.Compare(definitionName, job.Name, - StringComparison.OrdinalIgnoreCase) == 0) - { - JobRepository.Remove(job); - } - } - } - - #endregion - - #region Private Methods - - private void RefreshRepository() - { - ScheduledJobStore.CreateDirectoryIfNotExists(); - CreateFileSystemWatcher(); - - IEnumerable jobDefinitions = ScheduledJobStore.GetJobDefinitions(); - foreach (string definitionName in jobDefinitions) - { - // Create Job2 objects for each job run in store. - Collection jobRuns = GetJobRuns(definitionName); - if (jobRuns == null) - { - continue; - } - - ScheduledJobDefinition definition = null; - foreach (DateTime jobRun in jobRuns) - { - if (jobRun > JobRepository.GetLatestJobRun(definitionName)) - { - Job2 job; - try - { - if (definition == null) - { - definition = ScheduledJobDefinition.LoadFromStore(definitionName, null); - } - - job = LoadJobFromStore(definition.Name, jobRun); - } - catch (ScheduledJobException) - { - continue; - } - catch (DirectoryNotFoundException) - { - continue; - } - catch (FileNotFoundException) - { - continue; - } - catch (UnauthorizedAccessException) - { - continue; - } - catch (IOException) - { - continue; - } - - JobRepository.AddOrReplace(job); - JobRepository.SetLatestJobRun(definitionName, jobRun); - } - } - } - } - - private void CreateFileSystemWatcher() - { - // Lazily create the static file system watcher - // on first use. - if (StoreWatcher == null) - { - lock (SyncObject) - { - if (StoreWatcher == null) - { - StoreWatcher = new FileSystemWatcher(ScheduledJobStore.GetJobDefinitionLocation()); - StoreWatcher.IncludeSubdirectories = true; - StoreWatcher.NotifyFilter = NotifyFilters.LastWrite; - StoreWatcher.Filter = "Results.xml"; - StoreWatcher.EnableRaisingEvents = true; - StoreWatcher.Changed += (object sender, FileSystemEventArgs e) => - { - UpdateRepositoryObjects(e); - }; - } - } - } - } - - private static void UpdateRepositoryObjects(FileSystemEventArgs e) - { - // Extract job run information from change file path. - string updateDefinitionName; - DateTime updateJobRun; - if (!GetJobRunInfo(e.Name, out updateDefinitionName, out updateJobRun)) - { - System.Diagnostics.Debug.Assert(false, "All job run updates should have valid directory names."); - return; - } - - // Find corresponding job in repository. - ScheduledJob updateJob = JobRepository.GetJob(updateDefinitionName, updateJobRun); - if (updateJob == null) - { - return; - } - - // Load updated job information from store. - Job2 job = null; - try - { - job = LoadJobFromStore(updateDefinitionName, updateJobRun); - } - catch (ScheduledJobException) - { } - catch (DirectoryNotFoundException) - { } - catch (FileNotFoundException) - { } - catch (UnauthorizedAccessException) - { } - catch (IOException) - { } - - // Update job in repository based on new job store data. - if (job != null) - { - updateJob.Update(job as ScheduledJob); - } - } - - /// - /// Parses job definition name and job run DateTime from provided path string. - /// Example: - /// path = "ScheduledJob1\\Output\\20111219-200921-369\\Results.xml" - /// 'ScheduledJob1' is the definition name. - /// '20111219-200921-369' is the jobRun DateTime. - /// - /// - /// - /// - /// - private static bool GetJobRunInfo( - string path, - out string definitionName, - out DateTime jobRunReturn) - { - // Parse definition name from path. - string[] pathItems = path.Split(System.IO.Path.DirectorySeparatorChar); - if (pathItems.Length == 4) - { - definitionName = pathItems[0]; - return ScheduledJobStore.ConvertJobRunNameToDateTime(pathItems[2], out jobRunReturn); - } - - definitionName = null; - jobRunReturn = DateTime.MinValue; - return false; - } - - internal static Collection GetJobRuns(string definitionName) - { - Collection jobRuns = null; - try - { - jobRuns = ScheduledJobStore.GetJobRunsForDefinition(definitionName); - } - catch (DirectoryNotFoundException) - { } - catch (FileNotFoundException) - { } - catch (UnauthorizedAccessException) - { } - catch (IOException) - { } - - return jobRuns; - } - - private void GetJobsBefore( - DateTime dateTime, - ref List jobList) - { - foreach (var job in JobRepository.Jobs) - { - if (job.PSEndTime < dateTime && - !jobList.Contains(job)) - { - jobList.Add(job); - } - } - } - - private void GetJobsAfter( - DateTime dateTime, - ref List jobList) - { - foreach (var job in JobRepository.Jobs) - { - if (job.PSEndTime > dateTime && - !jobList.Contains(job)) - { - jobList.Add(job); - } - } - } - - private void GetNewestJobs( - int maxNumber, - ref List jobList) - { - List allJobs = JobRepository.Jobs; - - // Sort descending. - allJobs.Sort((firstJob, secondJob) => - { - if (firstJob.PSEndTime > secondJob.PSEndTime) - { - return -1; - } - else if (firstJob.PSEndTime < secondJob.PSEndTime) - { - return 1; - } - else - { - return 0; - } - }); - - int count = 0; - foreach (var job in allJobs) - { - if (++count > maxNumber) - { - break; - } - - if (!jobList.Contains(job)) - { - jobList.Add(job); - } - } - } - - #endregion - - #region Private Repository Class - - /// - /// Collection of Job2 objects. - /// - internal class ScheduledJobRepository - { - #region Private Members - - private object _syncObject = new object(); - private Dictionary _jobs = new Dictionary(); - private Dictionary _latestJobRuns = new Dictionary(); - - #endregion - - #region Public Properties - - /// - /// Returns all job objects in the repository as a List. - /// - public List Jobs - { - get - { - lock (_syncObject) - { - return new List(_jobs.Values); - } - } - } - - /// - /// Returns count of jobs in repository. - /// - public int Count - { - get - { - lock (_syncObject) - { - return _jobs.Count; - } - } - } - - #endregion - - #region Public Methods - - /// - /// Add Job2 to repository. - /// - /// Job2 to add - public void Add(Job2 job) - { - if (job == null) - { - throw new PSArgumentNullException("job"); - } - - lock (_syncObject) - { - if (_jobs.ContainsKey(job.InstanceId)) - { - string msg = StringUtil.Format(ScheduledJobErrorStrings.ScheduledJobAlreadyExistsInLocal, job.Name, job.InstanceId); - throw new ScheduledJobException(msg); - } - _jobs.Add(job.InstanceId, job); - } - } - - /// - /// Add or replace passed in Job2 object to repository. - /// - /// Job2 to add - public void AddOrReplace(Job2 job) - { - if (job == null) - { - throw new PSArgumentNullException("job"); - } - - lock (_syncObject) - { - if (_jobs.ContainsKey(job.InstanceId)) - { - _jobs.Remove(job.InstanceId); - } - _jobs.Add(job.InstanceId, job); - } - } - - /// - /// Remove Job2 from repository. - /// - /// - public void Remove(Job2 job) - { - if (job == null) - { - throw new PSArgumentNullException("job"); - } - - lock (_syncObject) - { - if (_jobs.ContainsKey(job.InstanceId) == false) - { - string msg = StringUtil.Format(ScheduledJobErrorStrings.ScheduledJobNotInRepository, job.Name); - throw new ScheduledJobException(msg); - } - _jobs.Remove(job.InstanceId); - } - } - - /// - /// Clears all Job2 items from the repository. - /// - public void Clear() - { - lock (_syncObject) - { - _jobs.Clear(); - } - } - - /// - /// Gets the latest job run Date/Time for the given definition name. - /// - /// ScheduledJobDefinition name - /// Job Run DateTime - public DateTime GetLatestJobRun(string definitionName) - { - if (string.IsNullOrEmpty(definitionName)) - { - throw new PSArgumentException("definitionName"); - } - - lock (_syncObject) - { - if (_latestJobRuns.ContainsKey(definitionName)) - { - return _latestJobRuns[definitionName]; - } - else - { - DateTime startJobRun = DateTime.MinValue; - _latestJobRuns.Add(definitionName, startJobRun); - return startJobRun; - } - } - } - - /// - /// Sets the latest job run Date/Time for the given definition name. - /// - /// - /// - public void SetLatestJobRun(string definitionName, DateTime jobRun) - { - if (string.IsNullOrEmpty(definitionName)) - { - throw new PSArgumentException("definitionName"); - } - - lock (_syncObject) - { - if (_latestJobRuns.ContainsKey(definitionName)) - { - _latestJobRuns.Remove(definitionName); - _latestJobRuns.Add(definitionName, jobRun); - } - else - { - _latestJobRuns.Add(definitionName, jobRun); - } - } - } - - /// - /// Search repository for specific job run. - /// - /// Definition name - /// Job run DateTime - /// Scheduled job if found - public ScheduledJob GetJob(string definitionName, DateTime jobRun) - { - lock (_syncObject) - { - foreach (ScheduledJob job in _jobs.Values) - { - if (job.PSBeginTime == null) - { - continue; - } - DateTime PSBeginTime = job.PSBeginTime ?? DateTime.MinValue; - if (definitionName.Equals(job.Definition.Name, StringComparison.OrdinalIgnoreCase) && - jobRun.Year == PSBeginTime.Year && - jobRun.Month == PSBeginTime.Month && - jobRun.Day == PSBeginTime.Day && - jobRun.Hour == PSBeginTime.Hour && - jobRun.Minute == PSBeginTime.Minute && - jobRun.Second == PSBeginTime.Second && - jobRun.Millisecond == PSBeginTime.Millisecond) - { - return job; - } - } - } - - return null; - } - - #endregion - } - - #endregion - } -} diff --git a/src/Microsoft.PowerShell.ScheduledJob/ScheduledJobStore.cs b/src/Microsoft.PowerShell.ScheduledJob/ScheduledJobStore.cs deleted file mode 100644 index 68bc4f14022..00000000000 --- a/src/Microsoft.PowerShell.ScheduledJob/ScheduledJobStore.cs +++ /dev/null @@ -1,683 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Text; -using System.IO; -using System.Globalization; -using System.Management.Automation; -using System.Security.AccessControl; -using System.Security.Principal; - -namespace Microsoft.PowerShell.ScheduledJob -{ - /// - /// This class encapsulates the work of determining the file location where - /// a job definition will be stored and retrieved and where job runs will - /// be stored and retrieved. Scheduled job definitions are stored in a - /// location based on the current user. Job runs are stored in the - /// corresponding scheduled job definition location under an "Output" - /// directory, where each run will have a subdirectory with a name derived - /// from the job run date/time. - /// - /// File Structure for "JobDefinitionFoo": - /// $env:User\AppData\Local\Windows\PowerShell\ScheduledJobs\JobDefinitionFoo\ - /// ScheduledJobDefinition.xml - /// Output\ - /// 110321-130942\ - /// Status.xml - /// Results.xml - /// 110319-173502\ - /// Status.xml - /// Results.xml - /// ... - /// - /// - internal class ScheduledJobStore - { - #region Public Enums - - public enum JobRunItem - { - None = 0, - Status = 1, - Results = 2 - } - - #endregion - - #region Public Strings - - public const string ScheduledJobsPath = @"Microsoft\Windows\PowerShell\ScheduledJobs"; - public const string DefinitionFileName = "ScheduledJobDefinition"; - public const string JobRunOutput = "Output"; - public const string ScheduledJobDefExistsFQEID = "ScheduledJobDefExists"; - - #endregion - - #region Public Methods - - /// - /// Returns FileStream object for existing scheduled job definition. - /// Definition file is looked for in the default user local appdata path. - /// - /// Scheduled job definition name. - /// File mode. - /// File access. - /// File share. - /// FileStream object. - public static FileStream GetFileForJobDefinition( - string definitionName, - FileMode fileMode, - FileAccess fileAccess, - FileShare fileShare) - { - if (string.IsNullOrEmpty(definitionName)) - { - throw new PSArgumentException("definitionName"); - } - - string filePathName = GetFilePathName(definitionName, DefinitionFileName); - return File.Open(filePathName, fileMode, fileAccess, fileShare); - } - - /// - /// Returns FileStream object for existing scheduled job definition. - /// Definition file is looked for in the path provided. - /// - /// Scheduled job definition name. - /// Scheduled job definition file path. - /// File mode. - /// File share. - /// File share. - /// - public static FileStream GetFileForJobDefinition( - string definitionName, - string definitionPath, - FileMode fileMode, - FileAccess fileAccess, - FileShare fileShare) - { - if (string.IsNullOrEmpty(definitionName)) - { - throw new PSArgumentException("definitionName"); - } - - if (string.IsNullOrEmpty(definitionPath)) - { - throw new PSArgumentException("definitionPath"); - } - - string filePathName = string.Format(CultureInfo.InvariantCulture, @"{0}\{1}\{2}.xml", - definitionPath, definitionName, DefinitionFileName); - return File.Open(filePathName, fileMode, fileAccess, fileShare); - } - - /// - /// Checks the provided path against the the default path of scheduled jobs - /// for the current user. - /// - /// Path for scheduled job definitions - /// True if paths are equal - public static bool IsDefaultUserPath(string definitionPath) - { - return definitionPath.Equals(GetJobDefinitionLocation(), StringComparison.OrdinalIgnoreCase); - } - - /// - /// Returns a FileStream object for a new scheduled job definition name. - /// - /// Scheduled job definition name. - /// FileStream object. - public static FileStream CreateFileForJobDefinition( - string definitionName) - { - if (string.IsNullOrEmpty(definitionName)) - { - throw new PSArgumentException("definitionName"); - } - - string filePathName = CreateFilePathName(definitionName, DefinitionFileName); - return File.Create(filePathName); - } - - /// - /// Returns an IEnumerable object of scheduled job definition names in - /// the job store. - /// - /// IEnumerable of job definition names. - public static IEnumerable GetJobDefinitions() - { - // Directory names are identical to the corresponding scheduled job definition names. - string directoryPath = GetDirectoryPath(); - IEnumerable definitions = Directory.EnumerateDirectories(directoryPath); - return (definitions != null) ? definitions : new Collection() as IEnumerable; - } - - /// - /// Returns a FileStream object for an existing scheduled job definition - /// run. - /// - /// Scheduled job definition name. - /// DateTime of job run start time. - /// Job run item. - /// File access - /// File mode - /// File share - /// FileStream object. - public static FileStream GetFileForJobRunItem( - string definitionName, - DateTime runStart, - JobRunItem runItem, - FileMode fileMode, - FileAccess fileAccess, - FileShare fileShare) - { - if (string.IsNullOrEmpty(definitionName)) - { - throw new PSArgumentException("definitionName"); - } - - string filePathName = GetRunFilePathName(definitionName, runItem, runStart); - return File.Open(filePathName, fileMode, fileAccess, fileShare); - } - - /// - /// Returns a FileStream object for a new scheduled job definition run. - /// - /// Scheduled job definition path. - /// DateTime of job run start time. - /// Job run item. - /// FileStream object. - public static FileStream CreateFileForJobRunItem( - string definitionOutputPath, - DateTime runStart, - JobRunItem runItem) - { - if (string.IsNullOrEmpty(definitionOutputPath)) - { - throw new PSArgumentException("definitionOutputPath"); - } - - string filePathName = GetRunFilePathNameFromPath(definitionOutputPath, runItem, runStart); - - // If the file already exists, we overwrite it because the job run - // can be updated multiple times while the job is running. - return File.Create(filePathName); - } - - /// - /// Returns a collection of DateTime objects which specify job run directories - /// that are currently in the store. - /// - /// Scheduled job definition name. - /// Collection of DateTime objects. - public static Collection GetJobRunsForDefinition( - string definitionName) - { - if (string.IsNullOrEmpty(definitionName)) - { - throw new PSArgumentException("definitionName"); - } - - string definitionOutputPath = GetJobRunOutputDirectory(definitionName); - - return GetJobRunsForDefinitionPath(definitionOutputPath); - } - - /// - /// Returns a collection of DateTime objects which specify job run directories - /// that are currently in the store. - /// - /// Scheduled job definition job run Output path. - /// Collection of DateTime objects. - public static Collection GetJobRunsForDefinitionPath( - string definitionOutputPath) - { - if (string.IsNullOrEmpty(definitionOutputPath)) - { - throw new PSArgumentException("definitionOutputPath"); - } - - Collection jobRunInfos = new Collection(); - IEnumerable jobRuns = Directory.EnumerateDirectories(definitionOutputPath); - if (jobRuns != null) - { - // Job run directory names are the date/times that the job was started. - foreach (string jobRun in jobRuns) - { - DateTime jobRunDateTime; - int indx = jobRun.LastIndexOf('\\'); - string jobRunName = (indx != -1) ? jobRun.Substring(indx + 1) : jobRun; - if (ConvertJobRunNameToDateTime(jobRunName, out jobRunDateTime)) - { - jobRunInfos.Add(jobRunDateTime); - } - } - } - - return jobRunInfos; - } - - /// - /// Remove the job definition and all job runs from job store. - /// - /// Scheduled Job Definition name - public static void RemoveJobDefinition( - string definitionName) - { - if (string.IsNullOrEmpty(definitionName)) - { - throw new PSArgumentException("definitionName"); - } - - // Remove job runs, job definition file, and job definition directory. - string jobDefDirectory = GetJobDefinitionPath(definitionName); - Directory.Delete(jobDefDirectory, true); - } - - /// - /// Renames the directory containing the old job definition name - /// to the new name provided. - /// - /// Existing job definition directory - /// Renamed job definition directory - public static void RenameScheduledJobDefDir( - string oldDefName, - string newDefName) - { - if (string.IsNullOrEmpty(oldDefName)) - { - throw new PSArgumentException("oldDefName"); - } - if (string.IsNullOrEmpty(newDefName)) - { - throw new PSArgumentException("newDefName"); - } - - string oldDirPath = GetJobDefinitionPath(oldDefName); - string newDirPath = GetJobDefinitionPath(newDefName); - Directory.Move(oldDirPath, newDirPath); - } - - /// - /// Remove a single job definition job run from the job store. - /// - /// Scheduled Job Definition name - /// DateTime of job run - public static void RemoveJobRun( - string definitionName, - DateTime runStart) - { - if (string.IsNullOrEmpty(definitionName)) - { - throw new PSArgumentException("definitionName"); - } - - // Remove the job run files and directory. - string runDirectory = GetRunDirectory(definitionName, runStart); - Directory.Delete(runDirectory, true); - } - - /// - /// Remove a single job definition job run from the job store. - /// - /// Scheduled Job Definition Output path - /// DateTime of job run - public static void RemoveJobRunFromOutputPath( - string definitionOutputPath, - DateTime runStart) - { - if (string.IsNullOrEmpty(definitionOutputPath)) - { - throw new PSArgumentException("definitionOutputPath"); - } - - // Remove the job run files and directory. - string runDirectory = GetRunDirectoryFromPath(definitionOutputPath, runStart); - Directory.Delete(runDirectory, true); - } - - /// - /// Remove all job runs for this job definition. - /// - /// Scheduled Job Definition name - public static void RemoveAllJobRuns( - string definitionName) - { - if (string.IsNullOrEmpty(definitionName)) - { - throw new PSArgumentException("definitionName"); - } - - Collection jobRuns = GetJobRunsForDefinition(definitionName); - foreach (DateTime jobRun in jobRuns) - { - string jobRunPath = GetRunDirectory(definitionName, jobRun); - Directory.Delete(jobRunPath, true); - } - } - - /// - /// Set read access on provided definition file for specified user. - /// - /// Definition name - /// Account user name - public static void SetReadAccessOnDefinitionFile( - string definitionName, - string user) - { - string filePath = GetFilePathName(definitionName, DefinitionFileName); - - // Get file security for existing file. - FileSecurity fileSecurity = new FileSecurity( - filePath, - AccessControlSections.Access); - - // Create rule. - FileSystemAccessRule fileAccessRule = new FileSystemAccessRule( - user, - FileSystemRights.Read, - AccessControlType.Allow); - fileSecurity.AddAccessRule(fileAccessRule); - - // Apply rule. - File.SetAccessControl(filePath, fileSecurity); - } - - /// - /// Set write access on Output directory for provided definition for - /// specified user. - /// - /// Definition name - /// Account user name - public static void SetWriteAccessOnJobRunOutput( - string definitionName, - string user) - { - string outputDirectoryPath = GetJobRunOutputDirectory(definitionName); - AddFullAccessToDirectory(user, outputDirectoryPath); - } - - /// - /// Returns the directory path for job run output for the specified - /// scheduled job definition. - /// - /// Definition name - /// Directory Path - public static string GetJobRunOutputDirectory( - string definitionName) - { - if (string.IsNullOrEmpty(definitionName)) - { - throw new PSArgumentException("definitionName"); - } - - return Path.Combine(GetJobDefinitionPath(definitionName), JobRunOutput); - } - - /// - /// Gets the directory path for a Scheduled Job Definition. - /// - /// Directory Path - public static string GetJobDefinitionLocation() - { -#if UNIX - return Path.Combine(Platform.SelectProductNameForDirectory(Platform.XDG_Type.CACHE), "ScheduledJobs")); -#else - return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), ScheduledJobsPath); -#endif - } - - public static void CreateDirectoryIfNotExists() - { - GetDirectoryPath(); - } - - #endregion - - #region Private Methods - - /// - /// Gets the directory path for Scheduled Jobs. Will create the directory if - /// it does not exist. - /// - /// Directory Path - private static string GetDirectoryPath() - { - string pathName; -#if UNIX - pathName = Path.Combine(Platform.SelectProductNameForDirectory(Platform.XDG_Type.CACHE), "ScheduledJobs")); -#else - pathName = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), ScheduledJobsPath); -#endif - if (!Directory.Exists(pathName)) - { - Directory.CreateDirectory(pathName); - } - - return pathName; - } - - /// - /// Creates a ScheduledJob definition directory with provided definition name - /// along with a job run Output directory, and returns a file path/name. - /// ...\ScheduledJobs\definitionName\fileName.xml - /// ...\ScheduledJobs\definitionName\Output\ - /// - /// Definition name - /// File name - /// File path/name - private static string CreateFilePathName(string definitionName, string fileName) - { - string filePath = GetJobDefinitionPath(definitionName); - string outputPath = GetJobRunOutputDirectory(definitionName); - if (Directory.Exists(filePath)) - { - ScheduledJobException ex = new ScheduledJobException(StringUtil.Format(ScheduledJobErrorStrings.JobDefFileAlreadyExists, definitionName)); - ex.FQEID = ScheduledJobDefExistsFQEID; - throw ex; - } - - Directory.CreateDirectory(filePath); - Directory.CreateDirectory(outputPath); - return string.Format(CultureInfo.InstalledUICulture, @"{0}\{1}.xml", filePath, fileName); - } - - /// - /// Returns a file path/name for an existing Scheduled job definition directory. - /// - /// Definition name - /// File name - /// File path/name - private static string GetFilePathName(string definitionName, string fileName) - { - string filePath = GetJobDefinitionPath(definitionName); - return string.Format(CultureInfo.InvariantCulture, @"{0}\{1}.xml", filePath, fileName); - } - - /// - /// Gets the directory path for a Scheduled Job Definition. - /// - /// Scheduled job definition name - /// Directory Path - private static string GetJobDefinitionPath(string definitionName) - { -#if UNIX - return Path.Combine(Platform.SelectProductNameForDirectory(Platform.XDG_Type.CACHE), "ScheduledJobs", definitionName); -#else - return Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), - ScheduledJobsPath, - definitionName); -#endif - } - - /// - /// Returns a directory path for an existing ScheduledJob run result directory. - /// - /// Definition name - /// File name - /// Directory Path - private static string GetRunDirectory( - string definitionName, - DateTime runStart) - { - string directoryPath = GetJobRunOutputDirectory(definitionName); - return string.Format(CultureInfo.InvariantCulture, @"{0}\{1}", directoryPath, - ConvertDateTimeToJobRunName(runStart)); - } - - /// - /// Returns a directory path for an existing ScheduledJob run based on - /// provided definition Output directory path. - /// - /// Output directory path - /// File name - /// Directory Path - private static string GetRunDirectoryFromPath( - string definitionOutputPath, - DateTime runStart) - { - return string.Format(CultureInfo.InvariantCulture, @"{0}\{1}", - definitionOutputPath, ConvertDateTimeToJobRunName(runStart)); - } - - /// - /// Returns a file path/name for a run result file. Will create the - /// job run directory if it does not exist. - /// - /// Definition name - /// Result type - /// Run date - /// File path/name - private static string GetRunFilePathName( - string definitionName, - JobRunItem runItem, - DateTime runStart) - { - string directoryPath = GetJobRunOutputDirectory(definitionName); - string jobRunPath = string.Format(CultureInfo.InvariantCulture, @"{0}\{1}", - directoryPath, ConvertDateTimeToJobRunName(runStart)); - - return string.Format(CultureInfo.InvariantCulture, @"{0}\{1}.xml", jobRunPath, - runItem.ToString()); - } - - /// - /// Returns a file path/name for a job run result, based on the passed in - /// job run output path. Will create the job run directory if it does not - /// exist. - /// - /// Definition job run output path - /// Result type - /// Run date - /// - private static string GetRunFilePathNameFromPath( - string outputPath, - JobRunItem runItem, - DateTime runStart) - { - string jobRunPath = string.Format(CultureInfo.InvariantCulture, @"{0}\{1}", - outputPath, ConvertDateTimeToJobRunName(runStart)); - - if (!Directory.Exists(jobRunPath)) - { - // Create directory for this job run date. - Directory.CreateDirectory(jobRunPath); - } - - return string.Format(CultureInfo.InvariantCulture, @"{0}\{1}.xml", jobRunPath, - runItem.ToString()); - } - - private static void AddFullAccessToDirectory( - string user, - string directoryPath) - { - // Create rule. - DirectoryInfo info = new DirectoryInfo(directoryPath); - DirectorySecurity dSecurity = info.GetAccessControl(); - FileSystemAccessRule fileAccessRule = new FileSystemAccessRule( - user, - FileSystemRights.FullControl, - InheritanceFlags.ContainerInherit | InheritanceFlags.ObjectInherit, - PropagationFlags.None, - AccessControlType.Allow); - - // Apply rule. - dSecurity.AddAccessRule(fileAccessRule); - info.SetAccessControl(dSecurity); - } - - // - // String format: 'YYYYMMDD-HHMMSS-SSS' - // ,where SSS is milliseconds. - // - - private static string ConvertDateTimeToJobRunName(DateTime dt) - { - return string.Format(CultureInfo.InvariantCulture, - @"{0:d4}{1:d2}{2:d2}-{3:d2}{4:d2}{5:d2}-{6:d3}", - dt.Year, dt.Month, dt.Day, - dt.Hour, dt.Minute, dt.Second, dt.Millisecond); - } - - /// - /// Converts a jobRun name string to an equivalent DateTime - /// - /// - /// - /// - internal static bool ConvertJobRunNameToDateTime(string jobRunName, out DateTime jobRun) - { - if (jobRunName == null || jobRunName.Length != 19) - { - jobRun = new DateTime(); - return false; - } - - int year = 0; - int month = 0; - int day = 0; - int hour = 0; - int minute = 0; - int second = 0; - int msecs = 0; - bool success = true; - - try - { - year = Convert.ToInt32(jobRunName.Substring(0, 4)); - month = Convert.ToInt32(jobRunName.Substring(4, 2)); - day = Convert.ToInt32(jobRunName.Substring(6, 2)); - hour = Convert.ToInt32(jobRunName.Substring(9, 2)); - minute = Convert.ToInt32(jobRunName.Substring(11, 2)); - second = Convert.ToInt32(jobRunName.Substring(13, 2)); - msecs = Convert.ToInt32(jobRunName.Substring(16, 3)); - } - catch (FormatException) - { - success = false; - } - catch (OverflowException) - { - success = false; - } - - if (success) - { - jobRun = new DateTime(year, month, day, hour, minute, second, msecs); - } - else - { - jobRun = new DateTime(); - } - - return success; - } - - #endregion - } -} diff --git a/src/Microsoft.PowerShell.ScheduledJob/ScheduledJobTrigger.cs b/src/Microsoft.PowerShell.ScheduledJob/ScheduledJobTrigger.cs deleted file mode 100644 index 9e0dbfd47c4..00000000000 --- a/src/Microsoft.PowerShell.ScheduledJob/ScheduledJobTrigger.cs +++ /dev/null @@ -1,880 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Text; -using System.Runtime.Serialization; -using System.Management.Automation; -using System.Globalization; -using System.Threading; -using Microsoft.Management.Infrastructure; -using System.Security.Permissions; - -namespace Microsoft.PowerShell.ScheduledJob -{ - /// - /// This class contains parameters used to define how/when a PowerShell job is - /// run via the Windows Task Scheduler (WTS). - /// - [Serializable] - public sealed class ScheduledJobTrigger : ISerializable - { - #region Private Members - - private DateTime? _time; - private List _daysOfWeek; - private TimeSpan _randomDelay; - private Int32 _interval = 1; - private string _user; - private TriggerFrequency _frequency = TriggerFrequency.None; - private TimeSpan? _repInterval; - private TimeSpan? _repDuration; - - private Int32 _id; - private bool _enabled = true; - private ScheduledJobDefinition _jobDefAssociation; - - private static string _allUsers = "*"; - - #endregion - - #region Public Properties - - /// - /// Trigger time. - /// - public DateTime? At - { - get { return _time; } - set { _time = value; } - } - - /// - /// Trigger days of week. - /// - public List DaysOfWeek - { - get { return _daysOfWeek; } - set { _daysOfWeek = value; } - } - - /// - /// Trigger days or weeks interval. - /// - public Int32 Interval - { - get { return _interval; } - set { _interval = value; } - } - - /// - /// Trigger frequency. - /// - public TriggerFrequency Frequency - { - get { return _frequency; } - set { _frequency = value; } - } - - /// - /// Trigger random delay. - /// - public TimeSpan RandomDelay - { - get { return _randomDelay; } - set { _randomDelay = value; } - } - - /// - /// Trigger Once frequency repetition interval. - /// - public TimeSpan? RepetitionInterval - { - get { return _repInterval; } - set - { - // A TimeSpan value of zero is equivalent to a null value. - _repInterval = (value != null && value.Value == TimeSpan.Zero) ? - null : value; - } - } - - /// - /// Trigger Once frequency repetition duration. - /// - public TimeSpan? RepetitionDuration - { - get { return _repDuration; } - set - { - // A TimeSpan value of zero is equivalent to a null value. - _repDuration = (value != null && value.Value == TimeSpan.Zero) ? - null : value; - } - } - - /// - /// Trigger user name. - /// - public string User - { - get { return _user; } - set { _user = value; } - } - - /// - /// Returns the trigger local Id. - /// - public Int32 Id - { - get { return _id; } - internal set { _id = value; } - } - - /// - /// Defines enabled state of trigger. - /// - public bool Enabled - { - get { return _enabled; } - set { _enabled = value; } - } - - /// - /// ScheduledJobDefinition object this trigger is associated with. - /// - public ScheduledJobDefinition JobDefinition - { - get { return _jobDefAssociation; } - internal set { _jobDefAssociation = value; } - } - - #endregion - - #region Constructors - - /// - /// Default constructor. - /// - public ScheduledJobTrigger() - { } - - /// - /// Constructor. - /// - /// Enabled - /// Trigger frequency - /// Trigger time - /// Weekly days of week - /// Daily or Weekly interval - /// Random delay - /// Repetition interval - /// Repetition duration - /// Logon user - /// Trigger id - private ScheduledJobTrigger( - bool enabled, - TriggerFrequency frequency, - DateTime? time, - List daysOfWeek, - Int32 interval, - TimeSpan randomDelay, - TimeSpan? repetitionInterval, - TimeSpan? repetitionDuration, - string user, - Int32 id) - { - _enabled = enabled; - _frequency = frequency; - _time = time; - _daysOfWeek = daysOfWeek; - _interval = interval; - _randomDelay = randomDelay; - RepetitionInterval = repetitionInterval; - RepetitionDuration = repetitionDuration; - _user = user; - _id = id; - } - - /// - /// Copy constructor. - /// - /// ScheduledJobTrigger - internal ScheduledJobTrigger(ScheduledJobTrigger copyTrigger) - { - if (copyTrigger == null) - { - throw new PSArgumentNullException("copyTrigger"); - } - - _enabled = copyTrigger.Enabled; - _frequency = copyTrigger.Frequency; - _id = copyTrigger.Id; - _time = copyTrigger.At; - _daysOfWeek = copyTrigger.DaysOfWeek; - _interval = copyTrigger.Interval; - _randomDelay = copyTrigger.RandomDelay; - _repInterval = copyTrigger.RepetitionInterval; - _repDuration = copyTrigger.RepetitionDuration; - _user = copyTrigger.User; - - _jobDefAssociation = copyTrigger.JobDefinition; - } - - /// - /// Serialization constructor - /// - /// SerializationInfo - /// StreamingContext - [SecurityPermissionAttribute(SecurityAction.Demand, SerializationFormatter = true)] - private ScheduledJobTrigger( - SerializationInfo info, - StreamingContext context) - { - if (info == null) - { - throw new PSArgumentNullException("info"); - } - - DateTime time = info.GetDateTime("Time_Value"); - if (time != DateTime.MinValue) - { - _time = time; - } - else - { - _time = null; - } - - RepetitionInterval = (TimeSpan?)info.GetValue("RepetitionInterval_Value", typeof(TimeSpan)); - RepetitionDuration = (TimeSpan?)info.GetValue("RepetitionDuration_Value", typeof(TimeSpan)); - - _daysOfWeek = (List)info.GetValue("DaysOfWeek_Value", typeof(List)); - _randomDelay = (TimeSpan)info.GetValue("RandomDelay_Value", typeof(TimeSpan)); - _interval = info.GetInt32("Interval_Value"); - _user = info.GetString("User_Value"); - _frequency = (TriggerFrequency)info.GetValue("TriggerFrequency_Value", typeof(TriggerFrequency)); - _id = info.GetInt32("ID_Value"); - _enabled = info.GetBoolean("Enabled_Value"); - - // Runtime reference and not saved to store. - _jobDefAssociation = null; - } - - #endregion - - #region ISerializable Implementation - - /// - /// GetObjectData for ISerializable implementation. - /// - /// - /// - [SecurityPermissionAttribute(SecurityAction.Demand, SerializationFormatter = true)] - public void GetObjectData(SerializationInfo info, StreamingContext context) - { - if (info == null) - { - throw new PSArgumentNullException("info"); - } - - if (_time == null) - { - info.AddValue("Time_Value", DateTime.MinValue); - } - else - { - info.AddValue("Time_Value", _time); - } - - if (_repInterval == null) - { - info.AddValue("RepetitionInterval_Value", TimeSpan.Zero); - } - else - { - info.AddValue("RepetitionInterval_Value", _repInterval); - } - - if (_repDuration == null) - { - info.AddValue("RepetitionDuration_Value", TimeSpan.Zero); - } - else - { - info.AddValue("RepetitionDuration_Value", _repDuration); - } - - info.AddValue("DaysOfWeek_Value", _daysOfWeek); - info.AddValue("RandomDelay_Value", _randomDelay); - info.AddValue("Interval_Value", _interval); - info.AddValue("User_Value", _user); - info.AddValue("TriggerFrequency_Value", _frequency); - info.AddValue("ID_Value", _id); - info.AddValue("Enabled_Value", _enabled); - } - - #endregion - - #region Internal Methods - - internal void ClearProperties() - { - _time = null; - _daysOfWeek = null; - _interval = 1; - _randomDelay = TimeSpan.Zero; - _repInterval = null; - _repDuration = null; - _user = null; - _frequency = TriggerFrequency.None; - _enabled = false; - _id = 0; - } - - internal void Validate() - { - switch (_frequency) - { - case TriggerFrequency.None: - throw new ScheduledJobException(ScheduledJobErrorStrings.MissingJobTriggerType); - - case TriggerFrequency.AtStartup: - // AtStartup has no required parameters. - break; - - case TriggerFrequency.AtLogon: - // AtLogon has no required parameters. - break; - - case TriggerFrequency.Once: - if (_time == null) - { - string msg = StringUtil.Format(ScheduledJobErrorStrings.MissingJobTriggerTime, ScheduledJobErrorStrings.TriggerOnceType); - throw new ScheduledJobException(msg); - } - if (_repInterval != null || _repDuration != null) - { - ValidateOnceRepetitionParams(_repInterval, _repDuration); - } - break; - - case TriggerFrequency.Daily: - if (_time == null) - { - string msg = StringUtil.Format(ScheduledJobErrorStrings.MissingJobTriggerTime, ScheduledJobErrorStrings.TriggerDailyType); - throw new ScheduledJobException(msg); - } - if (_interval < 1) - { - throw new ScheduledJobException(ScheduledJobErrorStrings.InvalidDaysIntervalParam); - } - break; - - case TriggerFrequency.Weekly: - if (_time == null) - { - string msg = StringUtil.Format(ScheduledJobErrorStrings.MissingJobTriggerTime, ScheduledJobErrorStrings.TriggerWeeklyType); - throw new ScheduledJobException(msg); - } - if (_interval < 1) - { - throw new ScheduledJobException(ScheduledJobErrorStrings.InvalidWeeksIntervalParam); - } - if (_daysOfWeek == null || _daysOfWeek.Count == 0) - { - string msg = StringUtil.Format(ScheduledJobErrorStrings.MissingJobTriggerDaysOfWeek, ScheduledJobErrorStrings.TriggerWeeklyType); - throw new ScheduledJobException(msg); - } - break; - } - } - - internal static void ValidateOnceRepetitionParams( - TimeSpan? repInterval, - TimeSpan? repDuration) - { - // Both Interval and Duration parameters must be specified together. - if (repInterval == null || repDuration == null) - { - throw new PSArgumentException(ScheduledJobErrorStrings.InvalidRepetitionParams); - } - - // Interval and Duration parameters must not have negative value. - if (repInterval < TimeSpan.Zero || repDuration < TimeSpan.Zero) - { - throw new PSArgumentException(ScheduledJobErrorStrings.InvalidRepetitionParamValues); - } - - // Zero values are allowed but only if both parameters are set to zero. - // This removes repetition from the Once trigger. - if (repInterval == TimeSpan.Zero && repDuration != TimeSpan.Zero) - { - throw new PSArgumentException(ScheduledJobErrorStrings.MismatchedRepetitionParamValues); - } - - // Parameter values must be GE to one minute unless both are zero to remove repetition. - if (repInterval < TimeSpan.FromMinutes(1) && - !(repInterval == TimeSpan.Zero && repDuration == TimeSpan.Zero)) - { - throw new PSArgumentException(ScheduledJobErrorStrings.InvalidRepetitionIntervalValue); - } - - // Interval parameter must be LE to Duration parameter. - if (repInterval > repDuration) - { - throw new PSArgumentException(ScheduledJobErrorStrings.InvalidRepetitionInterval); - } - } - - internal void CopyTo(ScheduledJobTrigger targetTrigger) - { - if (targetTrigger == null) - { - throw new PSArgumentNullException("targetTrigger"); - } - - targetTrigger.Enabled = _enabled; - targetTrigger.Frequency = _frequency; - targetTrigger.Id = _id; - targetTrigger.At = _time; - targetTrigger.DaysOfWeek = _daysOfWeek; - targetTrigger.Interval = _interval; - targetTrigger.RandomDelay = _randomDelay; - targetTrigger.RepetitionInterval = _repInterval; - targetTrigger.RepetitionDuration = _repDuration; - targetTrigger.User = _user; - targetTrigger.JobDefinition = _jobDefAssociation; - } - - #endregion - - #region Static methods - - /// - /// Creates a one time ScheduledJobTrigger object. - /// - /// DateTime when trigger activates - /// Random delay - /// Repetition interval - /// Repetition duration - /// Trigger Id - /// Trigger enabled state - /// ScheduledJobTrigger - public static ScheduledJobTrigger CreateOnceTrigger( - DateTime time, - TimeSpan delay, - TimeSpan? repetitionInterval, - TimeSpan? repetitionDuration, - Int32 id, - bool enabled) - { - return new ScheduledJobTrigger( - enabled, - TriggerFrequency.Once, - time, - null, - 1, - delay, - repetitionInterval, - repetitionDuration, - null, - id); - } - - /// - /// Creates a daily ScheduledJobTrigger object. - /// - /// Time of day when trigger activates - /// Days interval for trigger activation - /// Random delay - /// Trigger Id - /// Trigger enabled state - /// ScheduledJobTrigger - public static ScheduledJobTrigger CreateDailyTrigger( - DateTime time, - Int32 interval, - TimeSpan delay, - Int32 id, - bool enabled) - { - return new ScheduledJobTrigger( - enabled, - TriggerFrequency.Daily, - time, - null, - interval, - delay, - null, - null, - null, - id); - } - - /// - /// Creates a weekly ScheduledJobTrigger object. - /// - /// Time of day when trigger activates - /// Weeks interval for trigger activation - /// Days of the week for trigger activation - /// Random delay - /// Trigger Id - /// Trigger enabled state - /// ScheduledJobTrigger - public static ScheduledJobTrigger CreateWeeklyTrigger( - DateTime time, - Int32 interval, - IEnumerable daysOfWeek, - TimeSpan delay, - Int32 id, - bool enabled) - { - List lDaysOfWeek = (daysOfWeek != null) ? new List(daysOfWeek) : null; - - return new ScheduledJobTrigger( - enabled, - TriggerFrequency.Weekly, - time, - lDaysOfWeek, - interval, - delay, - null, - null, - null, - id); - } - - /// - /// Creates a trigger that activates after user log on. - /// - /// Name of user - /// Random delay - /// Trigger Id - /// Trigger enabled state - /// ScheduledJobTrigger - public static ScheduledJobTrigger CreateAtLogOnTrigger( - string user, - TimeSpan delay, - Int32 id, - bool enabled) - { - return new ScheduledJobTrigger( - enabled, - TriggerFrequency.AtLogon, - null, - null, - 1, - delay, - null, - null, - string.IsNullOrEmpty(user) ? AllUsers : user, - id); - } - - /// - /// Creates a trigger that activates after OS boot. - /// - /// Random delay - /// Trigger Id - /// Trigger enabled state - /// ScheduledJobTrigger - public static ScheduledJobTrigger CreateAtStartupTrigger( - TimeSpan delay, - Int32 id, - bool enabled) - { - return new ScheduledJobTrigger( - enabled, - TriggerFrequency.AtStartup, - null, - null, - 1, - delay, - null, - null, - null, - id); - } - - /// - /// Compares provided user name to All Users string ("*"). - /// - /// Logon user name. - /// Boolean, true if All Users. - internal static bool IsAllUsers(string userName) - { - return (string.Compare(userName, ScheduledJobTrigger.AllUsers, - StringComparison.OrdinalIgnoreCase) == 0); - } - - /// - /// Returns the All Users string. - /// - internal static string AllUsers - { - get { return _allUsers; } - } - - #endregion - - #region Public Methods - - /// - /// Update the associated ScheduledJobDefinition object with the - /// current properties of this object. - /// - public void UpdateJobDefinition() - { - if (_jobDefAssociation == null) - { - string msg = StringUtil.Format(ScheduledJobErrorStrings.NoAssociatedJobDefinitionForTrigger, _id); - throw new RuntimeException(msg); - } - - _jobDefAssociation.UpdateTriggers(new ScheduledJobTrigger[1] { this }, true); - } - - #endregion - } - - #region Public Enums - - /// - /// Specifies trigger types in terms of the frequency that - /// the trigger is activated. - /// - public enum TriggerFrequency - { - /// - /// None. - /// - None = 0, - /// - /// Trigger activates once at a specified time. - /// - Once = 1, - /// - /// Trigger activates daily. - /// - Daily = 2, - /// - /// Trigger activates on a weekly basis and multiple days - /// during the week. - /// - Weekly = 3, - /// - /// Trigger activates at user logon to the operating system. - /// - AtLogon = 4, - /// - /// Trigger activates after machine boot up. - /// - AtStartup = 5 - } - - #endregion - - #region JobTriggerToCimInstanceConverter - /// - /// Class providing implementation of PowerShell conversions for types in Microsoft.Management.Infrastructure namespace - /// - public sealed class JobTriggerToCimInstanceConverter : PSTypeConverter - { - private static readonly string CIM_TRIGGER_NAMESPACE = @"Root\Microsoft\Windows\TaskScheduler"; - - /// - /// Determines if the converter can convert the parameter to the parameter. - /// - /// The value to convert from - /// The type to convert to - /// True if the converter can convert the parameter to the parameter, otherwise false. - public override bool CanConvertFrom(object sourceValue, Type destinationType) - { - if (destinationType == null) - { - throw new ArgumentNullException("destinationType"); - } - - return (sourceValue is ScheduledJobTrigger) && (destinationType.Equals(typeof(CimInstance))); - } - - /// - /// Converts the parameter to the parameter using formatProvider and ignoreCase - /// - /// The value to convert from - /// The type to convert to - /// The format provider to use like in IFormattable's ToString - /// true if case should be ignored - /// the parameter converted to the parameter using formatProvider and ignoreCase - /// if no conversion was possible - public override object ConvertFrom(object sourceValue, Type destinationType, IFormatProvider formatProvider, bool ignoreCase) - { - if (destinationType == null) - { - throw new ArgumentNullException("destinationType"); - } - - if (sourceValue == null) - { - throw new ArgumentNullException("sourceValue"); - } - - ScheduledJobTrigger originalTrigger = (ScheduledJobTrigger) sourceValue; - using (CimSession cimSession = CimSession.Create(null)) - { - switch (originalTrigger.Frequency) - { - case TriggerFrequency.Weekly: - return ConvertToWeekly(originalTrigger, cimSession); - case TriggerFrequency.Once: - return ConvertToOnce(originalTrigger, cimSession); - case TriggerFrequency.Daily: - return ConvertToDaily(originalTrigger, cimSession); - case TriggerFrequency.AtStartup: - return ConvertToAtStartup(originalTrigger, cimSession); - case TriggerFrequency.AtLogon: - return ConvertToAtLogon(originalTrigger, cimSession); - case TriggerFrequency.None: - return ConvertToDefault(originalTrigger, cimSession); - default: - string errorMsg = StringUtil.Format(ScheduledJobErrorStrings.UnknownTriggerFrequency, - originalTrigger.Frequency.ToString()); - throw new PSInvalidOperationException(errorMsg); - } - } - } - - /// - /// Returns true if the converter can convert the parameter to the parameter - /// - /// The value to convert from - /// The type to convert to - /// True if the converter can convert the parameter to the parameter, otherwise false. - public override bool CanConvertTo(object sourceValue, Type destinationType) - { - return false; - } - - /// - /// Converts the parameter to the parameter using formatProvider and ignoreCase - /// - /// The value to convert from - /// The type to convert to - /// The format provider to use like in IFormattable's ToString - /// true if case should be ignored - /// sourceValue converted to the parameter using formatProvider and ignoreCase - /// if no conversion was possible - public override object ConvertTo(object sourceValue, Type destinationType, IFormatProvider formatProvider, bool ignoreCase) - { - throw new NotImplementedException(); - } - - #region Helper Methods - - private CimInstance ConvertToWeekly(ScheduledJobTrigger trigger, CimSession cimSession) - { - CimClass cimClass = cimSession.GetClass(CIM_TRIGGER_NAMESPACE, "MSFT_TaskWeeklyTrigger"); - CimInstance cimInstance = new CimInstance(cimClass); - - cimInstance.CimInstanceProperties["DaysOfWeek"].Value = ScheduledJobWTS.ConvertDaysOfWeekToMask(trigger.DaysOfWeek); - cimInstance.CimInstanceProperties["RandomDelay"].Value = ScheduledJobWTS.ConvertTimeSpanToWTSString(trigger.RandomDelay); - cimInstance.CimInstanceProperties["WeeksInterval"].Value = trigger.Interval; - - AddCommonProperties(trigger, cimInstance); - return cimInstance; - } - - private CimInstance ConvertToOnce(ScheduledJobTrigger trigger, CimSession cimSession) - { - CimClass cimClass = cimSession.GetClass(CIM_TRIGGER_NAMESPACE, "MSFT_TaskTimeTrigger"); - CimInstance cimInstance = new CimInstance(cimClass); - - cimInstance.CimInstanceProperties["RandomDelay"].Value = ScheduledJobWTS.ConvertTimeSpanToWTSString(trigger.RandomDelay); - - if (trigger.RepetitionInterval != null && trigger.RepetitionDuration != null) - { - CimClass cimRepClass = cimSession.GetClass(CIM_TRIGGER_NAMESPACE, "MSFT_TaskRepetitionPattern"); - CimInstance cimRepInstance = new CimInstance(cimRepClass); - - cimRepInstance.CimInstanceProperties["Interval"].Value = ScheduledJobWTS.ConvertTimeSpanToWTSString(trigger.RepetitionInterval.Value); - - if (trigger.RepetitionDuration == TimeSpan.MaxValue) - { - cimRepInstance.CimInstanceProperties["StopAtDurationEnd"].Value = false; - } - else - { - cimRepInstance.CimInstanceProperties["StopAtDurationEnd"].Value = true; - cimRepInstance.CimInstanceProperties["Duration"].Value = ScheduledJobWTS.ConvertTimeSpanToWTSString(trigger.RepetitionDuration.Value); - } - - cimInstance.CimInstanceProperties["Repetition"].Value = cimRepInstance; - } - - AddCommonProperties(trigger, cimInstance); - return cimInstance; - } - - private CimInstance ConvertToDaily(ScheduledJobTrigger trigger, CimSession cimSession) - { - CimClass cimClass = cimSession.GetClass(CIM_TRIGGER_NAMESPACE, "MSFT_TaskDailyTrigger"); - CimInstance cimInstance = new CimInstance(cimClass); - - cimInstance.CimInstanceProperties["RandomDelay"].Value = ScheduledJobWTS.ConvertTimeSpanToWTSString(trigger.RandomDelay); - cimInstance.CimInstanceProperties["DaysInterval"].Value = trigger.Interval; - - AddCommonProperties(trigger, cimInstance); - return cimInstance; - } - - private CimInstance ConvertToAtLogon(ScheduledJobTrigger trigger, CimSession cimSession) - { - CimClass cimClass = cimSession.GetClass(CIM_TRIGGER_NAMESPACE, "MSFT_TaskLogonTrigger"); - CimInstance cimInstance = new CimInstance(cimClass); - - cimInstance.CimInstanceProperties["Delay"].Value = ScheduledJobWTS.ConvertTimeSpanToWTSString(trigger.RandomDelay); - - // Convert the "AllUsers" name ("*" character) to null for Task Scheduler. - string userId = (ScheduledJobTrigger.IsAllUsers(trigger.User)) ? null : trigger.User; - cimInstance.CimInstanceProperties["UserId"].Value = userId; - - AddCommonProperties(trigger, cimInstance); - return cimInstance; - } - - private CimInstance ConvertToAtStartup(ScheduledJobTrigger trigger, CimSession cimSession) - { - CimClass cimClass = cimSession.GetClass(CIM_TRIGGER_NAMESPACE, "MSFT_TaskBootTrigger"); - CimInstance cimInstance = new CimInstance(cimClass); - - cimInstance.CimInstanceProperties["Delay"].Value = ScheduledJobWTS.ConvertTimeSpanToWTSString(trigger.RandomDelay); - - AddCommonProperties(trigger, cimInstance); - return cimInstance; - } - - private CimInstance ConvertToDefault(ScheduledJobTrigger trigger, CimSession cimSession) - { - CimClass cimClass = cimSession.GetClass(CIM_TRIGGER_NAMESPACE, "MSFT_TaskTrigger"); - CimInstance result = new CimInstance(cimClass); - AddCommonProperties(trigger, result); - return result; - } - - private static void AddCommonProperties(ScheduledJobTrigger trigger, CimInstance cimInstance) - { - cimInstance.CimInstanceProperties["Enabled"].Value = trigger.Enabled; - - if (trigger.At != null) - { - cimInstance.CimInstanceProperties["StartBoundary"].Value = ScheduledJobWTS.ConvertDateTimeToString(trigger.At); - } - } - - #endregion - } - - #endregion -} diff --git a/src/Microsoft.PowerShell.ScheduledJob/ScheduledJobWTS.cs b/src/Microsoft.PowerShell.ScheduledJob/ScheduledJobWTS.cs deleted file mode 100644 index 3aec4228383..00000000000 --- a/src/Microsoft.PowerShell.ScheduledJob/ScheduledJobWTS.cs +++ /dev/null @@ -1,947 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Text; -using TaskScheduler; -using System.Diagnostics; -using System.Globalization; -using System.Management.Automation; -using System.Runtime.InteropServices; -using System.Security.AccessControl; - -namespace Microsoft.PowerShell.ScheduledJob -{ - /// - /// Managed code class to provide Windows Task Scheduler functionality for - /// scheduled jobs. - /// - internal sealed class ScheduledJobWTS : IDisposable - { - #region Private Members - - private ITaskService _taskScheduler; - private ITaskFolder _iRootFolder; - - private const short WTSSunday = 0x01; - private const short WTSMonday = 0x02; - private const short WTSTuesday = 0x04; - private const short WTSWednesday = 0x08; - private const short WTSThursday = 0x10; - private const short WTSFriday = 0x20; - private const short WTSSaturday = 0x40; - - // Task Scheduler folders for PowerShell scheduled job tasks. - private const string TaskSchedulerWindowsFolder = @"\Microsoft\Windows"; - private const string ScheduledJobSubFolder = @"PowerShell\ScheduledJobs"; - private const string ScheduledJobTasksRootFolder = @"\Microsoft\Windows\PowerShell\ScheduledJobs"; - - // Define a single Action Id since PowerShell Scheduled Job tasks will have only one action. - private const string ScheduledJobTaskActionId = "StartPowerShellJob"; - - #endregion - - #region Constructors - - public ScheduledJobWTS() - { - // Create the Windows Task Scheduler object. - _taskScheduler = (ITaskService)new TaskScheduler.TaskScheduler(); - - // Connect the task scheduler object to the local machine - // using the current user security token. - _taskScheduler.Connect(null, null, null, null); - - // Get or create the root folder in Task Scheduler for PowerShell scheduled jobs. - _iRootFolder = GetRootFolder(); - } - - #endregion - - #region Public Methods - - /// - /// Retrieves job triggers from WTS with provided task Id. - /// - /// Task Id - /// Task not found. - /// ScheduledJobTriggers - public Collection GetJobTriggers( - string taskId) - { - if (string.IsNullOrEmpty(taskId)) - { - throw new PSArgumentException("taskId"); - } - - ITaskDefinition iTaskDefinition = FindTask(taskId); - - Collection jobTriggers = new Collection(); - ITriggerCollection iTriggerCollection = iTaskDefinition.Triggers; - if (iTriggerCollection != null) - { - foreach (ITrigger iTrigger in iTriggerCollection) - { - ScheduledJobTrigger jobTrigger = CreateJobTrigger(iTrigger); - if (jobTrigger == null) - { - string msg = StringUtil.Format(ScheduledJobErrorStrings.UnknownTriggerType, taskId, iTrigger.Id); - throw new ScheduledJobException(msg); - } - - jobTriggers.Add(jobTrigger); - } - } - - return jobTriggers; - } - - /// - /// Retrieves options for the provided task Id. - /// - /// Task Id - /// Task not found. - /// ScheduledJobOptions - public ScheduledJobOptions GetJobOptions( - string taskId) - { - if (string.IsNullOrEmpty(taskId)) - { - throw new PSArgumentException("taskId"); - } - - ITaskDefinition iTaskDefinition = FindTask(taskId); - - return CreateJobOptions(iTaskDefinition); - } - - /// - /// Returns a boolean indicating whether the job/task is enabled - /// in the Task Scheduler. - /// - /// - /// - public bool GetTaskEnabled( - string taskId) - { - if (string.IsNullOrEmpty(taskId)) - { - throw new PSArgumentException("taskId"); - } - - ITaskDefinition iTaskDefinition = FindTask(taskId); - - return iTaskDefinition.Settings.Enabled; - } - - /// - /// Creates a new task in WTS with information from ScheduledJobDefinition. - /// - /// ScheduledJobDefinition - public void CreateTask( - ScheduledJobDefinition definition) - { - if (definition == null) - { - throw new PSArgumentNullException("definition"); - } - - // Create task definition - ITaskDefinition iTaskDefinition = _taskScheduler.NewTask(0); - - // Add task options. - AddTaskOptions(iTaskDefinition, definition.Options); - - // Add task triggers. - foreach (ScheduledJobTrigger jobTrigger in definition.JobTriggers) - { - AddTaskTrigger(iTaskDefinition, jobTrigger); - } - - // Add task action. - AddTaskAction(iTaskDefinition, definition); - - // Create a security descriptor for the current user so that only the user - // (and Local System account) can see/access the registered task. - string startSddl = "D:P(A;;GA;;;SY)(A;;GA;;;BA)"; // DACL Allow Generic Access to System and BUILTIN\Administrators. - System.Security.Principal.SecurityIdentifier userSid = - System.Security.Principal.WindowsIdentity.GetCurrent().User; - CommonSecurityDescriptor SDesc = new CommonSecurityDescriptor(false, false, startSddl); - SDesc.DiscretionaryAcl.AddAccess(AccessControlType.Allow, userSid, 0x10000000, InheritanceFlags.None, PropagationFlags.None); - string sddl = SDesc.GetSddlForm(AccessControlSections.All); - - // Register this new task with the Task Scheduler. - if (definition.Credential == null) - { - // Register task to run as currently logged on user. - _iRootFolder.RegisterTaskDefinition( - definition.Name, - iTaskDefinition, - (int)_TASK_CREATION.TASK_CREATE, - null, // User name - null, // Password - _TASK_LOGON_TYPE.TASK_LOGON_S4U, - sddl); - } - else - { - // Register task to run under provided user account/credentials. - _iRootFolder.RegisterTaskDefinition( - definition.Name, - iTaskDefinition, - (int)_TASK_CREATION.TASK_CREATE, - definition.Credential.UserName, - GetCredentialPassword(definition.Credential), - _TASK_LOGON_TYPE.TASK_LOGON_PASSWORD, - sddl); - } - } - - /// - /// Removes the WTS task for this ScheduledJobDefinition. - /// Throws error if one or more instances of this task are running. - /// Force parameter will stop all running instances and remove task. - /// - /// ScheduledJobDefinition - /// Force running instances to stop and remove task - public void RemoveTask( - ScheduledJobDefinition definition, - bool force = false) - { - if (definition == null) - { - throw new PSArgumentNullException("definition"); - } - - RemoveTaskByName(definition.Name, force, false); - } - - /// - /// Removes a Task Scheduler task from the PowerShell/ScheduledJobs folder - /// based on a task name. - /// - /// Task Scheduler task name - /// Force running instances to stop and remove task - /// First check for existence of task - public void RemoveTaskByName( - string taskName, - bool force, - bool firstCheckForTask) - { - // Get registered task. - IRegisteredTask iRegisteredTask = null; - try - { - iRegisteredTask = _iRootFolder.GetTask(taskName); - } - catch (System.IO.DirectoryNotFoundException) - { - if (!firstCheckForTask) - { - throw; - } - } - catch (System.IO.FileNotFoundException) - { - if (!firstCheckForTask) - { - throw; - } - } - - if (iRegisteredTask == null) - { - return; - } - - // Check to see if any instances of this job/task is running. - IRunningTaskCollection iRunningTasks = iRegisteredTask.GetInstances(0); - if (iRunningTasks.Count > 0) - { - if (!force) - { - string msg = StringUtil.Format(ScheduledJobErrorStrings.CannotRemoveTaskRunningInstance, taskName); - throw new ScheduledJobException(msg); - } - - // Stop all running tasks. - iRegisteredTask.Stop(0); - } - - // Remove task. - _iRootFolder.DeleteTask(taskName, 0); - } - - /// - /// Starts task running from Task Scheduler. - /// - /// ScheduledJobDefinition - /// - /// - public void RunTask( - ScheduledJobDefinition definition) - { - // Get registered task. - IRegisteredTask iRegisteredTask = _iRootFolder.GetTask(definition.Name); - - // Run task. - iRegisteredTask.Run(null); - } - - /// - /// Updates an existing task in WTS with information from - /// ScheduledJobDefinition. - /// - /// ScheduledJobDefinition - public void UpdateTask( - ScheduledJobDefinition definition) - { - if (definition == null) - { - throw new PSArgumentNullException("definition"); - } - - // Get task to update. - ITaskDefinition iTaskDefinition = FindTask(definition.Name); - - // Replace options. - AddTaskOptions(iTaskDefinition, definition.Options); - - // Set enabled state. - iTaskDefinition.Settings.Enabled = definition.Enabled; - - // Replace triggers. - iTaskDefinition.Triggers.Clear(); - foreach (ScheduledJobTrigger jobTrigger in definition.JobTriggers) - { - AddTaskTrigger(iTaskDefinition, jobTrigger); - } - - // Replace action. - iTaskDefinition.Actions.Clear(); - AddTaskAction(iTaskDefinition, definition); - - // Register updated task. - if (definition.Credential == null) - { - // Register task to run as currently logged on user. - _iRootFolder.RegisterTaskDefinition( - definition.Name, - iTaskDefinition, - (int)_TASK_CREATION.TASK_UPDATE, - null, // User name - null, // Password - _TASK_LOGON_TYPE.TASK_LOGON_S4U, - null); - } - else - { - // Register task to run under provided user account/credentials. - _iRootFolder.RegisterTaskDefinition( - definition.Name, - iTaskDefinition, - (int)_TASK_CREATION.TASK_UPDATE, - definition.Credential.UserName, - GetCredentialPassword(definition.Credential), - _TASK_LOGON_TYPE.TASK_LOGON_PASSWORD, - null); - } - } - - #endregion - - #region Private Methods - - /// - /// Creates a new WTS trigger based on the provided ScheduledJobTrigger object - /// and adds it to the provided ITaskDefinition object. - /// - /// ITaskDefinition - /// ScheduledJobTrigger - private void AddTaskTrigger( - ITaskDefinition iTaskDefinition, - ScheduledJobTrigger jobTrigger) - { - ITrigger iTrigger = null; - - switch (jobTrigger.Frequency) - { - case TriggerFrequency.AtStartup: - { - iTrigger = iTaskDefinition.Triggers.Create(_TASK_TRIGGER_TYPE2.TASK_TRIGGER_BOOT); - IBootTrigger iBootTrigger = iTrigger as IBootTrigger; - Debug.Assert(iBootTrigger != null); - - iBootTrigger.Delay = ConvertTimeSpanToWTSString(jobTrigger.RandomDelay); - - iTrigger.Id = jobTrigger.Id.ToString(CultureInfo.InvariantCulture); - iTrigger.Enabled = jobTrigger.Enabled; - } - break; - - case TriggerFrequency.AtLogon: - { - iTrigger = iTaskDefinition.Triggers.Create(_TASK_TRIGGER_TYPE2.TASK_TRIGGER_LOGON); - ILogonTrigger iLogonTrigger = iTrigger as ILogonTrigger; - Debug.Assert(iLogonTrigger != null); - - iLogonTrigger.UserId = ScheduledJobTrigger.IsAllUsers(jobTrigger.User) ? null : jobTrigger.User; - iLogonTrigger.Delay = ConvertTimeSpanToWTSString(jobTrigger.RandomDelay); - - iTrigger.Id = jobTrigger.Id.ToString(CultureInfo.InvariantCulture); - iTrigger.Enabled = jobTrigger.Enabled; - } - break; - - case TriggerFrequency.Once: - { - iTrigger = iTaskDefinition.Triggers.Create(_TASK_TRIGGER_TYPE2.TASK_TRIGGER_TIME); - ITimeTrigger iTimeTrigger = iTrigger as ITimeTrigger; - Debug.Assert(iTimeTrigger != null); - - iTimeTrigger.RandomDelay = ConvertTimeSpanToWTSString(jobTrigger.RandomDelay); - - // Time trigger repetition. - if (jobTrigger.RepetitionInterval != null && - jobTrigger.RepetitionDuration != null) - { - iTimeTrigger.Repetition.Interval = ConvertTimeSpanToWTSString(jobTrigger.RepetitionInterval.Value); - if (jobTrigger.RepetitionDuration.Value == TimeSpan.MaxValue) - { - iTimeTrigger.Repetition.StopAtDurationEnd = false; - } - else - { - iTimeTrigger.Repetition.StopAtDurationEnd = true; - iTimeTrigger.Repetition.Duration = ConvertTimeSpanToWTSString(jobTrigger.RepetitionDuration.Value); - } - } - - iTrigger.StartBoundary = ConvertDateTimeToString(jobTrigger.At); - iTrigger.Id = jobTrigger.Id.ToString(CultureInfo.InvariantCulture); - iTrigger.Enabled = jobTrigger.Enabled; - } - break; - - case TriggerFrequency.Daily: - { - iTrigger = iTaskDefinition.Triggers.Create(_TASK_TRIGGER_TYPE2.TASK_TRIGGER_DAILY); - IDailyTrigger iDailyTrigger = iTrigger as IDailyTrigger; - Debug.Assert(iDailyTrigger != null); - - iDailyTrigger.RandomDelay = ConvertTimeSpanToWTSString(jobTrigger.RandomDelay); - iDailyTrigger.DaysInterval = (short)jobTrigger.Interval; - - iTrigger.StartBoundary = ConvertDateTimeToString(jobTrigger.At); - iTrigger.Id = jobTrigger.Id.ToString(CultureInfo.InvariantCulture); - iTrigger.Enabled = jobTrigger.Enabled; - } - break; - - case TriggerFrequency.Weekly: - { - iTrigger = iTaskDefinition.Triggers.Create(_TASK_TRIGGER_TYPE2.TASK_TRIGGER_WEEKLY); - IWeeklyTrigger iWeeklyTrigger = iTrigger as IWeeklyTrigger; - Debug.Assert(iWeeklyTrigger != null); - - iWeeklyTrigger.RandomDelay = ConvertTimeSpanToWTSString(jobTrigger.RandomDelay); - iWeeklyTrigger.WeeksInterval = (short)jobTrigger.Interval; - iWeeklyTrigger.DaysOfWeek = ConvertDaysOfWeekToMask(jobTrigger.DaysOfWeek); - - iTrigger.StartBoundary = ConvertDateTimeToString(jobTrigger.At); - iTrigger.Id = jobTrigger.Id.ToString(CultureInfo.InvariantCulture); - iTrigger.Enabled = jobTrigger.Enabled; - } - break; - } - } - - /// - /// Creates a ScheduledJobTrigger object based on a provided WTS ITrigger. - /// - /// ITrigger - /// ScheduledJobTrigger - private ScheduledJobTrigger CreateJobTrigger( - ITrigger iTrigger) - { - ScheduledJobTrigger rtnJobTrigger = null; - - if (iTrigger is IBootTrigger) - { - IBootTrigger iBootTrigger = (IBootTrigger)iTrigger; - rtnJobTrigger = ScheduledJobTrigger.CreateAtStartupTrigger( - ParseWTSTime(iBootTrigger.Delay), - ConvertStringId(iBootTrigger.Id), - iBootTrigger.Enabled); - } - else if (iTrigger is ILogonTrigger) - { - ILogonTrigger iLogonTrigger = (ILogonTrigger)iTrigger; - rtnJobTrigger = ScheduledJobTrigger.CreateAtLogOnTrigger( - iLogonTrigger.UserId, - ParseWTSTime(iLogonTrigger.Delay), - ConvertStringId(iLogonTrigger.Id), - iLogonTrigger.Enabled); - } - else if (iTrigger is ITimeTrigger) - { - ITimeTrigger iTimeTrigger = (ITimeTrigger)iTrigger; - TimeSpan repInterval = ParseWTSTime(iTimeTrigger.Repetition.Interval); - TimeSpan repDuration = (repInterval != TimeSpan.Zero && iTimeTrigger.Repetition.StopAtDurationEnd == false) ? - TimeSpan.MaxValue : ParseWTSTime(iTimeTrigger.Repetition.Duration); - rtnJobTrigger = ScheduledJobTrigger.CreateOnceTrigger( - DateTime.Parse(iTimeTrigger.StartBoundary, CultureInfo.InvariantCulture), - ParseWTSTime(iTimeTrigger.RandomDelay), - repInterval, - repDuration, - ConvertStringId(iTimeTrigger.Id), - iTimeTrigger.Enabled); - } - else if (iTrigger is IDailyTrigger) - { - IDailyTrigger iDailyTrigger = (IDailyTrigger)iTrigger; - rtnJobTrigger = ScheduledJobTrigger.CreateDailyTrigger( - DateTime.Parse(iDailyTrigger.StartBoundary, CultureInfo.InvariantCulture), - (Int32)iDailyTrigger.DaysInterval, - ParseWTSTime(iDailyTrigger.RandomDelay), - ConvertStringId(iDailyTrigger.Id), - iDailyTrigger.Enabled); - } - else if (iTrigger is IWeeklyTrigger) - { - IWeeklyTrigger iWeeklyTrigger = (IWeeklyTrigger)iTrigger; - rtnJobTrigger = ScheduledJobTrigger.CreateWeeklyTrigger( - DateTime.Parse(iWeeklyTrigger.StartBoundary, CultureInfo.InvariantCulture), - (Int32)iWeeklyTrigger.WeeksInterval, - ConvertMaskToDaysOfWeekArray(iWeeklyTrigger.DaysOfWeek), - ParseWTSTime(iWeeklyTrigger.RandomDelay), - ConvertStringId(iWeeklyTrigger.Id), - iWeeklyTrigger.Enabled); - } - - return rtnJobTrigger; - } - - private void AddTaskOptions( - ITaskDefinition iTaskDefinition, - ScheduledJobOptions jobOptions) - { - iTaskDefinition.Settings.DisallowStartIfOnBatteries = !jobOptions.StartIfOnBatteries; - iTaskDefinition.Settings.StopIfGoingOnBatteries = jobOptions.StopIfGoingOnBatteries; - iTaskDefinition.Settings.WakeToRun = jobOptions.WakeToRun; - iTaskDefinition.Settings.RunOnlyIfIdle = !jobOptions.StartIfNotIdle; - iTaskDefinition.Settings.IdleSettings.StopOnIdleEnd = jobOptions.StopIfGoingOffIdle; - iTaskDefinition.Settings.IdleSettings.RestartOnIdle = jobOptions.RestartOnIdleResume; - iTaskDefinition.Settings.IdleSettings.IdleDuration = ConvertTimeSpanToWTSString(jobOptions.IdleDuration); - iTaskDefinition.Settings.IdleSettings.WaitTimeout = ConvertTimeSpanToWTSString(jobOptions.IdleTimeout); - iTaskDefinition.Settings.Hidden = !jobOptions.ShowInTaskScheduler; - iTaskDefinition.Settings.RunOnlyIfNetworkAvailable = !jobOptions.RunWithoutNetwork; - iTaskDefinition.Settings.AllowDemandStart = !jobOptions.DoNotAllowDemandStart; - iTaskDefinition.Settings.MultipleInstances = ConvertFromMultiInstances(jobOptions.MultipleInstancePolicy); - iTaskDefinition.Principal.RunLevel = (jobOptions.RunElevated) ? - _TASK_RUNLEVEL.TASK_RUNLEVEL_HIGHEST : _TASK_RUNLEVEL.TASK_RUNLEVEL_LUA; - } - - private ScheduledJobOptions CreateJobOptions( - ITaskDefinition iTaskDefinition) - { - ITaskSettings iTaskSettings = iTaskDefinition.Settings; - IPrincipal iPrincipal = iTaskDefinition.Principal; - - return new ScheduledJobOptions( - !iTaskSettings.DisallowStartIfOnBatteries, - iTaskSettings.StopIfGoingOnBatteries, - iTaskSettings.WakeToRun, - !iTaskSettings.RunOnlyIfIdle, - iTaskSettings.IdleSettings.StopOnIdleEnd, - iTaskSettings.IdleSettings.RestartOnIdle, - ParseWTSTime(iTaskSettings.IdleSettings.IdleDuration), - ParseWTSTime(iTaskSettings.IdleSettings.WaitTimeout), - !iTaskSettings.Hidden, - iPrincipal.RunLevel == _TASK_RUNLEVEL.TASK_RUNLEVEL_HIGHEST, - !iTaskSettings.RunOnlyIfNetworkAvailable, - !iTaskSettings.AllowDemandStart, - ConvertToMultiInstances(iTaskSettings)); - } - - private void AddTaskAction( - ITaskDefinition iTaskDefinition, - ScheduledJobDefinition definition) - { - IExecAction iExecAction = iTaskDefinition.Actions.Create(_TASK_ACTION_TYPE.TASK_ACTION_EXEC) as IExecAction; - Debug.Assert(iExecAction != null); - - iExecAction.Id = ScheduledJobTaskActionId; - iExecAction.Path = definition.PSExecutionPath; - iExecAction.Arguments = definition.PSExecutionArgs; - } - - /// - /// Gets and returns the unsecured password for the provided - /// PSCredential object. - /// - /// PSCredential - /// Unsecured password string - private string GetCredentialPassword(PSCredential credential) - { - if (credential == null) - { - return null; - } - - IntPtr unmanagedString = IntPtr.Zero; - try - { - unmanagedString = Marshal.SecureStringToGlobalAllocUnicode(credential.Password); - return Marshal.PtrToStringUni(unmanagedString); - } - finally - { - Marshal.ZeroFreeGlobalAllocUnicode(unmanagedString); - } - } - - #endregion - - #region Private Utility Methods - - /// - /// Gets the Task Scheduler root folder for Scheduled Jobs or - /// creates it if it does not exist. - /// - /// Scheduled Jobs root folder. - private ITaskFolder GetRootFolder() - { - ITaskFolder iTaskRootFolder = null; - - try - { - iTaskRootFolder = _taskScheduler.GetFolder(ScheduledJobTasksRootFolder); - } - catch (System.IO.DirectoryNotFoundException) - { - } - catch (System.IO.FileNotFoundException) - { - // This can be thrown if COM interop tries to load the Microsoft.PowerShell.ScheduledJob - // assembly again. - } - - if (iTaskRootFolder == null) - { - // Create the PowerShell Scheduled Job root folder. - ITaskFolder iTSWindowsFolder = _taskScheduler.GetFolder(TaskSchedulerWindowsFolder); - iTaskRootFolder = iTSWindowsFolder.CreateFolder(ScheduledJobSubFolder); - } - - return iTaskRootFolder; - } - - /// - /// Finds a task with the provided Task Id and returns it as - /// a ITaskDefinition object. - /// - /// Task Id - /// ITaskDefinition - private ITaskDefinition FindTask(string taskId) - { - try - { - ITaskFolder iTaskFolder = _taskScheduler.GetFolder(ScheduledJobTasksRootFolder); - IRegisteredTask iRegisteredTask = iTaskFolder.GetTask(taskId); - return iRegisteredTask.Definition; - } - catch (System.IO.DirectoryNotFoundException e) - { - string msg = StringUtil.Format(ScheduledJobErrorStrings.CannotFindTaskId, taskId); - throw new ScheduledJobException(msg, e); - } - } - - private Int32 ConvertStringId(string triggerId) - { - Int32 triggerIdVal = 0; - - try - { - triggerIdVal = Convert.ToInt32(triggerId); - } - catch (FormatException) - { } - catch (OverflowException) - { } - - return triggerIdVal; - } - - /// - /// Helper method to parse a WTS time string and return - /// a corresponding TimeSpan object. Note that the - /// year and month values are ignored. - /// Format: - /// "PnYnMnDTnHnMnS" - /// "P" - Date separator - /// "nY" - year value. - /// "nM" - month value. - /// "nD" - day value. - /// "T" - Time separator - /// "nH" - hour value. - /// "nM" - minute value. - /// "nS" - second value. - /// - /// Formatted time string - /// TimeSpan - private TimeSpan ParseWTSTime(string wtsTime) - { - if (string.IsNullOrEmpty(wtsTime)) - { - return new TimeSpan(0); - } - - int days = 0; - int hours = 0; - int minutes = 0; - int seconds = 0; - int indx = 0; - int length = wtsTime.Length; - StringBuilder str = new StringBuilder(); - - try - { - while (indx != length) - { - char c = wtsTime[indx++]; - - switch (c) - { - case 'P': - str.Clear(); - while (indx != length && - wtsTime[indx] != 'T') - { - char c2 = wtsTime[indx++]; - if (c2 == 'Y') - { - // Ignore year value. - str.Clear(); - } - else if (c2 == 'M') - { - // Ignore month value. - str.Clear(); - } - else if (c2 == 'D') - { - days = Convert.ToInt32(str.ToString(), CultureInfo.InvariantCulture); - str.Clear(); - } - else if (c2 >= '0' && c2 <= '9') - { - str.Append(c2); - } - } - break; - - case 'T': - str.Clear(); - while (indx != length && - wtsTime[indx] != 'P') - { - char c2 = wtsTime[indx++]; - if (c2 == 'H') - { - hours = Convert.ToInt32(str.ToString(), CultureInfo.InvariantCulture); - str.Clear(); - } - else if (c2 == 'M') - { - minutes = Convert.ToInt32(str.ToString(), CultureInfo.InvariantCulture); - str.Clear(); - } - else if (c2 == 'S') - { - seconds = Convert.ToInt32(str.ToString(), CultureInfo.InvariantCulture); - str.Clear(); - } - else if (c2 >= '0' && c2 <= '9') - { - str.Append(c2); - } - } - break; - } - } - } - catch (FormatException) - { } - catch (OverflowException) - { } - - return new TimeSpan(days, hours, minutes, seconds); - } - - /// - /// Creates WTS formatted time string based on TimeSpan parameter. - /// - /// TimeSpan - /// WTS time string - internal static string ConvertTimeSpanToWTSString(TimeSpan time) - { - return string.Format( - CultureInfo.InvariantCulture, - "P{0}DT{1}H{2}M{3}S", - time.Days, - time.Hours, - time.Minutes, - time.Seconds); - } - - /// - /// Converts DateTime to string for WTS. - /// - /// DateTime - /// DateTime string - internal static string ConvertDateTimeToString(DateTime? dt) - { - if (dt == null) - { - return string.Empty; - } - else - { - return dt.Value.ToString("s", CultureInfo.InvariantCulture); - } - } - - /// - /// Returns a bitmask representing days of week as - /// required by Windows Task Scheduler API. - /// - /// Array of DayOfWeek - /// WTS days of week mask - internal static short ConvertDaysOfWeekToMask(IEnumerable daysOfWeek) - { - short rtnValue = 0; - foreach (DayOfWeek day in daysOfWeek) - { - switch (day) - { - case DayOfWeek.Sunday: - rtnValue |= WTSSunday; - break; - - case DayOfWeek.Monday: - rtnValue |= WTSMonday; - break; - - case DayOfWeek.Tuesday: - rtnValue |= WTSTuesday; - break; - - case DayOfWeek.Wednesday: - rtnValue |= WTSWednesday; - break; - - case DayOfWeek.Thursday: - rtnValue |= WTSThursday; - break; - - case DayOfWeek.Friday: - rtnValue |= WTSFriday; - break; - - case DayOfWeek.Saturday: - rtnValue |= WTSSaturday; - break; - } - } - - return rtnValue; - } - - /// - /// Converts WTS days of week mask to an array of DayOfWeek type. - /// - /// WTS days of week mask - /// Days of week as List - private List ConvertMaskToDaysOfWeekArray(short mask) - { - List daysOfWeek = new List(); - - if ((mask & WTSSunday) != 0) { daysOfWeek.Add(DayOfWeek.Sunday); } - if ((mask & WTSMonday) != 0) { daysOfWeek.Add(DayOfWeek.Monday); } - if ((mask & WTSTuesday) != 0) { daysOfWeek.Add(DayOfWeek.Tuesday); } - if ((mask & WTSWednesday) != 0) { daysOfWeek.Add(DayOfWeek.Wednesday); } - if ((mask & WTSThursday) != 0) { daysOfWeek.Add(DayOfWeek.Thursday); } - if ((mask & WTSFriday) != 0) { daysOfWeek.Add(DayOfWeek.Friday); } - if ((mask & WTSSaturday) != 0) { daysOfWeek.Add(DayOfWeek.Saturday); } - - return daysOfWeek; - } - - private TaskMultipleInstancePolicy ConvertToMultiInstances( - ITaskSettings iTaskSettings) - { - switch (iTaskSettings.MultipleInstances) - { - case _TASK_INSTANCES_POLICY.TASK_INSTANCES_IGNORE_NEW: - return TaskMultipleInstancePolicy.IgnoreNew; - - case _TASK_INSTANCES_POLICY.TASK_INSTANCES_PARALLEL: - return TaskMultipleInstancePolicy.Parallel; - - case _TASK_INSTANCES_POLICY.TASK_INSTANCES_QUEUE: - return TaskMultipleInstancePolicy.Queue; - - case _TASK_INSTANCES_POLICY.TASK_INSTANCES_STOP_EXISTING: - return TaskMultipleInstancePolicy.StopExisting; - } - - Debug.Assert(false); - return TaskMultipleInstancePolicy.None; - } - - private _TASK_INSTANCES_POLICY ConvertFromMultiInstances( - TaskMultipleInstancePolicy jobPolicies) - { - switch (jobPolicies) - { - case TaskMultipleInstancePolicy.IgnoreNew: - return _TASK_INSTANCES_POLICY.TASK_INSTANCES_IGNORE_NEW; - - case TaskMultipleInstancePolicy.Parallel: - return _TASK_INSTANCES_POLICY.TASK_INSTANCES_PARALLEL; - - case TaskMultipleInstancePolicy.Queue: - return _TASK_INSTANCES_POLICY.TASK_INSTANCES_QUEUE; - - case TaskMultipleInstancePolicy.StopExisting: - return _TASK_INSTANCES_POLICY.TASK_INSTANCES_STOP_EXISTING; - - default: - return _TASK_INSTANCES_POLICY.TASK_INSTANCES_IGNORE_NEW; - } - } - - #endregion - - #region IDisposable - - /// - /// Dispose. - /// - public void Dispose() - { - // Release reference to Task Scheduler object so that the COM - // object can be released. - _iRootFolder = null; - _taskScheduler = null; - - GC.SuppressFinalize(this); - } - - #endregion - } -} diff --git a/src/Microsoft.PowerShell.ScheduledJob/commands/AddJobTrigger.cs b/src/Microsoft.PowerShell.ScheduledJob/commands/AddJobTrigger.cs deleted file mode 100644 index dee94eeb212..00000000000 --- a/src/Microsoft.PowerShell.ScheduledJob/commands/AddJobTrigger.cs +++ /dev/null @@ -1,139 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Diagnostics.CodeAnalysis; -using System.Management.Automation; -using System.Management.Automation.Internal; -using System.Management.Automation.Host; -using System.Threading; -using System.Diagnostics; - -namespace Microsoft.PowerShell.ScheduledJob -{ - /// - /// This cmdlet adds ScheduledJobTriggers to ScheduledJobDefinition objects. - /// - [Cmdlet(VerbsCommon.Add, "JobTrigger", DefaultParameterSetName = AddJobTriggerCommand.JobDefinitionParameterSet, - HelpUri = "https://go.microsoft.com/fwlink/?LinkID=223913")] - public sealed class AddJobTriggerCommand : ScheduleJobCmdletBase - { - #region Parameters - - private const string JobDefinitionParameterSet = "JobDefinition"; - private const string JobDefinitionIdParameterSet = "JobDefinitionId"; - private const string JobDefinitionNameParameterSet = "JobDefinitionName"; - - /// - /// ScheduledJobTrigger. - /// - [Parameter(Position = 1, Mandatory = true, ValueFromPipeline = true, - ParameterSetName = AddJobTriggerCommand.JobDefinitionParameterSet)] - [Parameter(Position = 1, Mandatory = true, ValueFromPipeline = true, - ParameterSetName = AddJobTriggerCommand.JobDefinitionIdParameterSet)] - [Parameter(Position = 1, Mandatory = true, ValueFromPipeline = true, - ParameterSetName = AddJobTriggerCommand.JobDefinitionNameParameterSet)] - [ValidateNotNullOrEmpty] - [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] - public ScheduledJobTrigger[] Trigger - { - get { return _triggers; } - set { _triggers = value; } - } - private ScheduledJobTrigger[] _triggers; - - /// - /// ScheduledJobDefinition Id. - /// - [Parameter(Position = 0, Mandatory = true, - ParameterSetName = AddJobTriggerCommand.JobDefinitionIdParameterSet)] - [ValidateNotNullOrEmpty] - [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] - public Int32[] Id - { - get { return _ids; } - set { _ids = value; } - } - private Int32[] _ids; - - /// - /// ScheduledJobDefinition Name. - /// - [Parameter(Position = 0, Mandatory = true, - ParameterSetName = AddJobTriggerCommand.JobDefinitionNameParameterSet)] - [ValidateNotNullOrEmpty] - [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] - public string[] Name - { - get { return _names; } - set { _names = value; } - } - private string[] _names; - - /// - /// ScheduledJobDefinition. - /// - [Parameter(Position = 0, Mandatory = true, ValueFromPipeline = true, - ParameterSetName = AddJobTriggerCommand.JobDefinitionParameterSet)] - [ValidateNotNullOrEmpty] - [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] - public ScheduledJobDefinition[] InputObject - { - get { return _definitions; } - set { _definitions = value; } - } - private ScheduledJobDefinition[] _definitions; - - #endregion - - #region Cmdlet Overrides - - /// - /// Process input. - /// - protected override void ProcessRecord() - { - switch (ParameterSetName) - { - case JobDefinitionParameterSet: - AddToJobDefinition(_definitions); - break; - - case JobDefinitionIdParameterSet: - AddToJobDefinition(GetJobDefinitionsById(_ids)); - break; - - case JobDefinitionNameParameterSet: - AddToJobDefinition(GetJobDefinitionsByName(_names)); - break; - } - } - - #endregion - - #region Private Methods - - private void AddToJobDefinition(IEnumerable jobDefinitions) - { - foreach (ScheduledJobDefinition definition in jobDefinitions) - { - try - { - definition.AddTriggers(_triggers, true); - } - catch (ScheduledJobException e) - { - string msg = StringUtil.Format(ScheduledJobErrorStrings.CantAddJobTriggersToDefinition, definition.Name); - Exception reason = new RuntimeException(msg, e); - ErrorRecord errorRecord = new ErrorRecord(reason, "CantAddJobTriggersToScheduledJobDefinition", ErrorCategory.InvalidOperation, definition); - WriteError(errorRecord); - } - } - } - - #endregion - } -} diff --git a/src/Microsoft.PowerShell.ScheduledJob/commands/DisableJobDefinition.cs b/src/Microsoft.PowerShell.ScheduledJob/commands/DisableJobDefinition.cs deleted file mode 100644 index 0c8f97fe19c..00000000000 --- a/src/Microsoft.PowerShell.ScheduledJob/commands/DisableJobDefinition.cs +++ /dev/null @@ -1,32 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -using System; -using System.Collections.Generic; -using System.Management.Automation; - -namespace Microsoft.PowerShell.ScheduledJob -{ - /// - /// This cmdlet disables the specified ScheduledJobDefinition. - /// - [Cmdlet(VerbsLifecycle.Disable, "ScheduledJob", SupportsShouldProcess = true, DefaultParameterSetName = DisableScheduledJobDefinitionBase.DefinitionParameterSet, - HelpUri = "https://go.microsoft.com/fwlink/?LinkID=223927")] - [OutputType(typeof(ScheduledJobDefinition))] - public sealed class DisableScheduledJobCommand : DisableScheduledJobDefinitionBase - { - #region Properties - - /// - /// Returns true if scheduled job definition should be enabled, - /// false otherwise. - /// - protected override bool Enabled - { - get { return false; } - } - - #endregion - } -} diff --git a/src/Microsoft.PowerShell.ScheduledJob/commands/DisableJobDefinitionBase.cs b/src/Microsoft.PowerShell.ScheduledJob/commands/DisableJobDefinitionBase.cs deleted file mode 100644 index 0fb83a2a642..00000000000 --- a/src/Microsoft.PowerShell.ScheduledJob/commands/DisableJobDefinitionBase.cs +++ /dev/null @@ -1,149 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -using System; -using System.Collections.Generic; -using System.Management.Automation; - -namespace Microsoft.PowerShell.ScheduledJob -{ - /// - /// Base class for the DisableScheduledJobCommand, EnableScheduledJobCommand cmdlets. - /// - public abstract class DisableScheduledJobDefinitionBase : ScheduleJobCmdletBase - { - #region Parameters - - /// - /// DefinitionIdParameterSet - /// - protected const string DefinitionIdParameterSet = "DefinitionId"; - - /// - /// DefinitionNameParameterSet - /// - protected const string DefinitionNameParameterSet = "DefinitionName"; - - /// - /// DefinitionParameterSet - /// - protected const string DefinitionParameterSet = "Definition"; - - /// - /// ScheduledJobDefinition. - /// - [Parameter(Position = 0, Mandatory = true, ValueFromPipeline = true, - ParameterSetName = DisableScheduledJobDefinitionBase.DefinitionParameterSet)] - [ValidateNotNull] - public ScheduledJobDefinition InputObject - { - get { return _definition; } - set { _definition = value; } - } - private ScheduledJobDefinition _definition; - - /// - /// ScheduledJobDefinition Id. - /// - [Parameter(Position = 0, Mandatory = true, - ParameterSetName = DisableScheduledJobDefinitionBase.DefinitionIdParameterSet)] - public Int32 Id - { - get { return _definitionId; } - set { _definitionId = value; } - } - private Int32 _definitionId; - - /// - /// ScheduledJobDefinition Name. - /// - [Parameter(Position = 0, Mandatory = true, - ParameterSetName = DisableScheduledJobDefinitionBase.DefinitionNameParameterSet)] - [ValidateNotNullOrEmpty] - public string Name - { - get { return _definitionName; } - set { _definitionName = value; } - } - private string _definitionName; - - /// - /// Pass through ScheduledJobDefinition object. - /// - [Parameter(ParameterSetName = DisableScheduledJobDefinitionBase.DefinitionParameterSet)] - [Parameter(ParameterSetName = DisableScheduledJobDefinitionBase.DefinitionIdParameterSet)] - [Parameter(ParameterSetName = DisableScheduledJobDefinitionBase.DefinitionNameParameterSet)] - public SwitchParameter PassThru - { - get { return _passThru; } - set { _passThru = value; } - } - private SwitchParameter _passThru; - - #endregion - - #region Cmdlet Overrides - - /// - /// Process input. - /// - protected override void ProcessRecord() - { - ScheduledJobDefinition definition = null; - - switch (ParameterSetName) - { - case DefinitionParameterSet: - definition = _definition; - break; - - case DefinitionIdParameterSet: - definition = GetJobDefinitionById(_definitionId); - break; - - case DefinitionNameParameterSet: - definition = GetJobDefinitionByName(_definitionName); - break; - } - - string verbName = Enabled ? VerbsLifecycle.Enable : VerbsLifecycle.Disable; - - if (definition != null && - ShouldProcess(definition.Name, verbName)) - { - try - { - definition.SetEnabled(Enabled, true); - } - catch (ScheduledJobException e) - { - string msg = StringUtil.Format(ScheduledJobErrorStrings.CantSetEnableOnJobDefinition, definition.Name); - Exception reason = new RuntimeException(msg, e); - ErrorRecord errorRecord = new ErrorRecord(reason, "CantSetEnableOnScheduledJobDefinition", ErrorCategory.InvalidOperation, definition); - WriteError(errorRecord); - } - - if (_passThru) - { - WriteObject(definition); - } - } - } - - #endregion - - #region Properties - - /// - /// Returns true if scheduled job definition should be enabled, - /// false otherwise. - /// - protected abstract bool Enabled - { - get; - } - - #endregion - } -} diff --git a/src/Microsoft.PowerShell.ScheduledJob/commands/DisableJobTrigger.cs b/src/Microsoft.PowerShell.ScheduledJob/commands/DisableJobTrigger.cs deleted file mode 100644 index 2c80beb7cdb..00000000000 --- a/src/Microsoft.PowerShell.ScheduledJob/commands/DisableJobTrigger.cs +++ /dev/null @@ -1,36 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Diagnostics.CodeAnalysis; -using System.Management.Automation; -using System.Management.Automation.Internal; -using System.Management.Automation.Host; -using System.Threading; -using System.Diagnostics; - -namespace Microsoft.PowerShell.ScheduledJob -{ - /// - /// This cmdlet enables triggers on a ScheduledJobDefinition object. - /// - [Cmdlet(VerbsLifecycle.Disable, "JobTrigger", SupportsShouldProcess = true, DefaultParameterSetName = DisableJobTriggerCommand.EnabledParameterSet, - HelpUri = "https://go.microsoft.com/fwlink/?LinkID=223918")] - public sealed class DisableJobTriggerCommand : EnableDisableScheduledJobCmdletBase - { - #region Enabled Implementation - - /// - /// Property to determine if trigger should be enabled or disabled. - /// - internal override bool Enabled - { - get { return false; } - } - - #endregion - } -} diff --git a/src/Microsoft.PowerShell.ScheduledJob/commands/EnableDisableCmdletBase.cs b/src/Microsoft.PowerShell.ScheduledJob/commands/EnableDisableCmdletBase.cs deleted file mode 100644 index 2d4f6a51a61..00000000000 --- a/src/Microsoft.PowerShell.ScheduledJob/commands/EnableDisableCmdletBase.cs +++ /dev/null @@ -1,94 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Management.Automation; -using System.Management.Automation.Internal; -using System.Management.Automation.Host; -using System.Threading; -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; - -namespace Microsoft.PowerShell.ScheduledJob -{ - /// - /// Base class for DisableJobTrigger, EnableJobTrigger cmdlets. - /// - public abstract class EnableDisableScheduledJobCmdletBase : ScheduleJobCmdletBase - { - #region Parameters - - /// - /// JobDefinition parameter set. - /// - protected const string EnabledParameterSet = "JobEnabled"; - - /// - /// ScheduledJobTrigger objects to set properties on. - /// - [Parameter(Position = 0, Mandatory = true, ValueFromPipeline = true, - ParameterSetName = EnableDisableScheduledJobCmdletBase.EnabledParameterSet)] - [ValidateNotNullOrEmpty] - [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] - public ScheduledJobTrigger[] InputObject - { - get { return _triggers; } - set { _triggers = value; } - } - - /// - /// Pass through for scheduledjobtrigger object. - /// - [Parameter(ParameterSetName = EnableDisableScheduledJobCmdletBase.EnabledParameterSet)] - public SwitchParameter PassThru - { - get { return _passThru; } - set { _passThru = value; } - } - private SwitchParameter _passThru; - - private ScheduledJobTrigger[] _triggers; - - #endregion - - #region Cmdlet Overrides - - /// - /// Process input. - /// - protected override void ProcessRecord() - { - // Update each trigger with the current enabled state. - foreach (ScheduledJobTrigger trigger in _triggers) - { - trigger.Enabled = Enabled; - if (trigger.JobDefinition != null) - { - trigger.UpdateJobDefinition(); - } - - if (_passThru) - { - WriteObject(trigger); - } - } - } - - #endregion - - #region Internal Properties - - /// - /// Property to determine if trigger should be enabled or disabled. - /// - internal abstract bool Enabled - { - get; - } - - #endregion - } -} diff --git a/src/Microsoft.PowerShell.ScheduledJob/commands/EnableJobDefinition.cs b/src/Microsoft.PowerShell.ScheduledJob/commands/EnableJobDefinition.cs deleted file mode 100644 index 4bcd6eedb7c..00000000000 --- a/src/Microsoft.PowerShell.ScheduledJob/commands/EnableJobDefinition.cs +++ /dev/null @@ -1,32 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -using System; -using System.Collections.Generic; -using System.Management.Automation; - -namespace Microsoft.PowerShell.ScheduledJob -{ - /// - /// This cmdlet enables the specified ScheduledJobDefinition. - /// - [Cmdlet(VerbsLifecycle.Enable, "ScheduledJob", SupportsShouldProcess = true, DefaultParameterSetName = DisableScheduledJobDefinitionBase.DefinitionParameterSet, - HelpUri = "https://go.microsoft.com/fwlink/?LinkID=223926")] - [OutputType(typeof(ScheduledJobDefinition))] - public sealed class EnableScheduledJobCommand : DisableScheduledJobDefinitionBase - { - #region Properties - - /// - /// Returns true if scheduled job definition should be enabled, - /// false otherwise. - /// - protected override bool Enabled - { - get { return true; } - } - - #endregion - } -} diff --git a/src/Microsoft.PowerShell.ScheduledJob/commands/EnableJobTrigger.cs b/src/Microsoft.PowerShell.ScheduledJob/commands/EnableJobTrigger.cs deleted file mode 100644 index 023591ad4f1..00000000000 --- a/src/Microsoft.PowerShell.ScheduledJob/commands/EnableJobTrigger.cs +++ /dev/null @@ -1,36 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Diagnostics.CodeAnalysis; -using System.Management.Automation; -using System.Management.Automation.Internal; -using System.Management.Automation.Host; -using System.Threading; -using System.Diagnostics; - -namespace Microsoft.PowerShell.ScheduledJob -{ - /// - /// This cmdlet disables triggers on a ScheduledJobDefinition object. - /// - [Cmdlet(VerbsLifecycle.Enable, "JobTrigger", SupportsShouldProcess = true, DefaultParameterSetName = EnableJobTriggerCommand.EnabledParameterSet, - HelpUri = "https://go.microsoft.com/fwlink/?LinkID=223917")] - public sealed class EnableJobTriggerCommand : EnableDisableScheduledJobCmdletBase - { - #region Enabled Implementation - - /// - /// Property to determine if trigger should be enabled or disabled. - /// - internal override bool Enabled - { - get { return true; } - } - - #endregion - } -} diff --git a/src/Microsoft.PowerShell.ScheduledJob/commands/GetJobDefinition.cs b/src/Microsoft.PowerShell.ScheduledJob/commands/GetJobDefinition.cs deleted file mode 100644 index e138a570475..00000000000 --- a/src/Microsoft.PowerShell.ScheduledJob/commands/GetJobDefinition.cs +++ /dev/null @@ -1,96 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -using System; -using System.Collections.Generic; -using System.Management.Automation; -using System.Diagnostics.CodeAnalysis; - -namespace Microsoft.PowerShell.ScheduledJob -{ - /// - /// This cmdlet gets scheduled job definition objects from the local repository. - /// - [Cmdlet(VerbsCommon.Get, "ScheduledJob", DefaultParameterSetName = GetScheduledJobCommand.DefinitionIdParameterSet, - HelpUri = "https://go.microsoft.com/fwlink/?LinkID=223923")] - [OutputType(typeof(ScheduledJobDefinition))] - public sealed class GetScheduledJobCommand : ScheduleJobCmdletBase - { - #region Parameters - - private const string DefinitionIdParameterSet = "DefinitionId"; - private const string DefinitionNameParameterSet = "DefinitionName"; - - /// - /// ScheduledJobDefinition Id. - /// - [Parameter(Position = 0, - ParameterSetName = GetScheduledJobCommand.DefinitionIdParameterSet)] - [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] - public Int32[] Id - { - get { return _definitionIds; } - set { _definitionIds = value; } - } - private Int32[] _definitionIds; - - /// - /// ScheduledJobDefinition Name. - /// - [Parameter(Position = 0, Mandatory = true, - ParameterSetName = GetScheduledJobCommand.DefinitionNameParameterSet)] - [ValidateNotNullOrEmpty] - [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] - public string[] Name - { - get { return _definitionNames; } - set { _definitionNames = value; } - } - private string[] _definitionNames; - - #endregion - - #region Cmdlet Overrides - - /// - /// Process input. - /// - protected override void ProcessRecord() - { - switch (ParameterSetName) - { - case DefinitionIdParameterSet: - if (_definitionIds == null) - { - FindAllJobDefinitions( - (definition) => - { - WriteObject(definition); - }); - } - else - { - FindJobDefinitionsById( - _definitionIds, - (definition) => - { - WriteObject(definition); - }); - } - break; - - case DefinitionNameParameterSet: - FindJobDefinitionsByName( - _definitionNames, - (definition) => - { - WriteObject(definition); - }); - break; - } - } - - #endregion - } -} diff --git a/src/Microsoft.PowerShell.ScheduledJob/commands/GetJobTrigger.cs b/src/Microsoft.PowerShell.ScheduledJob/commands/GetJobTrigger.cs deleted file mode 100644 index ab1b05e9494..00000000000 --- a/src/Microsoft.PowerShell.ScheduledJob/commands/GetJobTrigger.cs +++ /dev/null @@ -1,140 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Diagnostics.CodeAnalysis; -using System.Management.Automation; -using System.Management.Automation.Internal; -using System.Management.Automation.Host; -using System.Threading; -using System.Diagnostics; - -namespace Microsoft.PowerShell.ScheduledJob -{ - /// - /// This cmdlet gets ScheduledJobTriggers for the specified ScheduledJobDefinition object. - /// - [Cmdlet(VerbsCommon.Get, "JobTrigger", DefaultParameterSetName = GetJobTriggerCommand.JobDefinitionParameterSet, - HelpUri = "https://go.microsoft.com/fwlink/?LinkID=223915")] - [OutputType(typeof(ScheduledJobTrigger))] - public sealed class GetJobTriggerCommand : ScheduleJobCmdletBase - { - #region Parameters - - private const string JobDefinitionParameterSet = "JobDefinition"; - private const string JobDefinitionIdParameterSet = "JobDefinitionId"; - private const string JobDefinitionNameParameterSet = "JobDefinitionName"; - - /// - /// Trigger number to get. - /// - [Parameter(Position = 1, - ParameterSetName = GetJobTriggerCommand.JobDefinitionParameterSet)] - [Parameter(Position = 1, - ParameterSetName = GetJobTriggerCommand.JobDefinitionIdParameterSet)] - [Parameter(Position = 1, - ParameterSetName = GetJobTriggerCommand.JobDefinitionNameParameterSet)] - [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] - public Int32[] TriggerId - { - get { return _triggerIds; } - set { _triggerIds = value; } - } - private Int32[] _triggerIds; - - /// - /// ScheduledJobDefinition. - /// - [Parameter(Position = 0, Mandatory = true, ValueFromPipeline = true, - ParameterSetName = GetJobTriggerCommand.JobDefinitionParameterSet)] - [ValidateNotNull] - public ScheduledJobDefinition InputObject - { - get { return _definition; } - set { _definition = value; } - } - private ScheduledJobDefinition _definition; - - /// - /// ScheduledJobDefinition Id. - /// - [Parameter(Position = 0, Mandatory = true, - ParameterSetName = GetJobTriggerCommand.JobDefinitionIdParameterSet)] - public Int32 Id - { - get { return _definitionId; } - set { _definitionId = value; } - } - private Int32 _definitionId; - - /// - /// ScheduledJobDefinition Name. - /// - [Parameter(Position = 0, Mandatory = true, - ParameterSetName = GetJobTriggerCommand.JobDefinitionNameParameterSet)] - [ValidateNotNullOrEmpty] - public string Name - { - get { return _name; } - set { _name = value; } - } - private string _name; - - #endregion - - #region Cmdlet Overrides - - /// - /// Process input. - /// - protected override void ProcessRecord() - { - switch (ParameterSetName) - { - case JobDefinitionParameterSet: - WriteTriggers(_definition); - break; - - case JobDefinitionIdParameterSet: - WriteTriggers(GetJobDefinitionById(_definitionId)); - break; - - case JobDefinitionNameParameterSet: - WriteTriggers(GetJobDefinitionByName(_name)); - break; - } - } - - #endregion - - #region Private Methods - - private void WriteTriggers(ScheduledJobDefinition definition) - { - if (definition == null) - { - return; - } - - List notFoundIds; - List triggers = definition.GetTriggers(_triggerIds, out notFoundIds); - - // Write found trigger objects. - foreach (ScheduledJobTrigger trigger in triggers) - { - WriteObject(trigger); - } - - // Report any triggers that were not found. - foreach (Int32 notFoundId in notFoundIds) - { - WriteTriggerNotFoundError(notFoundId, definition.Name, definition); - } - } - - #endregion - } -} diff --git a/src/Microsoft.PowerShell.ScheduledJob/commands/GetScheduledJobOption.cs b/src/Microsoft.PowerShell.ScheduledJob/commands/GetScheduledJobOption.cs deleted file mode 100644 index 3f8b3a27bc8..00000000000 --- a/src/Microsoft.PowerShell.ScheduledJob/commands/GetScheduledJobOption.cs +++ /dev/null @@ -1,98 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -using System; -using System.Collections.Generic; -using System.Management.Automation; - -namespace Microsoft.PowerShell.ScheduledJob -{ - /// - /// This cmdlet gets scheduled job option object from a provided ScheduledJobDefinition object. - /// - [Cmdlet(VerbsCommon.Get, "ScheduledJobOption", DefaultParameterSetName = GetScheduledJobOptionCommand.JobDefinitionParameterSet, - HelpUri = "https://go.microsoft.com/fwlink/?LinkID=223920")] - [OutputType(typeof(ScheduledJobOptions))] - public sealed class GetScheduledJobOptionCommand : ScheduleJobCmdletBase - { - #region Parameters - - private const string JobDefinitionParameterSet = "JobDefinition"; - private const string JobDefinitionIdParameterSet = "JobDefinitionId"; - private const string JobDefinitionNameParameterSet = "JobDefinitionName"; - - /// - /// ScheduledJobDefinition Id. - /// - [Parameter(Position = 0, Mandatory = true, - ParameterSetName = GetScheduledJobOptionCommand.JobDefinitionIdParameterSet)] - public Int32 Id - { - get { return _id; } - set { _id = value; } - } - private Int32 _id; - - /// - /// ScheduledJobDefinition Name. - /// - [Parameter(Position = 0, Mandatory = true, ValueFromPipelineByPropertyName = true, - ParameterSetName = GetScheduledJobOptionCommand.JobDefinitionNameParameterSet)] - [ValidateNotNullOrEmpty] - public string Name - { - get { return _name; } - set { _name = value; } - } - private string _name; - - /// - /// ScheduledJobDefinition. - /// - [Parameter(Position = 0, Mandatory = true, ValueFromPipeline = true, - ParameterSetName = GetScheduledJobOptionCommand.JobDefinitionParameterSet)] - [ValidateNotNull] - public ScheduledJobDefinition InputObject - { - get { return _definition; } - set { _definition = value; } - } - private ScheduledJobDefinition _definition; - - #endregion - - #region Cmdlet Overrides - - /// - /// Process input. - /// - protected override void ProcessRecord() - { - // Get ScheduledJobDefinition object. - ScheduledJobDefinition definition = null; - switch (ParameterSetName) - { - case JobDefinitionParameterSet: - definition = _definition; - break; - - case JobDefinitionIdParameterSet: - definition = GetJobDefinitionById(_id); - break; - - case JobDefinitionNameParameterSet: - definition = GetJobDefinitionByName(_name); - break; - } - - // Return options from the definition object. - if (definition != null) - { - WriteObject(definition.Options); - } - } - - #endregion - } -} diff --git a/src/Microsoft.PowerShell.ScheduledJob/commands/NewJobTrigger.cs b/src/Microsoft.PowerShell.ScheduledJob/commands/NewJobTrigger.cs deleted file mode 100644 index e62daaf3662..00000000000 --- a/src/Microsoft.PowerShell.ScheduledJob/commands/NewJobTrigger.cs +++ /dev/null @@ -1,327 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Diagnostics.CodeAnalysis; -using System.Management.Automation; -using System.Management.Automation.Internal; -using System.Management.Automation.Host; -using System.Threading; -using System.Diagnostics; - -namespace Microsoft.PowerShell.ScheduledJob -{ - /// - /// This cmdlet creates a new scheduled job trigger based on the provided - /// parameter values. - /// - [Cmdlet(VerbsCommon.New, "JobTrigger", DefaultParameterSetName = NewJobTriggerCommand.OnceParameterSet, - HelpUri = "https://go.microsoft.com/fwlink/?LinkID=223912")] - [OutputType(typeof(ScheduledJobTrigger))] - public sealed class NewJobTriggerCommand : ScheduleJobCmdletBase - { - #region Parameters - - private const string AtLogonParameterSet = "AtLogon"; - private const string AtStartupParameterSet = "AtStartup"; - private const string OnceParameterSet = "Once"; - private const string DailyParameterSet = "Daily"; - private const string WeeklyParameterSet = "Weekly"; - - /// - /// Daily interval for trigger. - /// - [Parameter(ParameterSetName = NewJobTriggerCommand.DailyParameterSet)] - public Int32 DaysInterval - { - get { return _daysInterval; } - set { _daysInterval = value; } - } - private Int32 _daysInterval = 1; - - /// - /// Weekly interval for trigger. - /// - [Parameter(ParameterSetName = NewJobTriggerCommand.WeeklyParameterSet)] - public Int32 WeeksInterval - { - get { return _weeksInterval; } - set { _weeksInterval = value; } - } - private Int32 _weeksInterval = 1; - - /// - /// Random delay for trigger. - /// - [Parameter(ParameterSetName = NewJobTriggerCommand.AtLogonParameterSet)] - [Parameter(ParameterSetName = NewJobTriggerCommand.AtStartupParameterSet)] - [Parameter(ParameterSetName = NewJobTriggerCommand.OnceParameterSet)] - [Parameter(ParameterSetName = NewJobTriggerCommand.DailyParameterSet)] - [Parameter(ParameterSetName = NewJobTriggerCommand.WeeklyParameterSet)] - public TimeSpan RandomDelay - { - get { return _randomDelay; } - set { _randomDelay = value; } - } - private TimeSpan _randomDelay; - - /// - /// Job start date/time for trigger. - /// - [Parameter(Mandatory = true, - ParameterSetName = NewJobTriggerCommand.OnceParameterSet)] - [Parameter(Mandatory = true, - ParameterSetName = NewJobTriggerCommand.DailyParameterSet)] - [Parameter(Mandatory = true, - ParameterSetName = NewJobTriggerCommand.WeeklyParameterSet)] - public DateTime At - { - get { return _atTime; } - set { _atTime = value; } - } - private DateTime _atTime; - - /// - /// User name for AtLogon trigger. User name is used to determine which user - /// log on causes the trigger to activate. - /// - [Parameter(ParameterSetName = NewJobTriggerCommand.AtLogonParameterSet)] - [ValidateNotNullOrEmpty] - public string User - { - get { return _user; } - set { _user = value; } - } - private string _user; - - /// - /// Days of week for trigger applies only to the Weekly parameter set. - /// Specifies which day(s) of the week the weekly trigger is activated. - /// - [Parameter(Mandatory = true, ParameterSetName = NewJobTriggerCommand.WeeklyParameterSet)] - [ValidateNotNullOrEmpty] - [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] - public DayOfWeek[] DaysOfWeek - { - get { return _daysOfWeek; } - set { _daysOfWeek = value; } - } - private DayOfWeek[] _daysOfWeek; - - /// - /// Switch to specify an AtStartup trigger. - /// - [Parameter(Mandatory = true, Position = 0, - ParameterSetName = NewJobTriggerCommand.AtStartupParameterSet)] - public SwitchParameter AtStartup - { - get { return _atStartup; } - set { _atStartup = value; } - } - private SwitchParameter _atStartup; - - /// - /// Switch to specify an AtLogon trigger. - /// - [Parameter(Mandatory = true, Position = 0, - ParameterSetName = NewJobTriggerCommand.AtLogonParameterSet)] - public SwitchParameter AtLogOn - { - get { return _atLogon; } - set { _atLogon = value; } - } - private SwitchParameter _atLogon; - - /// - /// Switch to specify a Once (one time) trigger. - /// - [Parameter(Mandatory = true, Position = 0, - ParameterSetName = NewJobTriggerCommand.OnceParameterSet)] - public SwitchParameter Once - { - get { return _once; } - set { _once = value; } - } - private SwitchParameter _once; - - /// - /// Repetition interval of a one time trigger. - /// - [Parameter(ParameterSetName = NewJobTriggerCommand.OnceParameterSet)] - public TimeSpan RepetitionInterval - { - get { return _repInterval; } - set { _repInterval = value; } - } - private TimeSpan _repInterval; - - /// - /// Repetition duration of a one time trigger. - /// - [Parameter(ParameterSetName = NewJobTriggerCommand.OnceParameterSet)] - public TimeSpan RepetitionDuration - { - get { return _repDuration; } - set { _repDuration = value; } - } - private TimeSpan _repDuration; - - /// - /// Repetition interval repeats indefinitely. - /// - [Parameter(ParameterSetName = NewJobTriggerCommand.OnceParameterSet)] - public SwitchParameter RepeatIndefinitely - { - get { return _repRepeatIndefinitely; } - set { _repRepeatIndefinitely = value; } - } - private SwitchParameter _repRepeatIndefinitely; - - /// - /// Switch to specify a Daily trigger. - /// - [Parameter(Mandatory = true, Position = 0, - ParameterSetName = NewJobTriggerCommand.DailyParameterSet)] - public SwitchParameter Daily - { - get { return _daily; } - set { _daily = value; } - } - private SwitchParameter _daily; - - /// - /// Switch to specify a Weekly trigger. - /// - [Parameter(Mandatory = true, Position = 0, - ParameterSetName = NewJobTriggerCommand.WeeklyParameterSet)] - public SwitchParameter Weekly - { - get { return _weekly; } - set { _weekly = value; } - } - private SwitchParameter _weekly; - - #endregion - - #region Cmdlet Overrides - - /// - /// Do begin processing. - /// - protected override void BeginProcessing() - { - base.BeginProcessing(); - - // Validate parameters. - if (_daysInterval < 1) - { - throw new PSArgumentException(ScheduledJobErrorStrings.InvalidDaysIntervalParam); - } - if (_weeksInterval < 1) - { - throw new PSArgumentException(ScheduledJobErrorStrings.InvalidWeeksIntervalParam); - } - } - - /// - /// Process input. - /// - protected override void ProcessRecord() - { - switch (ParameterSetName) - { - case AtLogonParameterSet: - CreateAtLogonTrigger(); - break; - - case AtStartupParameterSet: - CreateAtStartupTrigger(); - break; - - case OnceParameterSet: - CreateOnceTrigger(); - break; - - case DailyParameterSet: - CreateDailyTrigger(); - break; - - case WeeklyParameterSet: - CreateWeeklyTrigger(); - break; - } - } - - #endregion - - #region Private Methods - - private void CreateAtLogonTrigger() - { - WriteObject(ScheduledJobTrigger.CreateAtLogOnTrigger(_user, _randomDelay, 0, true)); - } - - private void CreateAtStartupTrigger() - { - WriteObject(ScheduledJobTrigger.CreateAtStartupTrigger(_randomDelay, 0, true)); - } - - private void CreateOnceTrigger() - { - TimeSpan? repInterval = null; - TimeSpan? repDuration = null; - if (MyInvocation.BoundParameters.ContainsKey("RepetitionInterval") || MyInvocation.BoundParameters.ContainsKey("RepetitionDuration") || - MyInvocation.BoundParameters.ContainsKey("RepeatIndefinitely")) - { - if (MyInvocation.BoundParameters.ContainsKey("RepeatIndefinitely")) - { - if (MyInvocation.BoundParameters.ContainsKey("RepetitionDuration")) - { - throw new PSArgumentException(ScheduledJobErrorStrings.InvalidRepeatIndefinitelyParams); - } - if (!MyInvocation.BoundParameters.ContainsKey("RepetitionInterval")) - { - throw new PSArgumentException(ScheduledJobErrorStrings.InvalidRepetitionRepeatParams); - } - _repDuration = TimeSpan.MaxValue; - } - else if (!MyInvocation.BoundParameters.ContainsKey("RepetitionInterval") || !MyInvocation.BoundParameters.ContainsKey("RepetitionDuration")) - { - throw new PSArgumentException(ScheduledJobErrorStrings.InvalidRepetitionParams); - } - if (_repInterval < TimeSpan.Zero || _repDuration < TimeSpan.Zero) - { - throw new PSArgumentException(ScheduledJobErrorStrings.InvalidRepetitionParamValues); - } - if (_repInterval < TimeSpan.FromMinutes(1)) - { - throw new PSArgumentException(ScheduledJobErrorStrings.InvalidRepetitionIntervalValue); - } - if (_repInterval > _repDuration) - { - throw new PSArgumentException(ScheduledJobErrorStrings.InvalidRepetitionInterval); - } - - repInterval = _repInterval; - repDuration = _repDuration; - } - - WriteObject(ScheduledJobTrigger.CreateOnceTrigger(_atTime, _randomDelay, repInterval, repDuration, 0, true)); - } - - private void CreateDailyTrigger() - { - WriteObject(ScheduledJobTrigger.CreateDailyTrigger(_atTime, _daysInterval, _randomDelay, 0, true)); - } - - private void CreateWeeklyTrigger() - { - WriteObject(ScheduledJobTrigger.CreateWeeklyTrigger(_atTime, _weeksInterval, _daysOfWeek, _randomDelay, 0, true)); - } - - #endregion - } -} diff --git a/src/Microsoft.PowerShell.ScheduledJob/commands/NewScheduledJobOption.cs b/src/Microsoft.PowerShell.ScheduledJob/commands/NewScheduledJobOption.cs deleted file mode 100644 index d6ec042cd73..00000000000 --- a/src/Microsoft.PowerShell.ScheduledJob/commands/NewScheduledJobOption.cs +++ /dev/null @@ -1,45 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -using System; -using System.Collections.Generic; -using System.Management.Automation; - -namespace Microsoft.PowerShell.ScheduledJob -{ - /// - /// This cmdlet creates a new scheduled job option object based on the provided - /// parameter values. - /// - [Cmdlet(VerbsCommon.New, "ScheduledJobOption", DefaultParameterSetName = ScheduledJobOptionCmdletBase.OptionsParameterSet, - HelpUri = "https://go.microsoft.com/fwlink/?LinkID=223919")] - [OutputType(typeof(ScheduledJobOptions))] - public sealed class NewScheduledJobOptionCommand : ScheduledJobOptionCmdletBase - { - #region Cmdlet Overrides - - /// - /// Process input. - /// - protected override void ProcessRecord() - { - WriteObject(new ScheduledJobOptions( - StartIfOnBattery, - !ContinueIfGoingOnBattery, - WakeToRun, - !StartIfIdle, - StopIfGoingOffIdle, - RestartOnIdleResume, - IdleDuration, - IdleTimeout, - !HideInTaskScheduler, - RunElevated, - !RequireNetwork, - DoNotAllowDemandStart, - MultipleInstancePolicy)); - } - - #endregion - } -} diff --git a/src/Microsoft.PowerShell.ScheduledJob/commands/RegisterJobDefinition.cs b/src/Microsoft.PowerShell.ScheduledJob/commands/RegisterJobDefinition.cs deleted file mode 100644 index d49b4c071fd..00000000000 --- a/src/Microsoft.PowerShell.ScheduledJob/commands/RegisterJobDefinition.cs +++ /dev/null @@ -1,379 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Management.Automation; -using System.Management.Automation.Runspaces; -using Microsoft.PowerShell.Commands; -using System.Diagnostics.CodeAnalysis; - -namespace Microsoft.PowerShell.ScheduledJob -{ - /// - /// This cmdlet creates a new scheduled job definition object based on the provided - /// parameter values and registers it with the Task Scheduler. - /// - [SuppressMessage("Microsoft.PowerShell", "PS1012:CallShouldProcessOnlyIfDeclaringSupport")] - [Cmdlet(VerbsLifecycle.Register, "ScheduledJob", SupportsShouldProcess = true, DefaultParameterSetName = RegisterScheduledJobCommand.ScriptBlockParameterSet, - HelpUri = "https://go.microsoft.com/fwlink/?LinkID=223922")] - [OutputType(typeof(ScheduledJobDefinition))] - public sealed class RegisterScheduledJobCommand : ScheduleJobCmdletBase - { - #region Parameters - - private const string FilePathParameterSet = "FilePath"; - private const string ScriptBlockParameterSet = "ScriptBlock"; - - - /// - /// File path for script to be run in job. - /// - [Parameter(Position = 1, Mandatory = true, - ParameterSetName = RegisterScheduledJobCommand.FilePathParameterSet)] - [ValidateNotNullOrEmpty] - public string FilePath - { - get { return _filePath; } - set { _filePath = value; } - } - private string _filePath; - - /// - /// ScriptBlock containing script to run in job. - /// - [Parameter(Position = 1, Mandatory = true, - ParameterSetName = RegisterScheduledJobCommand.ScriptBlockParameterSet)] - [ValidateNotNull] - public ScriptBlock ScriptBlock - { - get { return _scriptBlock; } - set { _scriptBlock = value; } - } - private ScriptBlock _scriptBlock; - - /// - /// Name of scheduled job definition. - /// - [Parameter(Position = 0, Mandatory = true, - ParameterSetName = RegisterScheduledJobCommand.FilePathParameterSet)] - [Parameter(Position = 0, Mandatory = true, - ParameterSetName = RegisterScheduledJobCommand.ScriptBlockParameterSet)] - [ValidateNotNullOrEmpty] - public string Name - { - get { return _name; } - set { _name = value; } - } - private string _name; - - /// - /// Triggers to define when job will run. - /// - [Parameter(ParameterSetName = RegisterScheduledJobCommand.FilePathParameterSet)] - [Parameter(ParameterSetName = RegisterScheduledJobCommand.ScriptBlockParameterSet)] - [ValidateNotNullOrEmpty] - [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] - public ScheduledJobTrigger[] Trigger - { - get { return _triggers; } - set { _triggers = value; } - } - private ScheduledJobTrigger[] _triggers; - - /// - /// Initialization script to run before the job starts. - /// - [Parameter(ParameterSetName = RegisterScheduledJobCommand.FilePathParameterSet)] - [Parameter(ParameterSetName = RegisterScheduledJobCommand.ScriptBlockParameterSet)] - [ValidateNotNull] - public ScriptBlock InitializationScript - { - get { return _initializationScript; } - set { _initializationScript = value; } - } - private ScriptBlock _initializationScript; - - /// - /// Runs the job in a 32-bit PowerShell process. - /// - [Parameter(ParameterSetName = RegisterScheduledJobCommand.FilePathParameterSet)] - [Parameter(ParameterSetName = RegisterScheduledJobCommand.ScriptBlockParameterSet)] - public SwitchParameter RunAs32 - { - get { return _runAs32; } - set { _runAs32 = value; } - } - private SwitchParameter _runAs32; - - /// - /// Credentials for job. - /// - [Parameter(ParameterSetName = RegisterScheduledJobCommand.FilePathParameterSet)] - [Parameter(ParameterSetName = RegisterScheduledJobCommand.ScriptBlockParameterSet)] - [Credential()] - public PSCredential Credential - { - get { return _credential; } - set { _credential = value; } - } - private PSCredential _credential; - - /// - /// Authentication mechanism to use for job. - /// - [Parameter(ParameterSetName = RegisterScheduledJobCommand.FilePathParameterSet)] - [Parameter(ParameterSetName = RegisterScheduledJobCommand.ScriptBlockParameterSet)] - public AuthenticationMechanism Authentication - { - get { return _authenticationMechanism; } - set { _authenticationMechanism = value; } - } - private AuthenticationMechanism _authenticationMechanism; - - /// - /// Scheduling options for job. - /// - [Parameter(ParameterSetName = RegisterScheduledJobCommand.FilePathParameterSet)] - [Parameter(ParameterSetName = RegisterScheduledJobCommand.ScriptBlockParameterSet)] - [ValidateNotNull] - public ScheduledJobOptions ScheduledJobOption - { - get { return _options; } - set { _options = value; } - } - private ScheduledJobOptions _options; - - /// - /// Argument list for FilePath parameter. - /// - [Parameter(ParameterSetName = RegisterScheduledJobCommand.FilePathParameterSet)] - [Parameter(ParameterSetName = RegisterScheduledJobCommand.ScriptBlockParameterSet)] - [ValidateNotNullOrEmpty] - [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] - public object[] ArgumentList - { - get { return _arguments; } - set { _arguments = value; } - } - private object[] _arguments; - - /// - /// Maximum number of job results allowed in job store. - /// - [Parameter(ParameterSetName = RegisterScheduledJobCommand.FilePathParameterSet)] - [Parameter(ParameterSetName = RegisterScheduledJobCommand.ScriptBlockParameterSet)] - public int MaxResultCount - { - get { return _executionHistoryLength; } - set { _executionHistoryLength = value; } - } - private int _executionHistoryLength; - - /// - /// Runs scheduled job immediately after successful registration. - /// - [Parameter(ParameterSetName = RegisterScheduledJobCommand.FilePathParameterSet)] - [Parameter(ParameterSetName = RegisterScheduledJobCommand.ScriptBlockParameterSet)] - public SwitchParameter RunNow - { - get { return _runNow; } - set { _runNow = value; } - } - private SwitchParameter _runNow; - - /// - /// Runs scheduled job at the repetition interval indicated by the - /// TimeSpan value for an unending duration. - /// - [Parameter(ParameterSetName = RegisterScheduledJobCommand.FilePathParameterSet)] - [Parameter(ParameterSetName = RegisterScheduledJobCommand.ScriptBlockParameterSet)] - public TimeSpan RunEvery - { - get { return _runEvery; } - set { _runEvery = value; } - } - private TimeSpan _runEvery; - - #endregion - - #region Cmdlet Overrides - - /// - /// Process input. - /// - protected override void ProcessRecord() - { - string targetString = StringUtil.Format(ScheduledJobErrorStrings.DefinitionWhatIf, Name); - if (!ShouldProcess(targetString, VerbsLifecycle.Register)) - { - return; - } - - ScheduledJobDefinition definition = null; - - switch (ParameterSetName) - { - case ScriptBlockParameterSet: - definition = CreateScriptBlockDefinition(); - break; - - case FilePathParameterSet: - definition = CreateFilePathDefinition(); - break; - } - - if (definition != null) - { - // Set the MaxCount value if available. - if (MyInvocation.BoundParameters.ContainsKey("MaxResultCount")) - { - if (MaxResultCount < 1) - { - string msg = StringUtil.Format(ScheduledJobErrorStrings.InvalidMaxResultCount); - Exception reason = new RuntimeException(msg); - ErrorRecord errorRecord = new ErrorRecord(reason, "InvalidMaxResultCountParameterForRegisterScheduledJobDefinition", ErrorCategory.InvalidArgument, null); - WriteError(errorRecord); - - return; - } - definition.SetExecutionHistoryLength(MaxResultCount, false); - } - - try - { - // If RunEvery parameter is specified then create a job trigger for the definition that - // runs the job at the requested interval. - if (MyInvocation.BoundParameters.ContainsKey("RunEvery")) - { - AddRepetitionJobTriggerToDefinition( - definition, - RunEvery, - false); - } - - definition.Register(); - WriteObject(definition); - - if (_runNow) - { - definition.RunAsTask(); - } - } - catch (ScheduledJobException e) - { - // Check for access denied error. - if (e.InnerException != null && e.InnerException is System.UnauthorizedAccessException) - { - string msg = StringUtil.Format(ScheduledJobErrorStrings.UnauthorizedAccessError, definition.Name); - Exception reason = new RuntimeException(msg, e); - ErrorRecord errorRecord = new ErrorRecord(reason, "UnauthorizedAccessToRegisterScheduledJobDefinition", ErrorCategory.PermissionDenied, definition); - WriteError(errorRecord); - } - else if (e.InnerException != null && e.InnerException is System.IO.DirectoryNotFoundException) - { - string msg = StringUtil.Format(ScheduledJobErrorStrings.DirectoryNotFoundError, definition.Name); - Exception reason = new RuntimeException(msg, e); - ErrorRecord errorRecord = new ErrorRecord(reason, "DirectoryNotFoundWhenRegisteringScheduledJobDefinition", ErrorCategory.ObjectNotFound, definition); - WriteError(errorRecord); - } - else if (e.InnerException != null && e.InnerException is System.Runtime.Serialization.InvalidDataContractException) - { - string innerMsg = (!string.IsNullOrEmpty(e.InnerException.Message)) ? e.InnerException.Message : string.Empty; - string msg = StringUtil.Format(ScheduledJobErrorStrings.CannotSerializeData, definition.Name, innerMsg); - Exception reason = new RuntimeException(msg, e); - ErrorRecord errorRecord = new ErrorRecord(reason, "CannotSerializeDataWhenRegisteringScheduledJobDefinition", ErrorCategory.InvalidData, definition); - WriteError(errorRecord); - } - else - { - // Create record around known exception type. - ErrorRecord errorRecord = new ErrorRecord(e, "CantRegisterScheduledJobDefinition", ErrorCategory.InvalidOperation, definition); - WriteError(errorRecord); - } - } - } - } - - #endregion - - #region Private Methods - - private ScheduledJobDefinition CreateScriptBlockDefinition() - { - JobDefinition jobDefinition = new JobDefinition(typeof(ScheduledJobSourceAdapter), ScriptBlock.ToString(), _name); - jobDefinition.ModuleName = ModuleName; - Dictionary parameterCollection = CreateCommonParameters(); - - // ScriptBlock, mandatory - parameterCollection.Add(ScheduledJobInvocationInfo.ScriptBlockParameter, ScriptBlock); - - JobInvocationInfo jobInvocationInfo = new ScheduledJobInvocationInfo(jobDefinition, parameterCollection); - - ScheduledJobDefinition definition = new ScheduledJobDefinition(jobInvocationInfo, Trigger, - ScheduledJobOption, _credential); - - return definition; - } - - private ScheduledJobDefinition CreateFilePathDefinition() - { - JobDefinition jobDefinition = new JobDefinition(typeof(ScheduledJobSourceAdapter), FilePath, _name); - jobDefinition.ModuleName = ModuleName; - Dictionary parameterCollection = CreateCommonParameters(); - - // FilePath, mandatory - if (!FilePath.EndsWith(".ps1", StringComparison.OrdinalIgnoreCase)) - { - string msg = StringUtil.Format(ScheduledJobErrorStrings.InvalidFilePathFile); - Exception reason = new RuntimeException(msg); - ErrorRecord errorRecord = new ErrorRecord(reason, "InvalidFilePathParameterForRegisterScheduledJobDefinition", ErrorCategory.InvalidArgument, this); - WriteError(errorRecord); - - return null; - } - Collection pathInfos = SessionState.Path.GetResolvedPSPathFromPSPath(FilePath); - if (pathInfos.Count != 1) - { - string msg = StringUtil.Format(ScheduledJobErrorStrings.InvalidFilePath); - Exception reason = new RuntimeException(msg); - ErrorRecord errorRecord = new ErrorRecord(reason, "InvalidFilePathParameterForRegisterScheduledJobDefinition", ErrorCategory.InvalidArgument, this); - WriteError(errorRecord); - - return null; - } - parameterCollection.Add(ScheduledJobInvocationInfo.FilePathParameter, pathInfos[0].Path); - - JobInvocationInfo jobInvocationInfo = new ScheduledJobInvocationInfo(jobDefinition, parameterCollection); - - ScheduledJobDefinition definition = new ScheduledJobDefinition(jobInvocationInfo, Trigger, - ScheduledJobOption, _credential); - - return definition; - } - - private Dictionary CreateCommonParameters() - { - Dictionary parameterCollection = new Dictionary(); - - parameterCollection.Add(ScheduledJobInvocationInfo.RunAs32Parameter, RunAs32.ToBool()); - parameterCollection.Add(ScheduledJobInvocationInfo.AuthenticationParameter, Authentication); - - if (InitializationScript != null) - { - parameterCollection.Add(ScheduledJobInvocationInfo.InitializationScriptParameter, InitializationScript); - } - - if (ArgumentList != null) - { - parameterCollection.Add(ScheduledJobInvocationInfo.ArgumentListParameter, ArgumentList); - } - - return parameterCollection; - } - - #endregion - } -} diff --git a/src/Microsoft.PowerShell.ScheduledJob/commands/RemoveJobTrigger.cs b/src/Microsoft.PowerShell.ScheduledJob/commands/RemoveJobTrigger.cs deleted file mode 100644 index b3a487bd367..00000000000 --- a/src/Microsoft.PowerShell.ScheduledJob/commands/RemoveJobTrigger.cs +++ /dev/null @@ -1,143 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Diagnostics.CodeAnalysis; -using System.Management.Automation; -using System.Management.Automation.Internal; -using System.Management.Automation.Host; -using System.Threading; -using System.Diagnostics; -using System.Globalization; - -namespace Microsoft.PowerShell.ScheduledJob -{ - /// - /// This cmdlet removes ScheduledJobTriggers from ScheduledJobDefinition objects. - /// - [Cmdlet(VerbsCommon.Remove, "JobTrigger", DefaultParameterSetName = RemoveJobTriggerCommand.JobDefinitionParameterSet, - HelpUri = "https://go.microsoft.com/fwlink/?LinkID=223914")] - public sealed class RemoveJobTriggerCommand : ScheduleJobCmdletBase - { - #region Parameters - - private const string JobDefinitionParameterSet = "JobDefinition"; - private const string JobDefinitionIdParameterSet = "JobDefinitionId"; - private const string JobDefinitionNameParameterSet = "JobDefinitionName"; - - /// - /// Trigger number to remove. - /// - [Parameter(ParameterSetName = RemoveJobTriggerCommand.JobDefinitionParameterSet)] - [Parameter(ParameterSetName = RemoveJobTriggerCommand.JobDefinitionIdParameterSet)] - [Parameter(ParameterSetName = RemoveJobTriggerCommand.JobDefinitionNameParameterSet)] - [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] - public Int32[] TriggerId - { - get { return _triggerIds; } - set { _triggerIds = value; } - } - private Int32[] _triggerIds; - - /// - /// ScheduledJobDefinition Id. - /// - [Parameter(Position = 0, Mandatory = true, - ParameterSetName = RemoveJobTriggerCommand.JobDefinitionIdParameterSet)] - [ValidateNotNullOrEmpty] - [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] - public Int32[] Id - { - get { return _definitionIds; } - set { _definitionIds = value; } - } - private Int32[] _definitionIds; - - /// - /// ScheduledJobDefinition Name. - /// - [Parameter(Position = 0, Mandatory = true, - ParameterSetName = RemoveJobTriggerCommand.JobDefinitionNameParameterSet)] - [ValidateNotNullOrEmpty] - [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] - public string[] Name - { - get { return _names; } - set { _names = value; } - } - private string[] _names; - - /// - /// ScheduledJobDefinition. - /// - [Parameter(Position = 0, Mandatory = true, ValueFromPipeline = true, - ParameterSetName = RemoveJobTriggerCommand.JobDefinitionParameterSet)] - [ValidateNotNullOrEmpty] - [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] - public ScheduledJobDefinition[] InputObject - { - get { return _definitions; } - set { _definitions = value; } - } - private ScheduledJobDefinition[] _definitions; - - #endregion - - #region Cmdlet Overrides - - /// - /// Process input. - /// - protected override void ProcessRecord() - { - switch (ParameterSetName) - { - case JobDefinitionParameterSet: - RemoveFromJobDefinition(_definitions); - break; - - case JobDefinitionIdParameterSet: - RemoveFromJobDefinition(GetJobDefinitionsById(_definitionIds)); - break; - - case JobDefinitionNameParameterSet: - RemoveFromJobDefinition(GetJobDefinitionsByName(_names)); - break; - } - } - - #endregion - - #region Private Methods - - private void RemoveFromJobDefinition(IEnumerable definitions) - { - foreach (ScheduledJobDefinition definition in definitions) - { - List notFoundIds = new List(); - try - { - notFoundIds = definition.RemoveTriggers(_triggerIds, true); - } - catch (ScheduledJobException e) - { - string msg = StringUtil.Format(ScheduledJobErrorStrings.CantRemoveTriggersFromDefinition, definition.Name); - Exception reason = new RuntimeException(msg, e); - ErrorRecord errorRecord = new ErrorRecord(reason, "CantRemoveTriggersFromScheduledJobDefinition", ErrorCategory.InvalidOperation, definition); - WriteError(errorRecord); - } - - // Report not found errors. - foreach (Int32 idNotFound in notFoundIds) - { - WriteTriggerNotFoundError(idNotFound, definition.Name, definition); - } - } - } - - #endregion - } -} diff --git a/src/Microsoft.PowerShell.ScheduledJob/commands/SchedJobCmdletBase.cs b/src/Microsoft.PowerShell.ScheduledJob/commands/SchedJobCmdletBase.cs deleted file mode 100644 index 17568de908d..00000000000 --- a/src/Microsoft.PowerShell.ScheduledJob/commands/SchedJobCmdletBase.cs +++ /dev/null @@ -1,467 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Management.Automation; - -namespace Microsoft.PowerShell.ScheduledJob -{ - /// - /// Base class for ScheduledJob cmdlets. - /// - public abstract class ScheduleJobCmdletBase : PSCmdlet - { - #region Cmdlet Strings - - /// - /// Scheduled job module name. - /// - protected const string ModuleName = "PSScheduledJob"; - - #endregion - - #region Utility Methods - - /// - /// Makes delegate callback call for each scheduledjob definition object found. - /// - /// Callback delegate for each discovered item. - internal void FindAllJobDefinitions( - Action itemFound) - { - Dictionary errors = ScheduledJobDefinition.RefreshRepositoryFromStore((definition) => - { - if (ValidateJobDefinition(definition)) - { - itemFound(definition); - } - }); - HandleAllLoadErrors(errors); - } - - /// - /// Returns a single ScheduledJobDefinition object from the local - /// scheduled job definition repository corresponding to the provided id. - /// - /// Local repository scheduled job definition id - /// Errors/warnings are written to host - /// ScheduledJobDefinition object - internal ScheduledJobDefinition GetJobDefinitionById( - Int32 id, - bool writeErrorsAndWarnings = true) - { - Dictionary errors = ScheduledJobDefinition.RefreshRepositoryFromStore(null); - HandleAllLoadErrors(errors); - - foreach (var definition in ScheduledJobDefinition.Repository.Definitions) - { - if (definition.Id == id && - ValidateJobDefinition(definition)) - { - return definition; - } - } - - if (writeErrorsAndWarnings) - { - WriteDefinitionNotFoundByIdError(id); - } - - return null; - } - - /// - /// Returns an array of ScheduledJobDefinition objects from the local - /// scheduled job definition repository corresponding to the provided Ids. - /// - /// Local repository scheduled job definition ids - /// Errors/warnings are written to host - /// List of ScheduledJobDefinition objects - internal List GetJobDefinitionsById( - Int32[] ids, - bool writeErrorsAndWarnings = true) - { - Dictionary errors = ScheduledJobDefinition.RefreshRepositoryFromStore(null); - HandleAllLoadErrors(errors); - - List definitions = new List(); - HashSet findIds = new HashSet(ids); - foreach (var definition in ScheduledJobDefinition.Repository.Definitions) - { - if (findIds.Contains(definition.Id) && - ValidateJobDefinition(definition)) - { - definitions.Add(definition); - findIds.Remove(definition.Id); - } - } - - if (writeErrorsAndWarnings) - { - foreach (int id in findIds) - { - WriteDefinitionNotFoundByIdError(id); - } - } - - return definitions; - } - - /// - /// Makes delegate callback call for each scheduledjob definition object found. - /// - /// Local repository scheduled job definition ids - /// Callback delegate for each discovered item. - /// Errors/warnings are written to host - internal void FindJobDefinitionsById( - Int32[] ids, - Action itemFound, - bool writeErrorsAndWarnings = true) - { - HashSet findIds = new HashSet(ids); - Dictionary errors = ScheduledJobDefinition.RefreshRepositoryFromStore((definition) => - { - if (findIds.Contains(definition.Id) && - ValidateJobDefinition(definition)) - { - itemFound(definition); - findIds.Remove(definition.Id); - } - }); - - HandleAllLoadErrors(errors); - - if (writeErrorsAndWarnings) - { - foreach (Int32 id in findIds) - { - WriteDefinitionNotFoundByIdError(id); - } - } - } - - /// - /// Returns an array of ScheduledJobDefinition objects from the local - /// scheduled job definition repository corresponding to the given name. - /// - /// Scheduled job definition name - /// Errors/warnings are written to host - /// ScheduledJobDefinition object - internal ScheduledJobDefinition GetJobDefinitionByName( - string name, - bool writeErrorsAndWarnings = true) - { - Dictionary errors = ScheduledJobDefinition.RefreshRepositoryFromStore(null); - - // Look for match. - WildcardPattern namePattern = new WildcardPattern(name, WildcardOptions.IgnoreCase); - foreach (var definition in ScheduledJobDefinition.Repository.Definitions) - { - if (namePattern.IsMatch(definition.Name) && - ValidateJobDefinition(definition)) - { - return definition; - } - } - - // Look for load error. - foreach (var error in errors) - { - if (namePattern.IsMatch(error.Key)) - { - HandleLoadError(error.Key, error.Value); - } - } - - if (writeErrorsAndWarnings) - { - WriteDefinitionNotFoundByNameError(name); - } - - return null; - } - - /// - /// Returns an array of ScheduledJobDefinition objects from the local - /// scheduled job definition repository corresponding to the given names. - /// - /// Scheduled job definition names - /// Errors/warnings are written to host - /// List of ScheduledJobDefinition objects - internal List GetJobDefinitionsByName( - string[] names, - bool writeErrorsAndWarnings = true) - { - Dictionary errors = ScheduledJobDefinition.RefreshRepositoryFromStore(null); - - List definitions = new List(); - foreach (string name in names) - { - WildcardPattern namePattern = new WildcardPattern(name, WildcardOptions.IgnoreCase); - - // Look for match. - bool nameFound = false; - foreach (var definition in ScheduledJobDefinition.Repository.Definitions) - { - if (namePattern.IsMatch(definition.Name) && - ValidateJobDefinition(definition)) - { - nameFound = true; - definitions.Add(definition); - } - } - - // Look for load error. - foreach (var error in errors) - { - if (namePattern.IsMatch(error.Key)) - { - HandleLoadError(error.Key, error.Value); - } - } - - if (!nameFound && writeErrorsAndWarnings) - { - WriteDefinitionNotFoundByNameError(name); - } - } - - return definitions; - } - - /// - /// Makes delegate callback call for each scheduledjob definition object found. - /// - /// Scheduled job definition names - /// Callback delegate for each discovered item. - /// Errors/warnings are written to host - internal void FindJobDefinitionsByName( - string[] names, - Action itemFound, - bool writeErrorsAndWarnings = true) - { - HashSet notFoundNames = new HashSet(names); - Dictionary patterns = new Dictionary(); - foreach (string name in names) - { - if (!patterns.ContainsKey(name)) - { - patterns.Add(name, new WildcardPattern(name, WildcardOptions.IgnoreCase)); - } - } - - Dictionary errors = ScheduledJobDefinition.RefreshRepositoryFromStore((definition) => - { - foreach (var item in patterns) - { - if (item.Value.IsMatch(definition.Name) && - ValidateJobDefinition(definition)) - { - itemFound(definition); - if (notFoundNames.Contains(item.Key)) - { - notFoundNames.Remove(item.Key); - } - } - } - }); - - // Look for load error. - foreach (var error in errors) - { - foreach (var item in patterns) - { - if (item.Value.IsMatch(error.Key)) - { - HandleLoadError(error.Key, error.Value); - } - } - } - - if (writeErrorsAndWarnings) - { - foreach (var name in notFoundNames) - { - WriteDefinitionNotFoundByNameError(name); - } - } - } - - /// - /// Writes a "Trigger not found" error to host. - /// - /// Trigger Id not found - /// ScheduledJobDefinition name - /// Error object - internal void WriteTriggerNotFoundError( - Int32 notFoundId, - string definitionName, - object errorObject) - { - string msg = StringUtil.Format(ScheduledJobErrorStrings.TriggerNotFound, notFoundId, definitionName); - Exception reason = new RuntimeException(msg); - ErrorRecord errorRecord = new ErrorRecord(reason, "ScheduledJobTriggerNotFound", ErrorCategory.ObjectNotFound, errorObject); - WriteError(errorRecord); - } - - /// - /// Writes a "Definition not found for Id" error to host. - /// - /// Definition Id - internal void WriteDefinitionNotFoundByIdError( - Int32 defId) - { - string msg = StringUtil.Format(ScheduledJobErrorStrings.DefinitionNotFoundById, defId); - Exception reason = new RuntimeException(msg); - ErrorRecord errorRecord = new ErrorRecord(reason, "ScheduledJobDefinitionNotFoundById", ErrorCategory.ObjectNotFound, null); - WriteError(errorRecord); - } - - /// - /// Writes a "Definition not found for Name" error to host. - /// - /// Definition Name - internal void WriteDefinitionNotFoundByNameError( - string name) - { - string msg = StringUtil.Format(ScheduledJobErrorStrings.DefinitionNotFoundByName, name); - Exception reason = new RuntimeException(msg); - ErrorRecord errorRecord = new ErrorRecord(reason, "ScheduledJobDefinitionNotFoundByName", ErrorCategory.ObjectNotFound, null); - WriteError(errorRecord); - } - - /// - /// Writes a "Load from job store" error to host. - /// - /// Scheduled job definition name - /// Exception thrown during loading - internal void WriteErrorLoadingDefinition(string name, Exception error) - { - string msg = StringUtil.Format(ScheduledJobErrorStrings.CantLoadDefinitionFromStore, name); - Exception reason = new RuntimeException(msg, error); - ErrorRecord errorRecord = new ErrorRecord(reason, "CantLoadScheduledJobDefinitionFromStore", ErrorCategory.InvalidOperation, null); - WriteError(errorRecord); - } - - /// - /// Creates a Once job trigger with provided repetition interval and an - /// infinite duration, and adds the trigger to the provided scheduled job - /// definition object. - /// - /// ScheduledJobDefinition - /// rep interval - /// save definition change - internal static void AddRepetitionJobTriggerToDefinition( - ScheduledJobDefinition definition, - TimeSpan repInterval, - bool save) - { - if (definition == null) - { - throw new PSArgumentNullException("definition"); - } - - TimeSpan repDuration = TimeSpan.MaxValue; - - // Validate every interval value. - if (repInterval < TimeSpan.Zero || repDuration < TimeSpan.Zero) - { - throw new PSArgumentException(ScheduledJobErrorStrings.InvalidRepetitionParamValues); - } - if (repInterval < TimeSpan.FromMinutes(1)) - { - throw new PSArgumentException(ScheduledJobErrorStrings.InvalidRepetitionIntervalValue); - } - if (repInterval > repDuration) - { - throw new PSArgumentException(ScheduledJobErrorStrings.InvalidRepetitionInterval); - } - - // Create job trigger. - var trigger = ScheduledJobTrigger.CreateOnceTrigger( - DateTime.Now, - TimeSpan.Zero, - repInterval, - repDuration, - 0, - true); - - definition.AddTriggers(new ScheduledJobTrigger[] { trigger }, save); - } - - #endregion - - #region Private Methods - - private void HandleAllLoadErrors(Dictionary errors) - { - foreach (var error in errors) - { - HandleLoadError(error.Key, error.Value); - } - } - - private void HandleLoadError(string name, Exception e) - { - if (e is System.IO.IOException || - e is System.Xml.XmlException || - e is System.TypeInitializationException || - e is System.Runtime.Serialization.SerializationException || - e is System.ArgumentNullException) - { - // Remove the corrupted scheduled job definition and - // notify user with error message. - ScheduledJobDefinition.RemoveDefinition(name); - WriteErrorLoadingDefinition(name, e); - } - } - - private void ValidateJobDefinitions() - { - foreach (var definition in ScheduledJobDefinition.Repository.Definitions) - { - ValidateJobDefinition(definition); - } - } - - /// - /// Validates the job definition object retrieved from store by syncing - /// its data with the corresponding Task Scheduler task. If no task - /// is found then validation fails. - /// - /// - /// - private bool ValidateJobDefinition(ScheduledJobDefinition definition) - { - Exception ex = null; - try - { - definition.SyncWithWTS(); - } - catch (System.IO.DirectoryNotFoundException e) - { - ex = e; - } - catch (System.IO.FileNotFoundException e) - { - ex = e; - } - catch (System.ArgumentNullException e) - { - ex = e; - } - - if (ex != null) - { - WriteErrorLoadingDefinition(definition.Name, ex); - } - - return (ex == null); - } - - #endregion - } -} diff --git a/src/Microsoft.PowerShell.ScheduledJob/commands/ScheduledJobOptionCmdletBase.cs b/src/Microsoft.PowerShell.ScheduledJob/commands/ScheduledJobOptionCmdletBase.cs deleted file mode 100644 index dc506b92491..00000000000 --- a/src/Microsoft.PowerShell.ScheduledJob/commands/ScheduledJobOptionCmdletBase.cs +++ /dev/null @@ -1,194 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -using System; -using System.Collections.Generic; -using System.Management.Automation; - -namespace Microsoft.PowerShell.ScheduledJob -{ - /// - /// Base class for NewScheduledJobOption, SetScheduledJobOption cmdlets. - /// - public abstract class ScheduledJobOptionCmdletBase : ScheduleJobCmdletBase - { - #region Parameters - - /// - /// Options parameter set name. - /// - protected const string OptionsParameterSet = "Options"; - - /// - /// Scheduled job task is run with elevated privileges when this switch is selected. - /// - [Parameter(ParameterSetName = ScheduledJobOptionCmdletBase.OptionsParameterSet)] - public SwitchParameter RunElevated - { - get { return _runElevated; } - set { _runElevated = value; } - } - private SwitchParameter _runElevated = false; - - /// - /// Scheduled job task is hidden in Windows Task Scheduler when true. - /// - [Parameter(ParameterSetName = ScheduledJobOptionCmdletBase.OptionsParameterSet)] - public SwitchParameter HideInTaskScheduler - { - get { return _hideInTaskScheduler; } - set { _hideInTaskScheduler = value; } - } - private SwitchParameter _hideInTaskScheduler = false; - - /// - /// Scheduled job task will be restarted when machine becomes idle. This is applicable - /// only if the job was configured to stop when no longer idle. - /// - [Parameter(ParameterSetName = ScheduledJobOptionCmdletBase.OptionsParameterSet)] - public SwitchParameter RestartOnIdleResume - { - get { return _restartOnIdleResume; } - set { _restartOnIdleResume = value; } - } - private SwitchParameter _restartOnIdleResume = false; - - /// - /// Provides task scheduler options for multiple running instances of the job. - /// - [Parameter(ParameterSetName = ScheduledJobOptionCmdletBase.OptionsParameterSet)] - public TaskMultipleInstancePolicy MultipleInstancePolicy - { - get { return _multipleInstancePolicy; } - set { _multipleInstancePolicy = value; } - } - private TaskMultipleInstancePolicy _multipleInstancePolicy = TaskMultipleInstancePolicy.IgnoreNew; - - /// - /// Prevents the job task from being started manually via Task Scheduler UI. - /// - [Parameter(ParameterSetName = ScheduledJobOptionCmdletBase.OptionsParameterSet)] - public SwitchParameter DoNotAllowDemandStart - { - get { return _doNotAllowDemandStart; } - set { _doNotAllowDemandStart = value; } - } - private SwitchParameter _doNotAllowDemandStart = false; - - /// - /// Allows the job task to be run only when network connection available. - /// - [Parameter(ParameterSetName = ScheduledJobOptionCmdletBase.OptionsParameterSet)] - public SwitchParameter RequireNetwork - { - get { return _requireNetwork; } - set { _requireNetwork = value; } - } - private SwitchParameter _requireNetwork = false; - - /// - /// Stops running job started by Task Scheduler if computer is no longer idle. - /// - [Parameter(ParameterSetName = ScheduledJobOptionCmdletBase.OptionsParameterSet)] - public SwitchParameter StopIfGoingOffIdle - { - get { return _stopIfGoingOffIdle; } - set { _stopIfGoingOffIdle = value; } - } - private SwitchParameter _stopIfGoingOffIdle = false; - - /// - /// Will wake the computer to run the job if computer is in sleep mode when - /// trigger activates. - /// - [Parameter(ParameterSetName = ScheduledJobOptionCmdletBase.OptionsParameterSet)] - public SwitchParameter WakeToRun - { - get { return _wakeToRun; } - set { _wakeToRun = value; } - } - private SwitchParameter _wakeToRun = false; - - /// - /// Continue running task job if computer going on battery. - /// - [Parameter(ParameterSetName = ScheduledJobOptionCmdletBase.OptionsParameterSet)] - public SwitchParameter ContinueIfGoingOnBattery - { - get { return _continueIfGoingOnBattery; } - set { _continueIfGoingOnBattery = value; } - } - private SwitchParameter _continueIfGoingOnBattery = false; - - /// - /// Will start job task even if computer is running on battery power. - /// - [Parameter(ParameterSetName = ScheduledJobOptionCmdletBase.OptionsParameterSet)] - public SwitchParameter StartIfOnBattery - { - get { return _startIfOnBattery; } - set { _startIfOnBattery = value; } - } - private SwitchParameter _startIfOnBattery = false; - - /// - /// Specifies how long Task Scheduler will wait for idle time after a trigger has - /// activated before giving up trying to run job during computer idle. - /// - [Parameter(ParameterSetName = ScheduledJobOptionCmdletBase.OptionsParameterSet)] - public TimeSpan IdleTimeout - { - get { return _idleTimeout; } - set { _idleTimeout = value; } - } - private TimeSpan _idleTimeout = new TimeSpan(1, 0, 0); - - /// - /// How long the computer needs to be idle before a triggered job task is started. - /// - [Parameter(ParameterSetName = ScheduledJobOptionCmdletBase.OptionsParameterSet)] - public TimeSpan IdleDuration - { - get { return _idleDuration; } - set { _idleDuration = value; } - } - private TimeSpan _idleDuration = new TimeSpan(0, 10, 0); - - /// - /// Will start job task if machine is idle. - /// - [Parameter(ParameterSetName = ScheduledJobOptionCmdletBase.OptionsParameterSet)] - public SwitchParameter StartIfIdle - { - get { return _startIfIdle; } - set { _startIfIdle = value; } - } - private SwitchParameter _startIfIdle = false; - - #endregion - - #region Cmdlet Overrides - - /// - /// Begin processing. - /// - protected override void BeginProcessing() - { - // Validate parameters. - if (MyInvocation.BoundParameters.ContainsKey("IdleTimeout") && - _idleTimeout < TimeSpan.Zero) - { - throw new PSArgumentException(ScheduledJobErrorStrings.InvalidIdleTimeout); - } - - if (MyInvocation.BoundParameters.ContainsKey("IdleDuration") && - _idleDuration < TimeSpan.Zero) - { - throw new PSArgumentException(ScheduledJobErrorStrings.InvalidIdleDuration); - } - } - - #endregion - } -} diff --git a/src/Microsoft.PowerShell.ScheduledJob/commands/SetJobDefinition.cs b/src/Microsoft.PowerShell.ScheduledJob/commands/SetJobDefinition.cs deleted file mode 100644 index eb044874995..00000000000 --- a/src/Microsoft.PowerShell.ScheduledJob/commands/SetJobDefinition.cs +++ /dev/null @@ -1,521 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -using System; -using System.Collections.Generic; -using System.Management.Automation; -using System.Management.Automation.Runspaces; -using System.Diagnostics; -using System.Diagnostics.CodeAnalysis; - -namespace Microsoft.PowerShell.ScheduledJob -{ - /// - /// This cmdlet updates a scheduled job definition object based on the provided - /// parameter values and saves changes to job store and Task Scheduler. - /// - [Cmdlet(VerbsCommon.Set, "ScheduledJob", DefaultParameterSetName = SetScheduledJobCommand.ScriptBlockParameterSet, - HelpUri = "https://go.microsoft.com/fwlink/?LinkID=223924")] - [OutputType(typeof(ScheduledJobDefinition))] - public sealed class SetScheduledJobCommand : ScheduleJobCmdletBase - { - #region Parameters - - private const string ExecutionParameterSet = "Execution"; - private const string ScriptBlockParameterSet = "ScriptBlock"; - private const string FilePathParameterSet = "FilePath"; - - - /// - /// Name of scheduled job definition. - /// - [Parameter(ParameterSetName = SetScheduledJobCommand.FilePathParameterSet)] - [Parameter(ParameterSetName = SetScheduledJobCommand.ScriptBlockParameterSet)] - [ValidateNotNullOrEmpty] - public string Name - { - get { return _name; } - set { _name = value; } - } - private string _name; - - /// - /// File path for script to be run in job. - /// - [Parameter(ParameterSetName = SetScheduledJobCommand.FilePathParameterSet)] - [ValidateNotNullOrEmpty] - public string FilePath - { - get { return _filePath; } - set { _filePath = value; } - } - private string _filePath; - - /// - /// ScriptBlock containing script to run in job. - /// - [Parameter(ParameterSetName = SetScheduledJobCommand.ScriptBlockParameterSet)] - [ValidateNotNull] - public ScriptBlock ScriptBlock - { - get { return _scriptBlock; } - set { _scriptBlock = value; } - } - private ScriptBlock _scriptBlock; - - /// - /// Triggers to define when job will run. - /// - [Parameter(ParameterSetName = SetScheduledJobCommand.ScriptBlockParameterSet)] - [Parameter(ParameterSetName = SetScheduledJobCommand.FilePathParameterSet)] - [ValidateNotNullOrEmpty] - [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] - public ScheduledJobTrigger[] Trigger - { - get { return _triggers; } - set { _triggers = value; } - } - private ScheduledJobTrigger[] _triggers; - - /// - /// Initialization script to run before the job starts. - /// - [Parameter(ParameterSetName = SetScheduledJobCommand.ScriptBlockParameterSet)] - [Parameter(ParameterSetName = SetScheduledJobCommand.FilePathParameterSet)] - [ValidateNotNull] - public ScriptBlock InitializationScript - { - get { return _initializationScript; } - set { _initializationScript = value; } - } - private ScriptBlock _initializationScript; - - /// - /// Runs the job in a 32-bit PowerShell process. - /// - [Parameter(ParameterSetName = SetScheduledJobCommand.ScriptBlockParameterSet)] - [Parameter(ParameterSetName = SetScheduledJobCommand.FilePathParameterSet)] - public SwitchParameter RunAs32 - { - get { return _runAs32; } - set { _runAs32 = value; } - } - private SwitchParameter _runAs32; - - /// - /// Credentials for job. - /// - [Parameter(ParameterSetName = SetScheduledJobCommand.ScriptBlockParameterSet)] - [Parameter(ParameterSetName = SetScheduledJobCommand.FilePathParameterSet)] - [Credential()] - public PSCredential Credential - { - get { return _credential; } - set { _credential = value; } - } - private PSCredential _credential; - - /// - /// Authentication mechanism to use for job. - /// - [Parameter(ParameterSetName = SetScheduledJobCommand.ScriptBlockParameterSet)] - [Parameter(ParameterSetName = SetScheduledJobCommand.FilePathParameterSet)] - public AuthenticationMechanism Authentication - { - get { return _authenticationMechanism; } - set { _authenticationMechanism = value; } - } - private AuthenticationMechanism _authenticationMechanism; - - /// - /// Scheduling options for job. - /// - [Parameter(ParameterSetName = SetScheduledJobCommand.ScriptBlockParameterSet)] - [Parameter(ParameterSetName = SetScheduledJobCommand.FilePathParameterSet)] - [ValidateNotNull] - public ScheduledJobOptions ScheduledJobOption - { - get { return _options; } - set { _options = value; } - } - private ScheduledJobOptions _options; - - /// - /// Input for the job. - /// - [Parameter(Position = 0, Mandatory = true, ValueFromPipeline = true, - ParameterSetName = SetScheduledJobCommand.ScriptBlockParameterSet)] - [Parameter(Position = 0, Mandatory = true, ValueFromPipeline = true, - ParameterSetName = SetScheduledJobCommand.FilePathParameterSet)] - [Parameter(Position = 0, Mandatory = true, ValueFromPipeline = true, - ParameterSetName = SetScheduledJobCommand.ExecutionParameterSet)] - [ValidateNotNull] - public ScheduledJobDefinition InputObject - { - get { return _definition; } - set { _definition = value; } - } - private ScheduledJobDefinition _definition; - - /// - /// ClearExecutionHistory - /// - [Parameter(ParameterSetName = SetScheduledJobCommand.ExecutionParameterSet)] - public SwitchParameter ClearExecutionHistory - { - get { return _clearExecutionHistory; } - set { _clearExecutionHistory = value; } - } - private SwitchParameter _clearExecutionHistory; - - /// - /// Maximum number of job results allowed in job store. - /// - [Parameter(ParameterSetName = SetScheduledJobCommand.ScriptBlockParameterSet)] - [Parameter(ParameterSetName = SetScheduledJobCommand.FilePathParameterSet)] - public int MaxResultCount - { - get { return _executionHistoryLength; } - set { _executionHistoryLength = value; } - } - private int _executionHistoryLength; - - /// - /// Pass the ScheduledJobDefinition object through to output. - /// - [Parameter(ParameterSetName = SetScheduledJobCommand.ScriptBlockParameterSet)] - [Parameter(ParameterSetName = SetScheduledJobCommand.FilePathParameterSet)] - [Parameter(ParameterSetName = SetScheduledJobCommand.ExecutionParameterSet)] - public SwitchParameter PassThru - { - get { return _passThru; } - set { _passThru = value; } - } - private SwitchParameter _passThru; - - /// - /// Argument list. - /// - [Parameter(ParameterSetName = SetScheduledJobCommand.ScriptBlockParameterSet)] - [Parameter(ParameterSetName = SetScheduledJobCommand.FilePathParameterSet)] - [ValidateNotNullOrEmpty] - [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] - public object[] ArgumentList - { - get { return _arguments; } - set { _arguments = value; } - } - private object[] _arguments; - - /// - /// Runs scheduled job immediately after successfully setting job definition. - /// - [Parameter(ParameterSetName = SetScheduledJobCommand.ScriptBlockParameterSet)] - [Parameter(ParameterSetName = SetScheduledJobCommand.FilePathParameterSet)] - public SwitchParameter RunNow - { - get { return _runNow; } - set { _runNow = value; } - } - private SwitchParameter _runNow; - - /// - /// Runs scheduled job at the repetition interval indicated by the - /// TimeSpan value for an unending duration. - /// - [Parameter(ParameterSetName = SetScheduledJobCommand.ScriptBlockParameterSet)] - [Parameter(ParameterSetName = SetScheduledJobCommand.FilePathParameterSet)] - public TimeSpan RunEvery - { - get { return _runEvery; } - set { _runEvery = value; } - } - private TimeSpan _runEvery; - - #endregion - - #region Cmdlet Overrides - - /// - /// Process input. - /// - protected override void ProcessRecord() - { - switch (ParameterSetName) - { - case ExecutionParameterSet: - UpdateExecutionDefinition(); - break; - - case ScriptBlockParameterSet: - case FilePathParameterSet: - UpdateDefinition(); - break; - } - - try - { - // If RunEvery parameter is specified then create a job trigger for the definition that - // runs the job at the requested interval. - bool addedTrigger = false; - if (MyInvocation.BoundParameters.ContainsKey("RunEvery")) - { - AddRepetitionJobTriggerToDefinition( - _definition, - RunEvery, - false); - - addedTrigger = true; - } - - if (Trigger != null || ScheduledJobOption != null || Credential != null || addedTrigger) - { - // Save definition to file and update WTS. - _definition.Save(); - } - else - { - // No WTS changes. Save definition to store only. - _definition.SaveToStore(); - } - - if (_runNow) - { - _definition.RunAsTask(); - } - } - catch (ScheduledJobException e) - { - ErrorRecord errorRecord; - - if (e.InnerException != null && - e.InnerException is System.UnauthorizedAccessException) - { - string msg = StringUtil.Format(ScheduledJobErrorStrings.NoAccessOnSetJobDefinition, _definition.Name); - errorRecord = new ErrorRecord(new RuntimeException(msg, e), - "NoAccessFailureOnSetJobDefinition", ErrorCategory.InvalidOperation, _definition); - } - else if (e.InnerException != null && - e.InnerException is System.IO.IOException) - { - string msg = StringUtil.Format(ScheduledJobErrorStrings.IOFailureOnSetJobDefinition, _definition.Name); - errorRecord = new ErrorRecord(new RuntimeException(msg, e), - "IOFailureOnSetJobDefinition", ErrorCategory.InvalidOperation, _definition); - } - else - { - string msg = StringUtil.Format(ScheduledJobErrorStrings.CantSetJobDefinition, _definition.Name); - errorRecord = new ErrorRecord(new RuntimeException(msg, e), - "CantSetPropertiesToScheduledJobDefinition", ErrorCategory.InvalidOperation, _definition); - } - - WriteError(errorRecord); - } - - if (_passThru) - { - WriteObject(_definition); - } - } - - #endregion - - #region Private Methods - - private void UpdateExecutionDefinition() - { - if (_clearExecutionHistory) - { - _definition.ClearExecutionHistory(); - } - } - - private void UpdateDefinition() - { - if (_name != null && - string.Compare(_name, _definition.Name, StringComparison.OrdinalIgnoreCase) != 0) - { - _definition.RenameAndSave(_name); - } - - UpdateJobInvocationInfo(); - - if (MyInvocation.BoundParameters.ContainsKey("MaxResultCount")) - { - _definition.SetExecutionHistoryLength(MaxResultCount, false); - } - - if (Credential != null) - { - _definition.Credential = Credential; - } - - if (Trigger != null) - { - _definition.SetTriggers(Trigger, false); - } - - if (ScheduledJobOption != null) - { - _definition.UpdateOptions(ScheduledJobOption, false); - } - } - - /// - /// Create new ScheduledJobInvocationInfo object with update information and - /// update the job definition object. - /// - private void UpdateJobInvocationInfo() - { - Dictionary parameters = UpdateParameters(); - string name = _definition.Name; - string command; - - if (ScriptBlock != null) - { - command = ScriptBlock.ToString(); - } - else if (FilePath != null) - { - command = FilePath; - } - else - { - command = _definition.InvocationInfo.Command; - } - - JobDefinition jobDefinition = new JobDefinition(typeof(ScheduledJobSourceAdapter), command, name); - jobDefinition.ModuleName = ModuleName; - JobInvocationInfo jobInvocationInfo = new ScheduledJobInvocationInfo(jobDefinition, parameters); - - _definition.UpdateJobInvocationInfo(jobInvocationInfo, false); - } - - /// - /// Creates a new parameter dictionary with update parameters. - /// - /// Updated parameters. - private Dictionary UpdateParameters() - { - Debug.Assert(_definition.InvocationInfo.Parameters.Count != 0, - "ScheduledJobDefinition must always have some job invocation parameters"); - Dictionary newParameters = new Dictionary(); - foreach (CommandParameter parameter in _definition.InvocationInfo.Parameters[0]) - { - newParameters.Add(parameter.Name, parameter.Value); - } - - // RunAs32 - if (MyInvocation.BoundParameters.ContainsKey("RunAs32")) - { - if (newParameters.ContainsKey(ScheduledJobInvocationInfo.RunAs32Parameter)) - { - newParameters[ScheduledJobInvocationInfo.RunAs32Parameter] = RunAs32.ToBool(); - } - else - { - newParameters.Add(ScheduledJobInvocationInfo.RunAs32Parameter, RunAs32.ToBool()); - } - } - - // Authentication - if (MyInvocation.BoundParameters.ContainsKey("Authentication")) - { - if (newParameters.ContainsKey(ScheduledJobInvocationInfo.AuthenticationParameter)) - { - newParameters[ScheduledJobInvocationInfo.AuthenticationParameter] = Authentication; - } - else - { - newParameters.Add(ScheduledJobInvocationInfo.AuthenticationParameter, Authentication); - } - } - - // InitializationScript - if (InitializationScript == null) - { - if (newParameters.ContainsKey(ScheduledJobInvocationInfo.InitializationScriptParameter)) - { - newParameters.Remove(ScheduledJobInvocationInfo.InitializationScriptParameter); - } - } - else - { - if (newParameters.ContainsKey(ScheduledJobInvocationInfo.InitializationScriptParameter)) - { - newParameters[ScheduledJobInvocationInfo.InitializationScriptParameter] = InitializationScript; - } - else - { - newParameters.Add(ScheduledJobInvocationInfo.InitializationScriptParameter, InitializationScript); - } - } - - // ScriptBlock - if (ScriptBlock != null) - { - // FilePath cannot also be specified. - if (newParameters.ContainsKey(ScheduledJobInvocationInfo.FilePathParameter)) - { - newParameters.Remove(ScheduledJobInvocationInfo.FilePathParameter); - } - - if (newParameters.ContainsKey(ScheduledJobInvocationInfo.ScriptBlockParameter)) - { - newParameters[ScheduledJobInvocationInfo.ScriptBlockParameter] = ScriptBlock; - } - else - { - newParameters.Add(ScheduledJobInvocationInfo.ScriptBlockParameter, ScriptBlock); - } - } - - // FilePath - if (FilePath != null) - { - // ScriptBlock cannot also be specified. - if (newParameters.ContainsKey(ScheduledJobInvocationInfo.ScriptBlockParameter)) - { - newParameters.Remove(ScheduledJobInvocationInfo.ScriptBlockParameter); - } - - if (newParameters.ContainsKey(ScheduledJobInvocationInfo.FilePathParameter)) - { - newParameters[ScheduledJobInvocationInfo.FilePathParameter] = FilePath; - } - else - { - newParameters.Add(ScheduledJobInvocationInfo.FilePathParameter, FilePath); - } - } - - // ArgumentList - if (ArgumentList == null) - { - // Clear existing argument list only if new scriptblock or script file path was specified - // (in this case old argument list is invalid). - if (newParameters.ContainsKey(ScheduledJobInvocationInfo.ArgumentListParameter) && - (ScriptBlock != null || FilePath != null)) - { - newParameters.Remove(ScheduledJobInvocationInfo.ArgumentListParameter); - } - } - else - { - if (newParameters.ContainsKey(ScheduledJobInvocationInfo.ArgumentListParameter)) - { - newParameters[ScheduledJobInvocationInfo.ArgumentListParameter] = ArgumentList; - } - else - { - newParameters.Add(ScheduledJobInvocationInfo.ArgumentListParameter, ArgumentList); - } - } - - return newParameters; - } - - #endregion - } -} diff --git a/src/Microsoft.PowerShell.ScheduledJob/commands/SetJobTrigger.cs b/src/Microsoft.PowerShell.ScheduledJob/commands/SetJobTrigger.cs deleted file mode 100644 index cea8cf487ce..00000000000 --- a/src/Microsoft.PowerShell.ScheduledJob/commands/SetJobTrigger.cs +++ /dev/null @@ -1,900 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Diagnostics.CodeAnalysis; -using System.Management.Automation; -using System.Diagnostics; - -namespace Microsoft.PowerShell.ScheduledJob -{ - /// - /// This cmdlet sets properties on a trigger for a ScheduledJobDefinition. - /// - [Cmdlet(VerbsCommon.Set, "JobTrigger", DefaultParameterSetName = SetJobTriggerCommand.DefaultParameterSet, - HelpUri = "https://go.microsoft.com/fwlink/?LinkID=223916")] - [OutputType(typeof(ScheduledJobTrigger))] - public sealed class SetJobTriggerCommand : ScheduleJobCmdletBase - { - #region Parameters - - private const string DefaultParameterSet = "DefaultParams"; - - /// - /// ScheduledJobTrigger objects to set properties on. - /// - [Parameter(Position = 0, Mandatory = true, ValueFromPipeline = true, - ParameterSetName = SetJobTriggerCommand.DefaultParameterSet)] - [ValidateNotNullOrEmpty] - [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] - public ScheduledJobTrigger[] InputObject - { - get { return _triggers; } - set { _triggers = value; } - } - private ScheduledJobTrigger[] _triggers; - - /// - /// Daily interval for trigger. - /// - [Parameter(ParameterSetName = SetJobTriggerCommand.DefaultParameterSet)] - public Int32 DaysInterval - { - get { return _daysInterval; } - set { _daysInterval = value; } - } - private Int32 _daysInterval = 1; - - /// - /// Weekly interval for trigger. - /// - [Parameter(ParameterSetName = SetJobTriggerCommand.DefaultParameterSet)] - public Int32 WeeksInterval - { - get { return _weeksInterval; } - set { _weeksInterval = value; } - } - private Int32 _weeksInterval = 1; - - /// - /// Random delay for trigger. - /// - [Parameter(ParameterSetName = SetJobTriggerCommand.DefaultParameterSet)] - public TimeSpan RandomDelay - { - get { return _randomDelay; } - set { _randomDelay = value; } - } - private TimeSpan _randomDelay; - - /// - /// Job start date/time for trigger. - /// - [Parameter(ParameterSetName = SetJobTriggerCommand.DefaultParameterSet)] - public DateTime At - { - get { return _atTime; } - set { _atTime = value; } - } - private DateTime _atTime; - - /// - /// User name for AtLogon trigger. The AtLogon parameter set will create a trigger - /// that activates after log on for the provided user name. - /// - [Parameter(ParameterSetName = SetJobTriggerCommand.DefaultParameterSet)] - [ValidateNotNullOrEmpty] - public string User - { - get { return _user; } - set { _user = value; } - } - private string _user; - - /// - /// Days of week for trigger. - /// - [Parameter(ParameterSetName = SetJobTriggerCommand.DefaultParameterSet)] - [ValidateNotNullOrEmpty] - [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] - public DayOfWeek[] DaysOfWeek - { - get { return _daysOfWeek; } - set { _daysOfWeek = value; } - } - private DayOfWeek[] _daysOfWeek; - - /// - /// Switch to specify an AtStartup trigger. - /// - [Parameter(ParameterSetName = SetJobTriggerCommand.DefaultParameterSet)] - public SwitchParameter AtStartup - { - get { return _atStartup; } - set { _atStartup = value; } - } - private SwitchParameter _atStartup; - - /// - /// Switch to specify an AtLogon trigger. - /// - [Parameter(ParameterSetName = SetJobTriggerCommand.DefaultParameterSet)] - public SwitchParameter AtLogOn - { - get { return _atLogon; } - set { _atLogon = value; } - } - private SwitchParameter _atLogon; - - /// - /// Switch to specify an Once trigger. - /// - [Parameter(ParameterSetName = SetJobTriggerCommand.DefaultParameterSet)] - public SwitchParameter Once - { - get { return _once; } - set { _once = value; } - } - private SwitchParameter _once; - - /// - /// Repetition interval of a one time trigger. - /// - [Parameter(ParameterSetName = SetJobTriggerCommand.DefaultParameterSet)] - public TimeSpan RepetitionInterval - { - get { return _repInterval; } - set { _repInterval = value; } - } - private TimeSpan _repInterval; - - /// - /// Repetition duration of a one time trigger. - /// - [Parameter(ParameterSetName = SetJobTriggerCommand.DefaultParameterSet)] - public TimeSpan RepetitionDuration - { - get { return _repDuration; } - set { _repDuration = value; } - } - private TimeSpan _repDuration; - - /// - /// Repetition interval repeats indefinitely. - /// - [Parameter(ParameterSetName = SetJobTriggerCommand.DefaultParameterSet)] - public SwitchParameter RepeatIndefinitely - { - get { return _repRepeatIndefinitely; } - set { _repRepeatIndefinitely = value; } - } - private SwitchParameter _repRepeatIndefinitely; - - /// - /// Switch to specify an Daily trigger. - /// - [Parameter(ParameterSetName = SetJobTriggerCommand.DefaultParameterSet)] - public SwitchParameter Daily - { - get { return _daily; } - set { _daily = value; } - } - private SwitchParameter _daily; - - /// - /// Switch to specify an Weekly trigger. - /// - [Parameter(ParameterSetName = SetJobTriggerCommand.DefaultParameterSet)] - public SwitchParameter Weekly - { - get { return _weekly; } - set { _weekly = value; } - } - private SwitchParameter _weekly; - - /// - /// Pass through job trigger object. - /// - [Parameter(ParameterSetName = SetJobTriggerCommand.DefaultParameterSet)] - public SwitchParameter PassThru - { - get { return _passThru; } - set { _passThru = value; } - } - private SwitchParameter _passThru; - - #endregion - - #region Cmdlet Overrides - - /// - /// Process input. - /// - protected override void ProcessRecord() - { - // Validate the parameter set and write any errors. - TriggerFrequency newTriggerFrequency = TriggerFrequency.None; - if (!ValidateParameterSet(ref newTriggerFrequency)) - { - return; - } - - // Update each trigger object with the current parameter set. - // The associated scheduled job definition will also be updated. - foreach (ScheduledJobTrigger trigger in _triggers) - { - ScheduledJobTrigger originalTrigger = new ScheduledJobTrigger(trigger); - if (!UpdateTrigger(trigger, newTriggerFrequency)) - { - continue; - } - - ScheduledJobDefinition definition = trigger.JobDefinition; - if (definition != null) - { - bool jobUpdateFailed = false; - - try - { - trigger.UpdateJobDefinition(); - } - catch (ScheduledJobException e) - { - jobUpdateFailed = true; - - string msg = StringUtil.Format(ScheduledJobErrorStrings.CantUpdateTriggerOnJobDef, definition.Name, trigger.Id); - Exception reason = new RuntimeException(msg, e); - ErrorRecord errorRecord = new ErrorRecord(reason, "CantSetPropertiesOnJobTrigger", ErrorCategory.InvalidOperation, trigger); - WriteError(errorRecord); - } - - if (jobUpdateFailed) - { - // Restore trigger to original configuration. - originalTrigger.CopyTo(trigger); - } - } - - if (_passThru) - { - WriteObject(trigger); - } - } - } - - #endregion - - #region Private Methods - - private bool ValidateParameterSet(ref TriggerFrequency newTriggerFrequency) - { - // First see if a switch parameter was set. - List switchParamList = new List(); - if (MyInvocation.BoundParameters.ContainsKey(_paramAtStartup)) - { - switchParamList.Add(TriggerFrequency.AtStartup); - } - if (MyInvocation.BoundParameters.ContainsKey(_paramAtLogon)) - { - switchParamList.Add(TriggerFrequency.AtLogon); - } - if (MyInvocation.BoundParameters.ContainsKey(_paramOnce)) - { - switchParamList.Add(TriggerFrequency.Once); - } - if (MyInvocation.BoundParameters.ContainsKey(_paramDaily)) - { - switchParamList.Add(TriggerFrequency.Daily); - } - if (MyInvocation.BoundParameters.ContainsKey(_paramWeekly)) - { - switchParamList.Add(TriggerFrequency.Weekly); - } - if (switchParamList.Count > 1) - { - WriteValidationError(ScheduledJobErrorStrings.ConflictingTypeParams); - return false; - } - newTriggerFrequency = (switchParamList.Count == 1) ? switchParamList[0] : TriggerFrequency.None; - - // Validate parameters against the new trigger frequency value. - bool rtnValue = false; - switch (newTriggerFrequency) - { - case TriggerFrequency.None: - rtnValue = true; - break; - - case TriggerFrequency.AtStartup: - rtnValue = ValidateStartupParams(); - break; - - case TriggerFrequency.AtLogon: - rtnValue = ValidateLogonParams(); - break; - - case TriggerFrequency.Once: - rtnValue = ValidateOnceParams(); - break; - - case TriggerFrequency.Daily: - rtnValue = ValidateDailyParams(); - break; - - case TriggerFrequency.Weekly: - rtnValue = ValidateWeeklyParams(); - break; - - default: - Debug.Assert(false, "Invalid trigger frequency value."); - rtnValue = false; - break; - } - - return rtnValue; - } - - private bool ValidateStartupParams() - { - if (MyInvocation.BoundParameters.ContainsKey(_paramDaysInterval)) - { - string msg = StringUtil.Format(ScheduledJobErrorStrings.InvalidDaysInterval, ScheduledJobErrorStrings.TriggerStartUpType); - WriteValidationError(msg); - return false; - } - if (MyInvocation.BoundParameters.ContainsKey(_paramWeeksInterval)) - { - string msg = StringUtil.Format(ScheduledJobErrorStrings.InvalidWeeksInterval, ScheduledJobErrorStrings.TriggerStartUpType); - WriteValidationError(msg); - return false; - } - if (MyInvocation.BoundParameters.ContainsKey(_paramAt)) - { - string msg = StringUtil.Format(ScheduledJobErrorStrings.InvalidAtTime, ScheduledJobErrorStrings.TriggerStartUpType); - WriteValidationError(msg); - return false; - } - if (MyInvocation.BoundParameters.ContainsKey(_paramUser)) - { - string msg = StringUtil.Format(ScheduledJobErrorStrings.InvalidUser, ScheduledJobErrorStrings.TriggerStartUpType); - WriteValidationError(msg); - return false; - } - if (MyInvocation.BoundParameters.ContainsKey(_paramDaysOfWeek)) - { - string msg = StringUtil.Format(ScheduledJobErrorStrings.InvalidDaysOfWeek, ScheduledJobErrorStrings.TriggerStartUpType); - WriteValidationError(msg); - return false; - } - if (MyInvocation.BoundParameters.ContainsKey(_paramRepetitionInterval) || MyInvocation.BoundParameters.ContainsKey(_paramRepetitionDuration) || - MyInvocation.BoundParameters.ContainsKey(_paramRepetitionInfiniteDuration)) - { - string msg = StringUtil.Format(ScheduledJobErrorStrings.InvalidSetTriggerRepetition, ScheduledJobErrorStrings.TriggerStartUpType); - WriteValidationError(msg); - return false; - } - - return true; - } - - private bool ValidateLogonParams() - { - if (MyInvocation.BoundParameters.ContainsKey(_paramDaysInterval)) - { - string msg = StringUtil.Format(ScheduledJobErrorStrings.InvalidDaysInterval, ScheduledJobErrorStrings.TriggerLogonType); - WriteValidationError(msg); - return false; - } - if (MyInvocation.BoundParameters.ContainsKey(_paramWeeksInterval)) - { - string msg = StringUtil.Format(ScheduledJobErrorStrings.InvalidWeeksInterval, ScheduledJobErrorStrings.TriggerLogonType); - WriteValidationError(msg); - return false; - } - if (MyInvocation.BoundParameters.ContainsKey(_paramAt)) - { - string msg = StringUtil.Format(ScheduledJobErrorStrings.InvalidAtTime, ScheduledJobErrorStrings.TriggerLogonType); - WriteValidationError(msg); - return false; - } - if (MyInvocation.BoundParameters.ContainsKey(_paramDaysOfWeek)) - { - string msg = StringUtil.Format(ScheduledJobErrorStrings.InvalidDaysOfWeek, ScheduledJobErrorStrings.TriggerLogonType); - WriteValidationError(msg); - return false; - } - if (MyInvocation.BoundParameters.ContainsKey(_paramRepetitionInterval) || MyInvocation.BoundParameters.ContainsKey(_paramRepetitionDuration) || - MyInvocation.BoundParameters.ContainsKey(_paramRepetitionInfiniteDuration)) - { - string msg = StringUtil.Format(ScheduledJobErrorStrings.InvalidSetTriggerRepetition, ScheduledJobErrorStrings.TriggerLogonType); - WriteValidationError(msg); - return false; - } - - return true; - } - - private bool ValidateOnceParams(ScheduledJobTrigger trigger = null) - { - if (MyInvocation.BoundParameters.ContainsKey(_paramDaysInterval)) - { - string msg = StringUtil.Format(ScheduledJobErrorStrings.InvalidDaysInterval, ScheduledJobErrorStrings.TriggerOnceType); - WriteValidationError(msg); - return false; - } - if (MyInvocation.BoundParameters.ContainsKey(_paramWeeksInterval)) - { - string msg = StringUtil.Format(ScheduledJobErrorStrings.InvalidWeeksInterval, ScheduledJobErrorStrings.TriggerOnceType); - WriteValidationError(msg); - return false; - } - if (MyInvocation.BoundParameters.ContainsKey(_paramUser)) - { - string msg = StringUtil.Format(ScheduledJobErrorStrings.InvalidUser, ScheduledJobErrorStrings.TriggerOnceType); - WriteValidationError(msg); - return false; - } - if (MyInvocation.BoundParameters.ContainsKey(_paramDaysOfWeek)) - { - string msg = StringUtil.Format(ScheduledJobErrorStrings.InvalidDaysOfWeek, ScheduledJobErrorStrings.TriggerOnceType); - WriteValidationError(msg); - return false; - } - - if (MyInvocation.BoundParameters.ContainsKey(_paramRepetitionInfiniteDuration)) - { - _repDuration = TimeSpan.MaxValue; - } - - if (MyInvocation.BoundParameters.ContainsKey(_paramRepetitionInterval) || MyInvocation.BoundParameters.ContainsKey(_paramRepetitionDuration) || - MyInvocation.BoundParameters.ContainsKey(_paramRepetitionInfiniteDuration)) - { - // Validate Once trigger repetition parameters. - try - { - ScheduledJobTrigger.ValidateOnceRepetitionParams(_repInterval, _repDuration); - } - catch (PSArgumentException e) - { - WriteValidationError(e.Message); - return false; - } - } - - if (trigger != null) - { - if (trigger.At == null && !MyInvocation.BoundParameters.ContainsKey(_paramAt)) - { - string msg = StringUtil.Format(ScheduledJobErrorStrings.MissingAtTime, ScheduledJobErrorStrings.TriggerOnceType); - WriteValidationError(msg); - return false; - } - } - - return true; - } - - private bool ValidateDailyParams(ScheduledJobTrigger trigger = null) - { - if (MyInvocation.BoundParameters.ContainsKey(_paramDaysInterval) && - _daysInterval < 1) - { - WriteValidationError(ScheduledJobErrorStrings.InvalidDaysIntervalParam); - return false; - } - if (MyInvocation.BoundParameters.ContainsKey(_paramWeeksInterval)) - { - string msg = StringUtil.Format(ScheduledJobErrorStrings.InvalidWeeksInterval, ScheduledJobErrorStrings.TriggerDailyType); - WriteValidationError(msg); - return false; - } - if (MyInvocation.BoundParameters.ContainsKey(_paramUser)) - { - string msg = StringUtil.Format(ScheduledJobErrorStrings.InvalidUser, ScheduledJobErrorStrings.TriggerDailyType); - WriteValidationError(msg); - return false; - } - if (MyInvocation.BoundParameters.ContainsKey(_paramDaysOfWeek)) - { - string msg = StringUtil.Format(ScheduledJobErrorStrings.InvalidDaysOfWeek, ScheduledJobErrorStrings.TriggerDailyType); - WriteValidationError(msg); - return false; - } - if (MyInvocation.BoundParameters.ContainsKey(_paramRepetitionInterval) || MyInvocation.BoundParameters.ContainsKey(_paramRepetitionDuration) || - MyInvocation.BoundParameters.ContainsKey(_paramRepetitionInfiniteDuration)) - { - string msg = StringUtil.Format(ScheduledJobErrorStrings.InvalidSetTriggerRepetition, ScheduledJobErrorStrings.TriggerDailyType); - WriteValidationError(msg); - return false; - } - - if (trigger != null) - { - if (trigger.At == null && !MyInvocation.BoundParameters.ContainsKey(_paramAt)) - { - string msg = StringUtil.Format(ScheduledJobErrorStrings.MissingAtTime, ScheduledJobErrorStrings.TriggerDailyType); - WriteValidationError(msg); - return false; - } - } - - return true; - } - - private bool ValidateWeeklyParams(ScheduledJobTrigger trigger = null) - { - if (MyInvocation.BoundParameters.ContainsKey(_paramDaysInterval)) - { - string msg = StringUtil.Format(ScheduledJobErrorStrings.InvalidDaysInterval, ScheduledJobErrorStrings.TriggerWeeklyType); - WriteValidationError(msg); - return false; - } - if (MyInvocation.BoundParameters.ContainsKey(_paramWeeksInterval) && - _weeksInterval < 1) - { - WriteValidationError(ScheduledJobErrorStrings.InvalidWeeksIntervalParam); - return false; - } - if (MyInvocation.BoundParameters.ContainsKey(_paramUser)) - { - string msg = StringUtil.Format(ScheduledJobErrorStrings.InvalidUser, ScheduledJobErrorStrings.TriggerWeeklyType); - WriteValidationError(msg); - return false; - } - if (MyInvocation.BoundParameters.ContainsKey(_paramRepetitionInterval) || MyInvocation.BoundParameters.ContainsKey(_paramRepetitionDuration) || - MyInvocation.BoundParameters.ContainsKey(_paramRepetitionInfiniteDuration)) - { - string msg = StringUtil.Format(ScheduledJobErrorStrings.InvalidSetTriggerRepetition, ScheduledJobErrorStrings.TriggerWeeklyType); - WriteValidationError(msg); - return false; - } - - if (trigger != null) - { - if (trigger.At == null && !MyInvocation.BoundParameters.ContainsKey(_paramAt)) - { - string msg = StringUtil.Format(ScheduledJobErrorStrings.MissingAtTime, ScheduledJobErrorStrings.TriggerDailyType); - WriteValidationError(msg); - return false; - } - if ((trigger.DaysOfWeek == null || trigger.DaysOfWeek.Count == 0) && - !MyInvocation.BoundParameters.ContainsKey(_paramDaysOfWeek)) - { - string msg = StringUtil.Format(ScheduledJobErrorStrings.MissingDaysOfWeek, ScheduledJobErrorStrings.TriggerDailyType); - WriteValidationError(msg); - return false; - } - } - - return true; - } - - private bool UpdateTrigger(ScheduledJobTrigger trigger, TriggerFrequency triggerFrequency) - { - if (triggerFrequency != TriggerFrequency.None) - { - // - // User has specified a specific trigger type. - // Parameters have been validated for this trigger type. - // - if (triggerFrequency != trigger.Frequency) - { - // Changing to a new trigger type. - return CreateTrigger(trigger, triggerFrequency); - } - else - { - // Modifying existing trigger type. - return ModifyTrigger(trigger, triggerFrequency); - } - } - else - { - // We are updating an existing trigger. Need to validate params - // against each trigger type we are updating. - return ModifyTrigger(trigger, trigger.Frequency, true); - } - } - - private bool CreateTrigger(ScheduledJobTrigger trigger, TriggerFrequency triggerFrequency) - { - switch (triggerFrequency) - { - case TriggerFrequency.AtStartup: - CreateAtStartupTrigger(trigger); - break; - - case TriggerFrequency.AtLogon: - CreateAtLogonTrigger(trigger); - break; - - case TriggerFrequency.Once: - if (trigger.Frequency != triggerFrequency && - !ValidateOnceParams(trigger)) - { - return false; - } - CreateOnceTrigger(trigger); - break; - - case TriggerFrequency.Daily: - if (trigger.Frequency != triggerFrequency && - !ValidateDailyParams(trigger)) - { - return false; - } - CreateDailyTrigger(trigger); - break; - - case TriggerFrequency.Weekly: - if (trigger.Frequency != triggerFrequency && - !ValidateWeeklyParams(trigger)) - { - return false; - } - CreateWeeklyTrigger(trigger); - break; - } - - return true; - } - - private bool ModifyTrigger(ScheduledJobTrigger trigger, TriggerFrequency triggerFrequency, bool validate = false) - { - switch (triggerFrequency) - { - case TriggerFrequency.AtStartup: - if (validate && - !ValidateStartupParams()) - { - return false; - } - ModifyStartupTrigger(trigger); - break; - - case TriggerFrequency.AtLogon: - if (validate && - !ValidateLogonParams()) - { - return false; - } - ModifyLogonTrigger(trigger); - break; - - case TriggerFrequency.Once: - if (validate && - !ValidateOnceParams()) - { - return false; - } - ModifyOnceTrigger(trigger); - break; - - case TriggerFrequency.Daily: - if (validate && - !ValidateDailyParams()) - { - return false; - } - ModifyDailyTrigger(trigger); - break; - - case TriggerFrequency.Weekly: - if (validate && - !ValidateWeeklyParams()) - { - return false; - } - ModifyWeeklyTrigger(trigger); - break; - } - - return true; - } - - private void ModifyStartupTrigger(ScheduledJobTrigger trigger) - { - if (MyInvocation.BoundParameters.ContainsKey(_paramRandomDelay)) - { - trigger.RandomDelay = _randomDelay; - } - } - - private void ModifyLogonTrigger(ScheduledJobTrigger trigger) - { - if (MyInvocation.BoundParameters.ContainsKey(_paramRandomDelay)) - { - trigger.RandomDelay = _randomDelay; - } - - if (MyInvocation.BoundParameters.ContainsKey(_paramUser)) - { - trigger.User = string.IsNullOrEmpty(_user) ? ScheduledJobTrigger.AllUsers : _user; - } - } - - private void ModifyOnceTrigger(ScheduledJobTrigger trigger) - { - if (MyInvocation.BoundParameters.ContainsKey(_paramRandomDelay)) - { - trigger.RandomDelay = _randomDelay; - } - - if (MyInvocation.BoundParameters.ContainsKey(_paramRepetitionInterval)) - { - trigger.RepetitionInterval = _repInterval; - } - - if (MyInvocation.BoundParameters.ContainsKey(_paramRepetitionDuration)) - { - trigger.RepetitionDuration = _repDuration; - } - - if (MyInvocation.BoundParameters.ContainsKey(_paramAt)) - { - trigger.At = _atTime; - } - } - - private void ModifyDailyTrigger(ScheduledJobTrigger trigger) - { - if (MyInvocation.BoundParameters.ContainsKey(_paramRandomDelay)) - { - trigger.RandomDelay = _randomDelay; - } - - if (MyInvocation.BoundParameters.ContainsKey(_paramAt)) - { - trigger.At = _atTime; - } - - if (MyInvocation.BoundParameters.ContainsKey(_paramDaysInterval)) - { - trigger.Interval = _daysInterval; - } - } - - private void ModifyWeeklyTrigger(ScheduledJobTrigger trigger) - { - if (MyInvocation.BoundParameters.ContainsKey(_paramRandomDelay)) - { - trigger.RandomDelay = _randomDelay; - } - - if (MyInvocation.BoundParameters.ContainsKey(_paramAt)) - { - trigger.At = _atTime; - } - - if (MyInvocation.BoundParameters.ContainsKey(_paramWeeksInterval)) - { - trigger.Interval = _weeksInterval; - } - - if (MyInvocation.BoundParameters.ContainsKey(_paramDaysOfWeek)) - { - trigger.DaysOfWeek = new List(_daysOfWeek); - } - } - - private void CreateAtLogonTrigger(ScheduledJobTrigger trigger) - { - bool enabled = trigger.Enabled; - int id = trigger.Id; - TimeSpan randomDelay = trigger.RandomDelay; - string user = string.IsNullOrEmpty(trigger.User) ? ScheduledJobTrigger.AllUsers : trigger.User; - - trigger.ClearProperties(); - trigger.Frequency = TriggerFrequency.AtLogon; - trigger.Enabled = enabled; - trigger.Id = id; - - trigger.RandomDelay = MyInvocation.BoundParameters.ContainsKey(_paramRandomDelay) ? _randomDelay : randomDelay; - trigger.User = MyInvocation.BoundParameters.ContainsKey(_paramUser) ? _user : user; - } - - private void CreateAtStartupTrigger(ScheduledJobTrigger trigger) - { - bool enabled = trigger.Enabled; - int id = trigger.Id; - TimeSpan randomDelay = trigger.RandomDelay; - - trigger.ClearProperties(); - trigger.Frequency = TriggerFrequency.AtStartup; - trigger.Enabled = enabled; - trigger.Id = id; - - trigger.RandomDelay = MyInvocation.BoundParameters.ContainsKey(_paramRandomDelay) ? _randomDelay : randomDelay; - } - - private void CreateOnceTrigger(ScheduledJobTrigger trigger) - { - bool enabled = trigger.Enabled; - int id = trigger.Id; - TimeSpan randomDelay = trigger.RandomDelay; - DateTime? atTime = trigger.At; - TimeSpan? repInterval = trigger.RepetitionInterval; - TimeSpan? repDuration = trigger.RepetitionDuration; - - trigger.ClearProperties(); - trigger.Frequency = TriggerFrequency.Once; - trigger.Enabled = enabled; - trigger.Id = id; - - trigger.RandomDelay = MyInvocation.BoundParameters.ContainsKey(_paramRandomDelay) ? _randomDelay : randomDelay; - trigger.At = MyInvocation.BoundParameters.ContainsKey(_paramAt) ? _atTime : atTime; - trigger.RepetitionInterval = MyInvocation.BoundParameters.ContainsKey(_paramRepetitionInterval) ? _repInterval : repInterval; - trigger.RepetitionDuration = MyInvocation.BoundParameters.ContainsKey(_paramRepetitionDuration) ? _repDuration : repDuration; - } - - private void CreateDailyTrigger(ScheduledJobTrigger trigger) - { - bool enabled = trigger.Enabled; - int id = trigger.Id; - TimeSpan randomDelay = trigger.RandomDelay; - DateTime? atTime = trigger.At; - int interval = trigger.Interval; - - trigger.ClearProperties(); - trigger.Frequency = TriggerFrequency.Daily; - trigger.Enabled = enabled; - trigger.Id = id; - - trigger.RandomDelay = MyInvocation.BoundParameters.ContainsKey(_paramRandomDelay) ? _randomDelay : randomDelay; - trigger.At = MyInvocation.BoundParameters.ContainsKey(_paramAt) ? _atTime : atTime; - trigger.Interval = MyInvocation.BoundParameters.ContainsKey(_paramDaysInterval) ? _daysInterval : interval; - } - - private void CreateWeeklyTrigger(ScheduledJobTrigger trigger) - { - bool enabled = trigger.Enabled; - int id = trigger.Id; - TimeSpan randomDelay = trigger.RandomDelay; - DateTime? atTime = trigger.At; - int interval = trigger.Interval; - List daysOfWeek = trigger.DaysOfWeek; - - trigger.ClearProperties(); - trigger.Frequency = TriggerFrequency.Weekly; - trigger.Enabled = enabled; - trigger.Id = id; - - trigger.RandomDelay = MyInvocation.BoundParameters.ContainsKey(_paramRandomDelay) ? _randomDelay : randomDelay; - trigger.At = MyInvocation.BoundParameters.ContainsKey(_paramAt) ? _atTime : atTime; - trigger.Interval = MyInvocation.BoundParameters.ContainsKey(_paramWeeksInterval) ? _weeksInterval : interval; - trigger.DaysOfWeek = MyInvocation.BoundParameters.ContainsKey(_paramDaysOfWeek) ? new List(_daysOfWeek) : daysOfWeek; - } - - private void WriteValidationError(string msg) - { - Exception reason = new RuntimeException(msg); - ErrorRecord errorRecord = new ErrorRecord(reason, "SetJobTriggerParameterValidationError", ErrorCategory.InvalidArgument, null); - WriteError(errorRecord); - } - - #endregion - - #region Private Members - - private string _paramAtStartup = "AtStartup"; - private string _paramAtLogon = "AtLogon"; - private string _paramOnce = "Once"; - private string _paramDaily = "Daily"; - private string _paramWeekly = "Weekly"; - // - private string _paramDaysInterval = "DaysInterval"; - private string _paramWeeksInterval = "WeeksInterval"; - private string _paramRandomDelay = "RandomDelay"; - private string _paramRepetitionInterval = "RepetitionInterval"; - private string _paramRepetitionDuration = "RepetitionDuration"; - private string _paramRepetitionInfiniteDuration = "RepeatIndefinitely"; - private string _paramAt = "At"; - private string _paramUser = "User"; - private string _paramDaysOfWeek = "DaysOfWeek"; - - #endregion - } -} diff --git a/src/Microsoft.PowerShell.ScheduledJob/commands/SetScheduledJobOption.cs b/src/Microsoft.PowerShell.ScheduledJob/commands/SetScheduledJobOption.cs deleted file mode 100644 index 917d6c187f4..00000000000 --- a/src/Microsoft.PowerShell.ScheduledJob/commands/SetScheduledJobOption.cs +++ /dev/null @@ -1,136 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -using System; -using System.Collections.Generic; -using System.Management.Automation; - -namespace Microsoft.PowerShell.ScheduledJob -{ - /// - /// This cmdlet sets the provided scheduled job options to the provided ScheduledJobOptions objects. - /// - [Cmdlet(VerbsCommon.Set, "ScheduledJobOption", DefaultParameterSetName = ScheduledJobOptionCmdletBase.OptionsParameterSet, - HelpUri = "https://go.microsoft.com/fwlink/?LinkID=223921")] - [OutputType(typeof(ScheduledJobOptions))] - public class SetScheduledJobOptionCommand : ScheduledJobOptionCmdletBase - { - #region Parameters - - /// - /// ScheduledJobOptions object. - /// - [Parameter(Position = 0, Mandatory = true, ValueFromPipeline = true, - ParameterSetName = ScheduledJobOptionCmdletBase.OptionsParameterSet)] - [ValidateNotNull] - public ScheduledJobOptions InputObject - { - get { return _jobOptions; } - set { _jobOptions = value; } - } - private ScheduledJobOptions _jobOptions; - - /// - /// Pas the ScheduledJobOptions object through to output. - /// - [Parameter(ParameterSetName = ScheduledJobOptionCmdletBase.OptionsParameterSet)] - public SwitchParameter PassThru - { - get { return _passThru; } - set { _passThru = value; } - } - private SwitchParameter _passThru; - - #endregion - - #region Cmdlet Overrides - - /// - /// Process input. - /// - protected override void ProcessRecord() - { - // Update ScheduledJobOptions object with current parameters. - // Update switch parameters only if they were selected. - // Also update the ScheduledJobDefinition object associated with this options object. - if (MyInvocation.BoundParameters.ContainsKey("StartIfOnBattery")) - { - _jobOptions.StartIfOnBatteries = StartIfOnBattery; - } - - if (MyInvocation.BoundParameters.ContainsKey("ContinueIfGoingOnBattery")) - { - _jobOptions.StopIfGoingOnBatteries = !ContinueIfGoingOnBattery; - } - - if (MyInvocation.BoundParameters.ContainsKey("WakeToRun")) - { - _jobOptions.WakeToRun = WakeToRun; - } - - if (MyInvocation.BoundParameters.ContainsKey("StartIfIdle")) - { - _jobOptions.StartIfNotIdle = !StartIfIdle; - } - - if (MyInvocation.BoundParameters.ContainsKey("StopIfGoingOffIdle")) - { - _jobOptions.StopIfGoingOffIdle = StopIfGoingOffIdle; - } - - if (MyInvocation.BoundParameters.ContainsKey("RestartOnIdleResume")) - { - _jobOptions.RestartOnIdleResume = RestartOnIdleResume; - } - - if (MyInvocation.BoundParameters.ContainsKey("HideInTaskScheduler")) - { - _jobOptions.ShowInTaskScheduler = !HideInTaskScheduler; - } - - if (MyInvocation.BoundParameters.ContainsKey("RunElevated")) - { - _jobOptions.RunElevated = RunElevated; - } - - if (MyInvocation.BoundParameters.ContainsKey("RequireNetwork")) - { - _jobOptions.RunWithoutNetwork = !RequireNetwork; - } - - if (MyInvocation.BoundParameters.ContainsKey("DoNotAllowDemandStart")) - { - _jobOptions.DoNotAllowDemandStart = DoNotAllowDemandStart; - } - - if (MyInvocation.BoundParameters.ContainsKey("IdleDuration")) - { - _jobOptions.IdleDuration = IdleDuration; - } - - if (MyInvocation.BoundParameters.ContainsKey("IdleTimeout")) - { - _jobOptions.IdleTimeout = IdleTimeout; - } - - if (MyInvocation.BoundParameters.ContainsKey("MultipleInstancePolicy")) - { - _jobOptions.MultipleInstancePolicy = MultipleInstancePolicy; - } - - // Update ScheduledJobDefinition with changes. - if (_jobOptions.JobDefinition != null) - { - _jobOptions.UpdateJobDefinition(); - } - - if (_passThru) - { - WriteObject(_jobOptions); - } - } - - #endregion - } -} diff --git a/src/Microsoft.PowerShell.ScheduledJob/commands/UnregisterJobDefinition.cs b/src/Microsoft.PowerShell.ScheduledJob/commands/UnregisterJobDefinition.cs deleted file mode 100644 index 2f85e7d3eb9..00000000000 --- a/src/Microsoft.PowerShell.ScheduledJob/commands/UnregisterJobDefinition.cs +++ /dev/null @@ -1,150 +0,0 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ - -using System; -using System.Collections.Generic; -using System.Management.Automation; -using System.Diagnostics.CodeAnalysis; - -namespace Microsoft.PowerShell.ScheduledJob -{ - /// - /// This cmdlet removes the specified ScheduledJobDefinition objects from the - /// Task Scheduler, job store, and local repository. - /// - [Cmdlet(VerbsLifecycle.Unregister, "ScheduledJob", SupportsShouldProcess = true, DefaultParameterSetName = UnregisterScheduledJobCommand.DefinitionParameterSet, - HelpUri = "https://go.microsoft.com/fwlink/?LinkID=223925")] - public sealed class UnregisterScheduledJobCommand : ScheduleJobCmdletBase - { - #region Parameters - - private const string DefinitionIdParameterSet = "DefinitionId"; - private const string DefinitionNameParameterSet = "DefinitionName"; - private const string DefinitionParameterSet = "Definition"; - - /// - /// ScheduledJobDefinition Id. - /// - [Parameter(Position = 0, Mandatory = true, - ParameterSetName = UnregisterScheduledJobCommand.DefinitionIdParameterSet)] - [ValidateNotNullOrEmpty] - [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] - public Int32[] Id - { - get { return _definitionIds; } - set { _definitionIds = value; } - } - private Int32[] _definitionIds; - - /// - /// ScheduledJobDefinition Name. - /// - [Parameter(Position = 0, Mandatory = true, - ParameterSetName = UnregisterScheduledJobCommand.DefinitionNameParameterSet)] - [ValidateNotNullOrEmpty] - [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] - public string[] Name - { - get { return _names; } - set { _names = value; } - } - private string[] _names; - - /// - /// ScheduledJobDefinition. - /// - [Parameter(Position = 0, Mandatory = true, ValueFromPipeline = true, - ParameterSetName = UnregisterScheduledJobCommand.DefinitionParameterSet)] - [ValidateNotNullOrEmpty] - [SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] - public ScheduledJobDefinition[] InputObject - { - get { return _definitions; } - set { _definitions = value; } - } - private ScheduledJobDefinition[] _definitions; - - /// - /// When true this will stop any running instances of this job definition before - /// removing the definition. - /// - [Parameter(ParameterSetName = UnregisterScheduledJobCommand.DefinitionIdParameterSet)] - [Parameter(ParameterSetName = UnregisterScheduledJobCommand.DefinitionNameParameterSet)] - [Parameter(ParameterSetName = UnregisterScheduledJobCommand.DefinitionParameterSet)] - public SwitchParameter Force - { - get { return _force; } - set { _force = value; } - } - private SwitchParameter _force; - - #endregion - - #region Cmdlet Overrides - - /// - /// Process input. - /// - protected override void ProcessRecord() - { - List definitions = null; - switch (ParameterSetName) - { - case DefinitionParameterSet: - definitions = new List(_definitions); - break; - - case DefinitionNameParameterSet: - definitions = GetJobDefinitionsByName(_names); - break; - - case DefinitionIdParameterSet: - definitions = GetJobDefinitionsById(_definitionIds); - break; - } - - if (definitions != null) - { - foreach (ScheduledJobDefinition definition in definitions) - { - string targetString = StringUtil.Format(ScheduledJobErrorStrings.DefinitionWhatIf, definition.Name); - if (ShouldProcess(targetString, VerbsLifecycle.Unregister)) - { - // Removes the ScheduledJobDefinition from the job store, - // Task Scheduler, and disposes the object. - try - { - definition.Remove(_force); - } - catch (ScheduledJobException e) - { - string msg = StringUtil.Format(ScheduledJobErrorStrings.CantUnregisterDefinition, definition.Name); - Exception reason = new RuntimeException(msg, e); - ErrorRecord errorRecord = new ErrorRecord(reason, "CantUnregisterScheduledJobDefinition", ErrorCategory.InvalidOperation, definition); - WriteError(errorRecord); - } - } - } - } - - // Check for unknown definition names. - if ((_names != null && _names.Length > 0) && - (_definitions == null || _definitions.Length < _names.Length)) - { - // Make sure there is no PowerShell task in Task Scheduler with removed names. - // This covers the case where the scheduled job definition was manually removed from - // the job store but remains as a PowerShell task in Task Scheduler. - using (ScheduledJobWTS taskScheduler = new ScheduledJobWTS()) - { - foreach (string name in _names) - { - taskScheduler.RemoveTaskByName(name, true, true); - } - } - } - } - - #endregion - } -} diff --git a/src/Microsoft.PowerShell.ScheduledJob/resources/ScheduledJobErrorStrings.resx b/src/Microsoft.PowerShell.ScheduledJob/resources/ScheduledJobErrorStrings.resx deleted file mode 100644 index 9d80707dc05..00000000000 --- a/src/Microsoft.PowerShell.ScheduledJob/resources/ScheduledJobErrorStrings.resx +++ /dev/null @@ -1,387 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - Cannot find scheduled job {0}. - {0} is the scheduled job definition name that cannot be found. - - - Cannot find scheduled job definition {0} in the Task Scheduler. - - - The scheduled job definition {0} cannot be removed because one or more instances are currently running. You can remove and stop all running instances by using the Force parameter. - - - An error occurred while adding triggers to the scheduled job {0}. - - - There is no entry in Task Scheduler for scheduled job definition {0}. A new Task Scheduler entry has been created for this scheduled job definition. - - - Cannot get the {0} scheduled job because it is corrupted or in an irresolvable state. Because it cannot run, Windows PowerShell has deleted {0} and its results from the computer. To recreate the scheduled job, use the Register-ScheduledJob cmdlet. For more information about corrupted scheduled jobs, see about_Scheduled_Jobs_Troubleshooting. - {0} is the name of the scheduled job definition. - - - An error occurred while loading job run results for scheduled job {0} with job run date {1}. - - - An error occurred while registering the scheduled job {0}. - - - An error occurred while removing job triggers from scheduled job {0}. - - - One or more scheduled job runs could not be retrieved {0}. - - - Job {0} cannot be saved because no file path was specified. - - - Job {0} has not been run and cannot be saved. Run the job first, and then save results. - - - An error occurred while enabling or disabling the scheduled job {0}. - - - An error occurred while setting properties on the scheduled job {0}. - - - Cannot start a job from the {0} scheduled job definition. - - - An error occurred while unregistering the scheduled job {0}. - - - An error occurred while updating the scheduled job definition {0} with this trigger {1}. See exception details for more information. - - - Only one JobTrigger type can be specified: AtStartup, AtLogon, Once, Daily, or Weekly. - - - A scheduled job definition object {0} already exists in the local scheduled job repository having this Global ID {1}. - - - A scheduled job definition object with Global ID {0} could not be found. - - - A scheduled job definition with ID {0} could not be found. - - - A scheduled job definition with Name {0} could not be found. - - - This scheduled job definition object {0} has been disposed. - - - Scheduled job definition {0}. - - - A directory not found error occurred while registering scheduled job definition {0}. Make sure you are running Windows PowerShell with elevated privileges. - - - An error occurred while registering scheduled job definition {0}. Cannot add this definition object to the job store. - - - An error occurred while registering scheduled job definition {0} to the Windows Task Scheduler. The Task Scheduler error is: {1}. - - - An error occurred while unregistering scheduled job definition {0}. - - - An error occurred while setting file access permissions for job definition {0} and user {1}. - - - An error occurred while updating scheduled job definition {0}. Cannot update this definition in the job store. - - - An error occurred while updating scheduled job definition {0}. Cannot update this definition with the Windows Task Scheduler. - - - An error has occurred within the Task Scheduler. - - - The At parameter is not valid for the {0} job trigger type. - - - The DaysInterval parameter is not valid for the {0} job trigger type. - - - The DaysInterval parameter value must be greater than zero. - - - The DaysOfWeek parameter is not valid for the {0} job trigger type. - - - The FilePath parameter is not valid. - - - Only Windows PowerShell script files are allowed for FilePath parameter. Specify a file with .ps1 extension. - - - The IdleDuration parameter cannot have a negative value. - - - The IdleTimeout parameter cannot have a negative value. - - - The scheduled job definition name {0} contains characters that are not valid. - - - The MaxResultCount parameter cannot have a negative or zero value. - - - The User parameter is not valid for the {0} job trigger type. - - - The WeeksInterval parameter is not valid for the {0} job trigger type. - - - The WeeksInterval parameter value must be greater than zero. - - - An I/O failure occurred while updating the scheduled job definition {0}. This could mean a file is missing or corrupted, either in Task Scheduler or in the Windows PowerShell scheduled job store. You might need to create the scheduled job definition again. - {0} is the scheduled job definition name - - - Job {0} is currently running. - - - The scheduled job definition {0} already exists in the job definition store. - - - The scheduled job results {0} already exist in the job results store. - - - The At parameter is required for the {0} job trigger type. - - - The DaysOfWeek parameter is required for the {0} job trigger type. - - - The job trigger {0} requires the DaysOfWeek parameter to be defined. - - - The Job trigger {0} requires the At parameter to be defined. - - - No Frequency type has been specified for this job trigger. One of the following job trigger frequencies must be specified: AtStartup, AtLogon, Once, Daily, Weekly. - - - An access denied error occurred while updating the scheduled job definition {0}. Try running Windows PowerShell with elevated user rights; that is, Run as Administrator. - {0} is the scheduled job definition name - - - There is no scheduled job definition object associated with this options object. - - - There is no scheduled job definition object associated with this trigger {0}. - - - The scheduled job {0} already exists in the local repository. - - - The scheduled job {0} is not in the local job repository. - - - The scheduled job definition {0} already exists in Task Scheduler. - - - Daily - - - AtLogon - - - A scheduled job trigger with ID {0} was not found for the scheduled job definition {1}. - - - Once - - - AtStartup - - - Weekly - - - An access denied error occurred when registering scheduled job definition {0}. Try running Windows PowerShell with elevated user rights; that is, Run As Administrator. - - - Cannot convert a ScheduledJobTrigger object with TriggerFrequency value of {0}. - - - An unknown trigger type was returned from Task Scheduler for scheduled job definition {0} with trigger ID {1}. - - - The scheduled job definition {0} could not be saved because one of the values in the ArgumentList parameter cannot be converted to XML. If possible, change the ArgumentList values to types that are easily converted to XML, such as strings, integers, and hash tables. {1} - {0} is the name of the scheduled job definition that cannot be registered -{1} is the inner exception message from .Net serialization, or empty if no exception message. - - - Commands that interact with the host program, such as Write-Host, cannot be included in Windows PowerShell scheduled jobs because scheduled jobs do not interact with the host program. Use an alternate command that does not interact with the host program, such as Write-Output or Out-File. - - - The RepetitionInterval parameter value must be less than or equal to the RepetitionDuration parameter value. - - - The RepetitionInterval parameter value must be greater than 1 minute. - - - The RepetitionInterval and RepetitionDuration Job trigger parameters must be specified together. - - - The Repetition parameters cannot have negative values. - - - The Repetition parameters are not valid for the {0} job trigger type. - - - The RepetitionInterval parameter cannot have a value of zero unless the RepetitionDuration parameter also has a zero value. A zero value removes repetition behavior from the Job trigger. - - - An error occured while attempting to rename scheduled job from {0} to {1}. - - - An error occured while attempting to rename scheduled job from {0} to {1} with error message: {2}. - - - An unrecoverable error occurred while renaming the scheduled job from {0} to {1}. The scheduled job will be removed. - - - An unrecoverable error occurred while renaming the scheduled job from {0} to {1} with message {2}. The scheduled job will be removed. - - - An error occurred while running scheduled job definition {0} from the Task Scheduler. - - - An error occurred while running scheduled job definition {0} from the Task Scheduler because {1}. - - - You cannot specify the RepetitionDuration and RepeatIndefinitely parameters in the same command. - - - When you use the RepeatIndefinitely parameter, the RepetitionInterval parameter is required. - - - the scheduled job definition could not be found - - - the scheduled job definition is disabled - - diff --git a/src/Microsoft.PowerShell.Security/Microsoft.PowerShell.Security.csproj b/src/Microsoft.PowerShell.Security/Microsoft.PowerShell.Security.csproj index 411070097c9..a6dadabfed2 100644 --- a/src/Microsoft.PowerShell.Security/Microsoft.PowerShell.Security.csproj +++ b/src/Microsoft.PowerShell.Security/Microsoft.PowerShell.Security.csproj @@ -1,10 +1,8 @@ - - - - + + - PowerShell Core's Microsoft.PowerShell.Security project - $(NoWarn);CS1570 + PowerShell's Microsoft.PowerShell.Security project + $(NoWarn);CS1570;CA1416 Microsoft.PowerShell.Security @@ -12,10 +10,6 @@ - - $(DefineConstants);CORECLR - - @@ -23,16 +17,4 @@ - - portable - - - - $(DefineConstants);UNIX - - - - full - - diff --git a/src/Microsoft.PowerShell.Security/resources/CertificateProviderStrings.resx b/src/Microsoft.PowerShell.Security/resources/CertificateProviderStrings.resx index bf4320ae3ac..c45c640bbb0 100644 --- a/src/Microsoft.PowerShell.Security/resources/CertificateProviderStrings.resx +++ b/src/Microsoft.PowerShell.Security/resources/CertificateProviderStrings.resx @@ -1,192 +1,189 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - X509 Certificate Provider - - - Cannot find the X509 certificate at path {0}. - - - Cannot find the X509 certificate store at path {0}. - - - Cannot find the certificate store because the specified X509 store location {0} is not valid. - - - Cannot process the path because path {0} is not a valid certificate provider path. - - - Move certificate - - - Remove certificate - - - Remove certificate and its private key. - - - Invoke Certificate Manager - - - {0} is not supported in the current operating system. - - - Item: {0} Destination: {1} - - - You cannot move a certificate container. - - - You cannot move a certificate from user store to or from machine. - - - You cannot move a certificate to the same store. - - - You cannot create an item other than certificate store. - - - Creating certificate stores under CurrentUser is not supported. - - - Deleting certificate stores under CurrentUser is not supported. - - - The destination is not a valid store. - - - Item: {0} - - - The store {0} is a built-in system store and cannot be deleted. - - - You cannot remove a certificate container. - - - Private key skipped. The certificate has no private key association. - - - The operation is on user root store and UI is not allowed. - - - . The following error may be a result of user credentials required on the remote machine. See Enable-WSManCredSSP Cmdlet help on how to enable and use CredSSP for delegation with Windows PowerShell remoting. - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + X509 Certificate Provider + + + Cannot find the X509 certificate at path {0}. + + + Cannot find the X509 certificate store at path {0}. + + + Cannot find the certificate store because the specified X509 store location {0} is not valid. + + + Cannot process the path because path {0} is not a valid certificate provider path. + + + Move certificate + + + Remove certificate + + + Remove certificate and its private key. + + + Invoke Certificate Manager + + + Item: {0} Destination: {1} + + + You cannot move a certificate container. + + + You cannot move a certificate from user store to or from machine. + + + You cannot move a certificate to the same store. + + + You cannot create an item other than certificate store. + + + Creating certificate stores under CurrentUser is not supported. + + + Deleting certificate stores under CurrentUser is not supported. + + + The destination is not a valid store. + + + Item: {0} + + + The store {0} is a built-in system store and cannot be deleted. + + + You cannot remove a certificate container. + + + Private key skipped. The certificate has no private key association. + + + The operation is on user root store and UI is not allowed. + + + . The following error may be a result of user credentials required on the remote machine. See Enable-WSManCredSSP Cmdlet help on how to enable and use CredSSP for delegation with PowerShell remoting. + + diff --git a/src/Microsoft.PowerShell.Security/resources/CmsCommands.resx b/src/Microsoft.PowerShell.Security/resources/CmsCommands.resx index 33eabae0556..9f47134f147 100644 --- a/src/Microsoft.PowerShell.Security/resources/CmsCommands.resx +++ b/src/Microsoft.PowerShell.Security/resources/CmsCommands.resx @@ -1,4 +1,4 @@ - + + @@ -20,16 +21,4 @@ - - portable - - - - $(DefineConstants);UNIX - - - - full - - diff --git a/src/Microsoft.WSMan.Management/NewWSManSession.cs b/src/Microsoft.WSMan.Management/NewWSManSession.cs index 5d44cf29361..09b22af9924 100644 --- a/src/Microsoft.WSMan.Management/NewWSManSession.cs +++ b/src/Microsoft.WSMan.Management/NewWSManSession.cs @@ -1,21 +1,19 @@ -// -// Copyright (C) Microsoft. All rights reserved. -// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + using System; -using System.IO; -using System.Reflection; -using System.ComponentModel; -using System.Runtime.InteropServices; -using System.Runtime.CompilerServices; -using System.Management.Automation; -using System.Management.Automation.Provider; -using System.Xml; using System.Collections; using System.Collections.Generic; +using System.ComponentModel; using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Management.Automation; +using System.Management.Automation.Provider; using System.Net; - - +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Xml; namespace Microsoft.WSMan.Management { @@ -25,15 +23,13 @@ namespace Microsoft.WSMan.Management /// Get-WSManInstance /// Set-WSManInstance /// Invoke-WSManAction - /// Connect-WSMan + /// Connect-WSMan. /// - - - [Cmdlet(VerbsCommon.New, "WSManSessionOption", HelpUri = "https://go.microsoft.com/fwlink/?LinkId=141449")] + [Cmdlet(VerbsCommon.New, "WSManSessionOption", HelpUri = "https://go.microsoft.com/fwlink/?LinkId=2096845")] + [OutputType(typeof(SessionOption))] public class NewWSManSessionOptionCommand : PSCmdlet { /// - /// /// [Parameter] [ValidateNotNullOrEmpty] @@ -43,11 +39,13 @@ public ProxyAccessType ProxyAccessType { return _proxyaccesstype; } + set { _proxyaccesstype = value; } } + private ProxyAccessType _proxyaccesstype; /// @@ -57,18 +55,23 @@ public ProxyAccessType ProxyAccessType /// - Negotiate: Use the default authentication (ad defined by the underlying /// protocol) for establishing a remote connection. /// - Basic: Use basic authentication for establishing a remote connection - /// - Digest: Use Digest authentication for establishing a remote connection + /// - Digest: Use Digest authentication for establishing a remote connection. /// [Parameter] [ValidateNotNullOrEmpty] public ProxyAuthentication ProxyAuthentication { - get { return proxyauthentication; } + get + { + return proxyauthentication; + } + set { proxyauthentication = value; } } + private ProxyAuthentication proxyauthentication; /// @@ -79,14 +82,18 @@ public ProxyAuthentication ProxyAuthentication [Credential] public PSCredential ProxyCredential { - get { return _proxycredential; } + get + { + return _proxycredential; + } + set { _proxycredential = value; } } - private PSCredential _proxycredential; + private PSCredential _proxycredential; /// /// The following is the definition of the input parameter "SkipCACheck". @@ -94,104 +101,134 @@ public PSCredential ProxyCredential /// certificate is signed by a trusted certificate authority (CA). Use only when /// the remote computer is trusted by other means, for example, if the remote /// computer is part of a network that is physically secure and isolated or the - /// remote computer is listed as a trusted host in WinRM configuration + /// remote computer is listed as a trusted host in WinRM configuration. /// [Parameter] public SwitchParameter SkipCACheck { - get { return skipcacheck; } + get + { + return skipcacheck; + } + set { skipcacheck = value; } } + private bool skipcacheck; /// /// The following is the definition of the input parameter "SkipCNCheck". /// Indicates that certificate common name (CN) of the server need not match the /// hostname of the server. Used only in remote operations using https. This - /// option should only be used for trusted machines + /// option should only be used for trusted machines. /// [Parameter] public SwitchParameter SkipCNCheck { - get { return skipcncheck; } + get + { + return skipcncheck; + } + set { skipcncheck = value; } } + private bool skipcncheck; /// /// The following is the definition of the input parameter "SkipRevocation". /// Indicates that certificate common name (CN) of the server need not match the /// hostname of the server. Used only in remote operations using https. This - /// option should only be used for trusted machines + /// option should only be used for trusted machines. /// [Parameter] public SwitchParameter SkipRevocationCheck { - get { return skiprevocationcheck; } + get + { + return skiprevocationcheck; + } + set { skiprevocationcheck = value; } } + private bool skiprevocationcheck; /// /// The following is the definition of the input parameter "SPNPort". /// Appends port number to the connection Service Principal Name SPN of the /// remote server. - /// SPN is used when authentication mechanism is Kerberos or Negotiate + /// SPN is used when authentication mechanism is Kerberos or Negotiate. /// [Parameter] [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "SPN")] - [ValidateRange(0, Int32.MaxValue)] - public Int32 SPNPort + [ValidateRange(0, int.MaxValue)] + public int SPNPort { - get { return spnport; } + get + { + return spnport; + } + set { spnport = value; } } - private Int32 spnport; + + private int spnport; /// /// The following is the definition of the input parameter "Timeout". - /// Defines the timeout in ms for the wsman operation + /// Defines the timeout in ms for the wsman operation. /// [Parameter] [Alias("OperationTimeoutMSec")] - [ValidateRange(0, Int32.MaxValue)] - public Int32 OperationTimeout + [ValidateRange(0, int.MaxValue)] + public int OperationTimeout { - get { return operationtimeout; } + get + { + return operationtimeout; + } + set { operationtimeout = value; } } - private Int32 operationtimeout; + + private int operationtimeout; /// /// The following is the definition of the input parameter "UnEncrypted". /// Specifies that no encryption will be used when doing remote operations over /// http. Unencrypted traffic is not allowed by default and must be enabled in - /// the local configuration + /// the local configuration. /// [Parameter] public SwitchParameter NoEncryption { - get { return noencryption; } + get + { + return noencryption; + } + set { noencryption = value; } } + private bool noencryption; /// @@ -203,17 +240,18 @@ public SwitchParameter NoEncryption [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "UTF")] public SwitchParameter UseUTF16 { - get { return useutf16; } + get + { + return useutf16; + } + set { useutf16 = value; } } - private bool useutf16; - - - + private bool useutf16; /// /// BeginProcessing method. @@ -241,9 +279,7 @@ protected override void BeginProcessing() return; } - - - //Creating the Session Object + // Creating the Session Object SessionOption objSessionOption = new SessionOption(); objSessionOption.SPNPort = spnport; @@ -252,7 +288,7 @@ protected override void BeginProcessing() objSessionOption.SkipCACheck = skipcacheck; objSessionOption.OperationTimeout = operationtimeout; objSessionOption.SkipRevocationCheck = skiprevocationcheck; - //Proxy Settings + // Proxy Settings objSessionOption.ProxyAccessType = _proxyaccesstype; objSessionOption.ProxyAuthentication = proxyauthentication; @@ -260,13 +296,14 @@ protected override void BeginProcessing() { objSessionOption.UseEncryption = false; } + if (_proxycredential != null) { NetworkCredential nwCredentials = _proxycredential.GetNetworkCredential(); objSessionOption.ProxyCredential = nwCredentials; } - WriteObject(objSessionOption); - }//End BeginProcessing() - }//End Class + WriteObject(objSessionOption); + } + } } diff --git a/src/Microsoft.WSMan.Management/PingWSMan.cs b/src/Microsoft.WSMan.Management/PingWSMan.cs index 608534b704a..88b443a6ef0 100644 --- a/src/Microsoft.WSMan.Management/PingWSMan.cs +++ b/src/Microsoft.WSMan.Management/PingWSMan.cs @@ -1,60 +1,63 @@ -// -// Copyright (C) Microsoft. All rights reserved. -// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + using System; -using System.IO; -using System.Reflection; +using System.Collections; +using System.Collections.Generic; using System.ComponentModel; -using System.Runtime.InteropServices; -using System.Runtime.CompilerServices; +using System.Diagnostics.CodeAnalysis; +using System.IO; using System.Management.Automation; using System.Management.Automation.Provider; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using System.Xml; -using System.Collections; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; - namespace Microsoft.WSMan.Management { - #region Test-WSMAN /// /// Issues an operation against the remote machine to ensure that the wsman - /// service is running + /// service is running. /// - - [Cmdlet(VerbsDiagnostic.Test, "WSMan", HelpUri = "https://go.microsoft.com/fwlink/?LinkId=141464")] + [Cmdlet(VerbsDiagnostic.Test, "WSMan", HelpUri = "https://go.microsoft.com/fwlink/?LinkId=2097114")] + [OutputType(typeof(XmlElement))] public class TestWSManCommand : AuthenticatingWSManCommand, IDisposable { /// /// The following is the definition of the input parameter "ComputerName". /// Executes the management operation on the specified computer. The default is /// the local computer. Type the fully qualified domain name, NETBIOS name or IP - /// address to indicate the remote host + /// address to indicate the remote host. /// [Parameter(Position = 0, ValueFromPipeline = true)] [Alias("cn")] - public String ComputerName + public string ComputerName { - get { return computername; } + get + { + return computername; + } + set { computername = value; - if ((string.IsNullOrEmpty(computername)) || (computername.Equals(".", StringComparison.CurrentCultureIgnoreCase))) + if ((string.IsNullOrEmpty(computername)) || (computername.Equals(".", StringComparison.OrdinalIgnoreCase))) { computername = "localhost"; } } } - private String computername = null; + + private string computername = null; /// /// The following is the definition of the input parameter "Authentication". /// This parameter takes a set of authentication methods the user can select /// from. The available method are an enum called AuthenticationMechanism in the - /// System.Management.Automation.Runspaces namespace. The available options + /// System.Management.Automation.Runspaces namespace. The available options /// should be as follows: /// - Default : Use the default authentication (ad defined by the underlying /// protocol) for establishing a remote connection. @@ -73,13 +76,18 @@ public String ComputerName [Alias("auth", "am")] public override AuthenticationMechanism Authentication { - get { return authentication; } + get + { + return authentication; + } + set { authentication = value; ValidateSpecifiedAuthentication(); } } + private AuthenticationMechanism authentication = AuthenticationMechanism.None; /// @@ -88,13 +96,15 @@ public override AuthenticationMechanism Authentication /// [Parameter(ParameterSetName = "ComputerName")] [ValidateNotNullOrEmpty] - [ValidateRange(1, Int32.MaxValue)] - public Int32 Port + [ValidateRange(1, int.MaxValue)] + public int Port { get { return port; } + set { port = value; } } - private Int32 port = 0; + + private int port = 0; /// /// The following is the definition of the input parameter "UseSSL". @@ -107,8 +117,10 @@ public Int32 Port public SwitchParameter UseSSL { get { return usessl; } + set { usessl = value; } } + private SwitchParameter usessl; /// @@ -117,38 +129,38 @@ public SwitchParameter UseSSL /// [Parameter(ParameterSetName = "ComputerName")] [ValidateNotNullOrEmpty] - public String ApplicationName + public string ApplicationName { get { return applicationname; } + set { applicationname = value; } } - private String applicationname = null; + private string applicationname = null; /// /// ProcessRecord method. /// protected override void ProcessRecord() { - WSManHelper helper = new WSManHelper(this); IWSManEx wsmanObject = (IWSManEx)new WSManClass(); - string connectionStr = String.Empty; + string connectionStr = string.Empty; connectionStr = helper.CreateConnectionString(null, port, computername, applicationname); IWSManSession m_SessionObj = null; try { m_SessionObj = helper.CreateSessionObject(wsmanObject, Authentication, null, Credential, connectionStr, CertificateThumbprint, usessl.IsPresent); - m_SessionObj.Timeout = 1000; //1 sec. we are putting this low so that Test-WSMan can return promptly if the server goes unresponsive. + m_SessionObj.Timeout = 1000; // 1 sec. we are putting this low so that Test-WSMan can return promptly if the server goes unresponsive. XmlDocument xmldoc = new XmlDocument(); xmldoc.LoadXml(m_SessionObj.Identify(0)); WriteObject(xmldoc.DocumentElement); } - catch(Exception) + catch (Exception) { try { - if (!String.IsNullOrEmpty(m_SessionObj.Error)) + if (!string.IsNullOrEmpty(m_SessionObj.Error)) { XmlDocument ErrorDoc = new XmlDocument(); ErrorDoc.LoadXml(m_SessionObj.Error); @@ -157,30 +169,30 @@ protected override void ProcessRecord() this.WriteError(er); } } - catch(Exception) - {} + catch (Exception) + { } } finally { if (m_SessionObj != null) Dispose(m_SessionObj); } - }//End BeginProcessing() + } #region IDisposable Members /// - /// public dispose method + /// Public dispose method. /// public void Dispose() { - //CleanUp(); + // CleanUp(); GC.SuppressFinalize(this); } /// - /// public dispose method + /// Public dispose method. /// public void @@ -191,7 +203,6 @@ protected override void ProcessRecord() } #endregion IDisposable Members - - }//End Class + } #endregion } diff --git a/src/Microsoft.WSMan.Management/Set-QuickConfig.cs b/src/Microsoft.WSMan.Management/Set-QuickConfig.cs index 240a25765b7..9ad9e39c332 100644 --- a/src/Microsoft.WSMan.Management/Set-QuickConfig.cs +++ b/src/Microsoft.WSMan.Management/Set-QuickConfig.cs @@ -1,19 +1,18 @@ -// -// Copyright (C) Microsoft. All rights reserved. -// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + using System; -using System.IO; -using System.Reflection; +using System.Collections; +using System.Collections.Generic; using System.ComponentModel; -using System.Runtime.InteropServices; -using System.Runtime.CompilerServices; +using System.Diagnostics.CodeAnalysis; +using System.IO; using System.Management.Automation; using System.Management.Automation.Provider; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; using System.Xml; -using System.Collections; -using System.Collections.Generic; -using System.Diagnostics.CodeAnalysis; - namespace Microsoft.WSMan.Management { @@ -28,49 +27,56 @@ namespace Microsoft.WSMan.Management /// 2. Set the WinRM service type to auto start /// 3. Create a listener to accept request on any IP address. By default /// transport is http - /// 4. Enable firewall exception for WS-Management traffic + /// 4. Enable firewall exception for WS-Management traffic. /// - [Cmdlet(VerbsCommon.Set, "WSManQuickConfig", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=141463")] + [Cmdlet(VerbsCommon.Set, "WSManQuickConfig", HelpUri = "https://go.microsoft.com/fwlink/?LinkID=2097112")] + [OutputType(typeof(string))] public class SetWSManQuickConfigCommand : PSCmdlet, IDisposable { /// /// The following is the definition of the input parameter "UseSSL". /// Indicates a https listener to be created. If this switch is not specified - /// then by default a http listener will be created + /// then by default a http listener will be created. /// [Parameter] [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "SSL")] public SwitchParameter UseSSL { get { return usessl; } + set { usessl = value; } } + private SwitchParameter usessl; - //helper variable + // helper variable private WSManHelper helper; /// /// Property that sets force parameter. This will allow /// configuring WinRM without prompting the user. /// - [Parameter()] + [Parameter] public SwitchParameter Force { get { return force; } + set { force = value; } } + private bool force = false; /// /// Property that will allow configuring WinRM with Public profile exception enabled. /// - [Parameter()] + [Parameter] public SwitchParameter SkipNetworkProfileCheck { get { return skipNetworkProfileCheck; } + set { skipNetworkProfileCheck = value; } } + private bool skipNetworkProfileCheck = false; /// @@ -78,24 +84,22 @@ public SwitchParameter SkipNetworkProfileCheck /// protected override void BeginProcessing() { - //If not running elevated, then throw an "elevation required" error message. + // If not running elevated, then throw an "elevation required" error message. WSManHelper.ThrowIfNotAdministrator(); helper = new WSManHelper(this); - String query = helper.GetResourceMsgFromResourcetext("QuickConfigContinueQuery"); - String caption = helper.GetResourceMsgFromResourcetext("QuickConfigContinueCaption"); + string query = helper.GetResourceMsgFromResourcetext("QuickConfigContinueQuery"); + string caption = helper.GetResourceMsgFromResourcetext("QuickConfigContinueCaption"); if (!force && !ShouldContinue(query, caption)) { return; } + QuickConfigRemoting(true); QuickConfigRemoting(false); - }//End BeginProcessing() - - + } #region private - private void QuickConfigRemoting(bool serviceonly) { IWSManSession m_SessionObj = null; @@ -112,7 +116,6 @@ private void QuickConfigRemoting(bool serviceonly) string xpathStatus = string.Empty; string xpathResult = string.Empty; - if (!usessl) { transport = "http"; @@ -129,12 +132,11 @@ private void QuickConfigRemoting(bool serviceonly) } else { - string openAllProfiles = skipNetworkProfileCheck ? "" : String.Empty; + string openAllProfiles = skipNetworkProfileCheck ? "" : string.Empty; analysisInputXml = @"" + transport + "" + openAllProfiles + ""; action = "Analyze"; } - string analysisOutputXml = m_SessionObj.Invoke(action, "winrm/config/service", analysisInputXml, 0); XmlDocument resultopxml = new XmlDocument(); resultopxml.LoadXml(analysisOutputXml); @@ -152,8 +154,6 @@ private void QuickConfigRemoting(bool serviceonly) xpathUpdate = "/cfg:Analyze_OUTPUT/cfg:EnableRemoting_INPUT"; } - - XmlNamespaceManager nsmgr = new XmlNamespaceManager(resultopxml.NameTable); nsmgr.AddNamespace("cfg", "http://schemas.microsoft.com/wbem/wsman/1/config/service"); string enabled = resultopxml.SelectSingleNode(xpathEnabled, nsmgr).InnerText; @@ -163,10 +163,11 @@ private void QuickConfigRemoting(bool serviceonly) { source = sourceAttribute.Value; } - string rxml = ""; + + string rxml = string.Empty; if (enabled.Equals("true")) { - string Err_Msg = ""; + string Err_Msg = string.Empty; if (serviceonly) { Err_Msg = WSManResourceLoader.GetResourceString("L_QuickConfigNoServiceChangesNeeded_Message"); @@ -181,6 +182,7 @@ private void QuickConfigRemoting(bool serviceonly) WriteObject(Err_Msg); return; } + if (!enabled.Equals("false")) { ArgumentException e = new ArgumentException(WSManResourceLoader.GetResourceString("L_QuickConfig_InvalidBool_0_ErrorMessage")); @@ -190,9 +192,9 @@ private void QuickConfigRemoting(bool serviceonly) } string resultAction = resultopxml.SelectSingleNode(xpathText, nsmgr).InnerText; - if ( source != null && source.Equals("GPO")) + if (source != null && source.Equals("GPO")) { - String Info_Msg = WSManResourceLoader.GetResourceString("L_QuickConfig_RemotingDisabledbyGP_00_ErrorMessage"); + string Info_Msg = WSManResourceLoader.GetResourceString("L_QuickConfig_RemotingDisabledbyGP_00_ErrorMessage"); Info_Msg += " " + resultAction; ArgumentException e = new ArgumentException(Info_Msg); WriteError(new ErrorRecord(e, "NotSpecified", ErrorCategory.NotSpecified, null)); @@ -200,7 +202,7 @@ private void QuickConfigRemoting(bool serviceonly) } string inputXml = resultopxml.SelectSingleNode(xpathUpdate, nsmgr).OuterXml; - if (resultAction.Equals("") || inputXml.Equals("")) + if (resultAction.Equals(string.Empty) || inputXml.Equals(string.Empty)) { ArgumentException e = new ArgumentException(WSManResourceLoader.GetResourceString("L_ERR_Message") + WSManResourceLoader.GetResourceString("L_QuickConfig_MissingUpdateXml_0_ErrorMessage")); ErrorRecord er = new ErrorRecord(e, "InvalidOperation", ErrorCategory.InvalidOperation, null); @@ -216,6 +218,7 @@ private void QuickConfigRemoting(bool serviceonly) { action = "EnableRemoting"; } + rxml = m_SessionObj.Invoke(action, "winrm/config/service", inputXml, 0); XmlDocument finalxml = new XmlDocument(); finalxml.LoadXml(rxml); @@ -230,7 +233,8 @@ private void QuickConfigRemoting(bool serviceonly) xpathStatus = "/cfg:EnableRemoting_OUTPUT/cfg:Status"; xpathResult = "/cfg:EnableRemoting_OUTPUT/cfg:Results"; } - if (finalxml.SelectSingleNode(xpathStatus, nsmgr).InnerText.ToString().Equals("succeeded")) + + if (finalxml.SelectSingleNode(xpathStatus, nsmgr).InnerText.Equals("succeeded")) { if (serviceonly) { @@ -240,6 +244,7 @@ private void QuickConfigRemoting(bool serviceonly) { WriteObject(WSManResourceLoader.GetResourceString("L_QuickConfigUpdated_Message")); } + WriteObject(finalxml.SelectSingleNode(xpathResult, nsmgr).InnerText); } else @@ -249,13 +254,13 @@ private void QuickConfigRemoting(bool serviceonly) } finally { - if (!String.IsNullOrEmpty(m_SessionObj.Error)) + if (!string.IsNullOrEmpty(m_SessionObj.Error)) { helper.AssertError(m_SessionObj.Error, true, null); } + if (m_SessionObj != null) Dispose(m_SessionObj); - } } #endregion private @@ -263,17 +268,17 @@ private void QuickConfigRemoting(bool serviceonly) #region IDisposable Members /// - /// public dispose method + /// Public dispose method. /// public void Dispose() { - //CleanUp(); + // CleanUp(); GC.SuppressFinalize(this); } /// - /// public dispose method + /// Public dispose method. /// public void @@ -284,7 +289,6 @@ private void QuickConfigRemoting(bool serviceonly) } #endregion IDisposable Members - - }//End Class + } #endregion Set-WsManQuickConfig } diff --git a/src/Microsoft.WSMan.Management/WSManConnections.cs b/src/Microsoft.WSMan.Management/WSManConnections.cs index 3dbb9985418..cac5196740f 100644 --- a/src/Microsoft.WSMan.Management/WSManConnections.cs +++ b/src/Microsoft.WSMan.Management/WSManConnections.cs @@ -1,21 +1,21 @@ -// -// Copyright (C) Microsoft. All rights reserved. -// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + using System; -using System.IO; -using System.Reflection; +using System.Collections; +using System.Collections.Generic; using System.ComponentModel; -using System.Runtime.InteropServices; -using System.Runtime.CompilerServices; +using System.Diagnostics.CodeAnalysis; +using System.IO; using System.Management.Automation; using System.Management.Automation.Provider; -using System.Xml; -using System.Collections; -using System.Collections.Generic; using System.Management.Automation.Runspaces; -using System.Diagnostics.CodeAnalysis; -using Dbg = System.Management.Automation; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Xml; +using Dbg = System.Management.Automation; namespace Microsoft.WSMan.Management { @@ -23,7 +23,7 @@ namespace Microsoft.WSMan.Management /// /// Common base class for all WSMan cmdlets that - /// take Authentication, CertificateThumbprint and Credential parameters + /// take Authentication, CertificateThumbprint and Credential parameters. /// public class AuthenticatingWSManCommand : PSCmdlet { @@ -38,20 +38,25 @@ public class AuthenticatingWSManCommand : PSCmdlet [Alias("cred", "c")] public virtual PSCredential Credential { - get { return credential; } + get + { + return credential; + } + set { credential = value; ValidateSpecifiedAuthentication(); } } + private PSCredential credential; /// /// The following is the definition of the input parameter "Authentication". /// This parameter takes a set of authentication methods the user can select /// from. The available method are an enum called Authentication in the - /// System.Management.Automation.Runspaces namespace. The available options + /// System.Management.Automation.Runspaces namespace. The available options /// should be as follows: /// - Default : Use the default authentication (ad defined by the underlying /// protocol) for establishing a remote connection. @@ -67,13 +72,18 @@ public virtual PSCredential Credential [Alias("auth", "am")] public virtual AuthenticationMechanism Authentication { - get { return authentication; } + get + { + return authentication; + } + set { authentication = value; ValidateSpecifiedAuthentication(); } } + private AuthenticationMechanism authentication = AuthenticationMechanism.Default; /// @@ -84,13 +94,18 @@ public virtual AuthenticationMechanism Authentication [ValidateNotNullOrEmpty] public virtual string CertificateThumbprint { - get { return thumbPrint; } + get + { + return thumbPrint; + } + set { thumbPrint = value; ValidateSpecifiedAuthentication(); } } + private string thumbPrint = null; internal void ValidateSpecifiedAuthentication() @@ -106,12 +121,11 @@ internal void ValidateSpecifiedAuthentication() #region Connect-WsMan /// - /// connect wsman cmdlet + /// Connect wsman cmdlet. /// - [Cmdlet(VerbsCommunications.Connect, "WSMan", DefaultParameterSetName = "ComputerName", HelpUri = "https://go.microsoft.com/fwlink/?LinkId=141437")] + [Cmdlet(VerbsCommunications.Connect, "WSMan", DefaultParameterSetName = "ComputerName", HelpUri = "https://go.microsoft.com/fwlink/?LinkId=2096841")] public class ConnectWSManCommand : AuthenticatingWSManCommand { - #region Parameters /// @@ -120,12 +134,14 @@ public class ConnectWSManCommand : AuthenticatingWSManCommand /// [Parameter(ParameterSetName = "ComputerName")] [ValidateNotNullOrEmpty] - public String ApplicationName + public string ApplicationName { get { return applicationname; } + set { applicationname = value; } } - private String applicationname = null; + + private string applicationname = null; /// /// The following is the definition of the input parameter "ComputerName". @@ -135,19 +151,24 @@ public String ApplicationName /// [Parameter(ParameterSetName = "ComputerName", Position = 0)] [Alias("cn")] - public String ComputerName + public string ComputerName { - get { return computername; } + get + { + return computername; + } + set { computername = value; - if ((string.IsNullOrEmpty(computername)) || (computername.Equals(".", StringComparison.CurrentCultureIgnoreCase))) + if ((string.IsNullOrEmpty(computername)) || (computername.Equals(".", StringComparison.OrdinalIgnoreCase))) { computername = "localhost"; } } } - private String computername = null; + + private string computername = null; /// /// The following is the definition of the input parameter "ConnectionURI". @@ -161,8 +182,10 @@ public String ComputerName public Uri ConnectionURI { get { return connectionuri; } + set { connectionuri = value; } } + private Uri connectionuri; /// @@ -177,8 +200,10 @@ public Uri ConnectionURI public Hashtable OptionSet { get { return optionset; } + set { optionset = value; } } + private Hashtable optionset; /// @@ -188,18 +213,20 @@ public Hashtable OptionSet [Parameter] [ValidateNotNullOrEmpty] [Parameter(ParameterSetName = "ComputerName")] - [ValidateRange(1, Int32.MaxValue)] - public Int32 Port + [ValidateRange(1, int.MaxValue)] + public int Port { get { return port; } + set { port = value; } } - private Int32 port = 0; + + private int port = 0; /// /// The following is the definition of the input parameter "SessionOption". /// Defines a set of extended options for the WSMan session. This hashtable can - /// be created using New-WSManSessionOption + /// be created using New-WSManSessionOption. /// [Parameter] [ValidateNotNullOrEmpty] @@ -208,8 +235,10 @@ public Int32 Port public SessionOption SessionOption { get { return sessionoption; } + set { sessionoption = value; } } + private SessionOption sessionoption; /// @@ -223,11 +252,11 @@ public SessionOption SessionOption public SwitchParameter UseSSL { get { return usessl; } + set { usessl = value; } } - private SwitchParameter usessl; - + private SwitchParameter usessl; #endregion @@ -236,15 +265,14 @@ public SwitchParameter UseSSL /// protected override void BeginProcessing() { - WSManHelper helper = new WSManHelper(this); if (connectionuri != null) { try { - //always in the format http://server:port/applicationname - string[] constrsplit = connectionuri.OriginalString.Split(new string[] { ":" + port + "/" + applicationname }, StringSplitOptions.None); - string[] constrsplit1 = constrsplit[0].Split(new string[] { "//" }, StringSplitOptions.None); + // always in the format http://server:port/applicationname + string[] constrsplit = connectionuri.OriginalString.Split(":" + port + "/" + applicationname, StringSplitOptions.None); + string[] constrsplit1 = constrsplit[0].Split("//", StringSplitOptions.None); computername = constrsplit1[1].Trim(); } catch (IndexOutOfRangeException) @@ -252,30 +280,27 @@ protected override void BeginProcessing() helper.AssertError(helper.GetResourceMsgFromResourcetext("NotProperURI"), false, connectionuri); } } - string crtComputerName = computername; - if (crtComputerName == null) - { - crtComputerName = "localhost"; - } - if (this.SessionState.Path.CurrentProviderLocation(WSManStringLiterals.rootpath).Path.StartsWith(this.SessionState.Drive.Current.Name + ":" + WSManStringLiterals.DefaultPathSeparator + crtComputerName, StringComparison.CurrentCultureIgnoreCase)) + + string crtComputerName = computername ?? "localhost"; + + if (this.SessionState.Path.CurrentProviderLocation(WSManStringLiterals.rootpath).Path.StartsWith(this.SessionState.Drive.Current.Name + ":" + WSManStringLiterals.DefaultPathSeparator + crtComputerName, StringComparison.OrdinalIgnoreCase)) { helper.AssertError(helper.GetResourceMsgFromResourcetext("ConnectFailure"), false, computername); } - helper.CreateWsManConnection(ParameterSetName, connectionuri, port, computername, applicationname, usessl.IsPresent, Authentication, sessionoption, Credential, CertificateThumbprint); - }//End BeginProcessing() - }//end class + helper.CreateWsManConnection(ParameterSetName, connectionuri, port, computername, applicationname, usessl.IsPresent, Authentication, sessionoption, Credential, CertificateThumbprint); + } + } #endregion - # region Disconnect-WSMAN + #region Disconnect-WSMAN /// /// The following is the definition of the input parameter "ComputerName". /// Executes the management operation on the specified computer(s). The default /// is the local computer. Type the fully qualified domain name, NETBIOS name or /// IP address to indicate the remote host(s) /// - - [Cmdlet(VerbsCommunications.Disconnect, "WSMan", HelpUri = "https://go.microsoft.com/fwlink/?LinkId=141439")] + [Cmdlet(VerbsCommunications.Disconnect, "WSMan", HelpUri = "https://go.microsoft.com/fwlink/?LinkId=2096839")] public class DisconnectWSManCommand : PSCmdlet, IDisposable { /// @@ -285,35 +310,39 @@ public class DisconnectWSManCommand : PSCmdlet, IDisposable /// IP address to indicate the remote host(s) /// [Parameter(Position = 0)] - public String ComputerName + public string ComputerName { - get { return computername; } - set + get { + return computername; + } + set + { computername = value; - if ((string.IsNullOrEmpty(computername)) || (computername.Equals(".", StringComparison.CurrentCultureIgnoreCase))) + if ((string.IsNullOrEmpty(computername)) || (computername.Equals(".", StringComparison.OrdinalIgnoreCase))) { computername = "localhost"; } } } - private String computername = null; + + private string computername = null; #region IDisposable Members /// - /// public dispose method + /// Public dispose method. /// public void Dispose() { - //CleanUp(); + // CleanUp(); GC.SuppressFinalize(this); } /// - /// public dispose method + /// Public dispose method. /// public void @@ -321,7 +350,6 @@ public String ComputerName { session = null; this.Dispose(); - } #endregion IDisposable Members @@ -332,15 +360,14 @@ public String ComputerName protected override void BeginProcessing() { WSManHelper helper = new WSManHelper(this); - if (computername == null) - { - computername = "localhost"; - } - if (this.SessionState.Path.CurrentProviderLocation(WSManStringLiterals.rootpath).Path.StartsWith(WSManStringLiterals.rootpath + ":" + WSManStringLiterals.DefaultPathSeparator + computername, StringComparison.CurrentCultureIgnoreCase)) + computername ??= "localhost"; + + if (this.SessionState.Path.CurrentProviderLocation(WSManStringLiterals.rootpath).Path.StartsWith(WSManStringLiterals.rootpath + ":" + WSManStringLiterals.DefaultPathSeparator + computername, StringComparison.OrdinalIgnoreCase)) { helper.AssertError(helper.GetResourceMsgFromResourcetext("DisconnectFailure"), false, computername); } - if (computername.Equals("localhost", StringComparison.CurrentCultureIgnoreCase)) + + if (computername.Equals("localhost", StringComparison.OrdinalIgnoreCase)) { helper.AssertError(helper.GetResourceMsgFromResourcetext("LocalHost"), false, computername); } @@ -354,10 +381,7 @@ protected override void BeginProcessing() { helper.AssertError(helper.GetResourceMsgFromResourcetext("InvalidComputerName"), false, computername); } - }//End BeginProcessing() - - - - }//End Class + } + } #endregion Disconnect-WSMAN } diff --git a/src/Microsoft.WSMan.Management/WSManInstance.cs b/src/Microsoft.WSMan.Management/WSManInstance.cs index f11115874ea..c96b002123d 100644 --- a/src/Microsoft.WSMan.Management/WSManInstance.cs +++ b/src/Microsoft.WSMan.Management/WSManInstance.cs @@ -1,22 +1,22 @@ -// -// Copyright (C) Microsoft. All rights reserved. -// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + using System; -using System.IO; -using System.Reflection; -using System.ComponentModel; -using System.Runtime.InteropServices; -using System.Runtime.CompilerServices; -using System.Management.Automation; -using System.Management.Automation.Provider; -using System.Xml; using System.Collections; using System.Collections.Generic; -using System.Management.Automation.Runspaces; +using System.ComponentModel; using System.Diagnostics.CodeAnalysis; -using Dbg = System.Management.Automation; using System.Globalization; +using System.IO; +using System.Management.Automation; +using System.Management.Automation.Provider; +using System.Management.Automation.Runspaces; +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Xml; +using Dbg = System.Management.Automation; namespace Microsoft.WSMan.Management { @@ -28,8 +28,8 @@ namespace Microsoft.WSMan.Management /// Invoke-WSManAction -Action StartService -ResourceURI wmicimv2/Win32_Service /// -SelectorSet {Name=Spooler} /// - - [Cmdlet(VerbsCommon.Get, "WSManInstance", DefaultParameterSetName = "GetInstance", HelpUri = "https://go.microsoft.com/fwlink/?LinkId=141444")] + [Cmdlet(VerbsCommon.Get, "WSManInstance", DefaultParameterSetName = "GetInstance", HelpUri = "https://go.microsoft.com/fwlink/?LinkId=2096627")] + [OutputType(typeof(XmlElement))] public class GetWSManInstanceCommand : AuthenticatingWSManCommand, IDisposable { #region parameter @@ -39,32 +39,42 @@ public class GetWSManInstanceCommand : AuthenticatingWSManCommand, IDisposable /// [Parameter(ParameterSetName = "GetInstance")] [Parameter(ParameterSetName = "Enumerate")] - public String ApplicationName + public string ApplicationName { - get { return applicationname; } + get + { + return applicationname; + } + set { { applicationname = value; } } } - private String applicationname = null; + + private string applicationname = null; /// /// The following is the definition of the input parameter "BasePropertiesOnly". /// Enumerate only those properties that are part of the base class /// specification in the Resource URI. When - /// Shallow is specified then this flag has no effect + /// Shallow is specified then this flag has no effect. /// [Parameter(ParameterSetName = "Enumerate")] [Alias("UBPO", "Base")] public SwitchParameter BasePropertiesOnly { - get { return basepropertiesonly; } + get + { + return basepropertiesonly; + } + set { { basepropertiesonly = value; } } } + private SwitchParameter basepropertiesonly; /// @@ -76,19 +86,24 @@ public SwitchParameter BasePropertiesOnly [Parameter(ParameterSetName = "GetInstance")] [Parameter(ParameterSetName = "Enumerate")] [Alias("CN")] - public String ComputerName + public string ComputerName { - get { return computername; } + get + { + return computername; + } + set { computername = value; - if ((string.IsNullOrEmpty(computername)) || (computername.Equals(".", StringComparison.CurrentCultureIgnoreCase))) + if ((string.IsNullOrEmpty(computername)) || (computername.Equals(".", StringComparison.OrdinalIgnoreCase))) { computername = "localhost"; } } } - private String computername = null; + + private string computername = null; /// /// The following is the definition of the input parameter "ConnectionURI". @@ -96,7 +111,6 @@ public String ComputerName /// remote machine. The format of this string is: /// transport://server:port/Prefix. /// - [Parameter( ParameterSetName = "GetInstance")] [Parameter( @@ -105,84 +119,107 @@ public String ComputerName [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "URI")] public Uri ConnectionURI { - get { return connectionuri; } + get + { + return connectionuri; + } + set { { connectionuri = value; } } } + private Uri connectionuri; /// /// The following is the definition of the input parameter "Dialect". - /// Defines the dialect for the filter predicate + /// Defines the dialect for the filter predicate. /// [Parameter] public Uri Dialect { - get { return dialect; } + get + { + return dialect; + } + set { { dialect = value; } } } + private Uri dialect; /// /// The following is the definition of the input parameter "Enumerate". /// Switch indicates list all instances of a management resource. Equivalent to - /// WSManagement Enumerate + /// WSManagement Enumerate. /// - [Parameter(Mandatory = true, ParameterSetName = "Enumerate")] public SwitchParameter Enumerate { - get { return enumerate; } + get + { + return enumerate; + } + set { { enumerate = value; } } } + private SwitchParameter enumerate; /// /// The following is the definition of the input parameter "Filter". - /// Indicates the filter expression for the enumeration + /// Indicates the filter expression for the enumeration. /// [Parameter(ParameterSetName = "Enumerate")] [ValidateNotNullOrEmpty] - public String Filter + public string Filter { - get { return filter; } + get + { + return filter; + } + set { { filter = value; } } } - private String filter; + + private string filter; /// /// The following is the definition of the input parameter "Fragment". /// Specifies a section inside the instance that is to be updated or retrieved - /// for the given operation + /// for the given operation. /// - [Parameter(ParameterSetName = "GetInstance")] [ValidateNotNullOrEmpty] - public String Fragment + public string Fragment { - get { return fragment; } + get + { + return fragment; + } + set { { fragment = value; } } } - private String fragment; + + private string fragment; /// /// The following is the definition of the input parameter "OptionSet". - /// OptionSet is a hashtable and is used to pass a set of switches to the + /// OptionSet is a hashtable and is used to pass a set of switches to the /// service to modify or refine the nature of the request. /// [Parameter(ValueFromPipeline = true, @@ -192,12 +229,17 @@ public String Fragment [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")] public Hashtable OptionSet { - get { return optionset; } + get + { + return optionset; + } + set { { optionset = value; } } } + private Hashtable optionset; /// @@ -206,37 +248,46 @@ public Hashtable OptionSet /// [Parameter(ParameterSetName = "Enumerate")] [Parameter(ParameterSetName = "GetInstance")] - public Int32 Port + public int Port { - get { return port; } + get + { + return port; + } + set { { port = value; } } } - private Int32 port = 0; + + private int port = 0; /// /// The following is the definition of the input parameter "Associations". /// Associations indicates retrieval of association instances as opposed to /// associated instances. This can only be used when specifying the Dialect as - /// Association + /// Association. /// - [Parameter(ParameterSetName = "Enumerate")] public SwitchParameter Associations { - get { return associations; } + get + { + return associations; + } + set { { associations = value; } } } + private SwitchParameter associations; /// /// The following is the definition of the input parameter "ResourceURI". - /// URI of the resource class/instance representation + /// URI of the resource class/instance representation. /// [Parameter(Mandatory = true, Position = 0, @@ -247,12 +298,17 @@ public SwitchParameter Associations [Alias("RURI")] public Uri ResourceURI { - get { return resourceuri; } + get + { + return resourceuri; + } + set { { resourceuri = value; } } } + private Uri resourceuri; /// @@ -265,28 +321,33 @@ public Uri ResourceURI /// are returned. EPRs contain information about the Resource URI and selectors /// for the instance /// If ObjectAndEPR is specified, then both the object and the associated EPRs - /// are returned + /// are returned. /// [Parameter(ParameterSetName = "Enumerate")] [ValidateNotNullOrEmpty] - [ValidateSetAttribute(new string[] { "object", "epr", "objectandepr" })] + [ValidateSet(new string[] { "object", "epr", "objectandepr" })] [Alias("RT")] - public String ReturnType + public string ReturnType { - get { return returntype; } + get + { + return returntype; + } + set { { returntype = value; } } } - private String returntype="object"; + + private string returntype = "object"; /// /// The following is the definition of the input parameter "SelectorSet". /// SelectorSet is a hash table which helps in identify an instance of the - /// management resource if there are are more than 1 instance of the resource - /// class + /// management resource if there are more than 1 instance of the resource + /// class. /// [Parameter( ParameterSetName = "GetInstance")] @@ -294,18 +355,23 @@ public String ReturnType [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")] public Hashtable SelectorSet { - get { return selectorset; } + get + { + return selectorset; + } + set { { selectorset = value; } } } + private Hashtable selectorset; /// /// The following is the definition of the input parameter "SessionOption". /// Defines a set of extended options for the WSMan session. This can be - /// created by using the cmdlet New-WSManSessionOption + /// created by using the cmdlet New-WSManSessionOption. /// [Parameter] [ValidateNotNullOrEmpty] @@ -313,30 +379,40 @@ public Hashtable SelectorSet [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")] public SessionOption SessionOption { - get { return sessionoption; } + get + { + return sessionoption; + } + set { { sessionoption = value; } } } + private SessionOption sessionoption; /// /// The following is the definition of the input parameter "Shallow". /// Enumerate only instances of the base class specified in the resource URI. If /// this flag is not specified, instances of the base class specified in the URI - /// and all its derived classes are returned + /// and all its derived classes are returned. /// [Parameter(ParameterSetName = "Enumerate")] public SwitchParameter Shallow { - get { return shallow; } + get + { + return shallow; + } + set { { shallow = value; } } } + private SwitchParameter shallow; /// @@ -352,37 +428,45 @@ public SwitchParameter Shallow [Alias("SSL")] public SwitchParameter UseSSL { - get { return usessl; } + get + { + return usessl; + } + set { { usessl = value; } } } + private SwitchParameter usessl; #endregion parameter - # region private - WSManHelper helper; + #region private + private WSManHelper helper; + private string GetFilter() { string name; string value; - string[] Split = filter.Trim().Split(new Char[] { '=', ';' }); - if ((Split.Length)%2 != 0) + string[] Split = filter.Trim().Split(new char[] { '=', ';' }); + if ((Split.Length) % 2 != 0) { - //mismatched property name/value pair + // mismatched property name/value pair return null; } + filter = ""; - for (int i = 0; i" + value + ""; } - filter = filter + ""; - return (filter.ToString()); + + filter += ""; + return (filter); } private void ReturnEnumeration(IWSManEx wsmanObject, IWSManResourceLocator wsmanResourceLocator, IWSManSession wsmanSession) @@ -394,17 +478,17 @@ private void ReturnEnumeration(IWSManEx wsmanObject, IWSManResourceLocator wsman IWSManEnumerator obj; if (returntype != null) { - if (returntype.Equals("object", StringComparison.CurrentCultureIgnoreCase)) + if (returntype.Equals("object", StringComparison.OrdinalIgnoreCase)) { flags = wsmanObject.EnumerationFlagReturnObject(); } - else if (returntype.Equals("epr", StringComparison.CurrentCultureIgnoreCase)) + else if (returntype.Equals("epr", StringComparison.OrdinalIgnoreCase)) { - flags = wsmanObject.EnumerationFlagReturnEPR(); + flags = wsmanObject.EnumerationFlagReturnEPR(); } else { - flags = wsmanObject.EnumerationFlagReturnObjectAndEPR(); + flags = wsmanObject.EnumerationFlagReturnObjectAndEPR(); } } @@ -420,44 +504,46 @@ private void ReturnEnumeration(IWSManEx wsmanObject, IWSManResourceLocator wsman { flags |= wsmanObject.EnumerationFlagHierarchyDeep(); } + if (dialect != null && filter != null) { - - if (dialect.ToString().Equals(helper.ALIAS_WQL, StringComparison.CurrentCultureIgnoreCase) || dialect.ToString().Equals(helper.URI_WQL_DIALECT, StringComparison.CurrentCultureIgnoreCase)) + if (dialect.ToString().Equals(helper.ALIAS_WQL, StringComparison.OrdinalIgnoreCase) || dialect.ToString().Equals(helper.URI_WQL_DIALECT, StringComparison.OrdinalIgnoreCase)) { fragment = helper.URI_WQL_DIALECT; dialect = new Uri(fragment); } - else if (dialect.ToString().Equals(helper.ALIAS_ASSOCIATION, StringComparison.CurrentCultureIgnoreCase) || dialect.ToString().Equals(helper.URI_ASSOCIATION_DIALECT, StringComparison.CurrentCultureIgnoreCase)) + else if (dialect.ToString().Equals(helper.ALIAS_ASSOCIATION, StringComparison.OrdinalIgnoreCase) || dialect.ToString().Equals(helper.URI_ASSOCIATION_DIALECT, StringComparison.OrdinalIgnoreCase)) { - if (associations) - { - flags |= wsmanObject.EnumerationFlagAssociationInstance(); - } - else - { - flags |= wsmanObject.EnumerationFlagAssociatedInstance(); - } - fragment = helper.URI_ASSOCIATION_DIALECT; - dialect = new Uri(fragment); + if (associations) + { + flags |= wsmanObject.EnumerationFlagAssociationInstance(); + } + else + { + flags |= wsmanObject.EnumerationFlagAssociatedInstance(); + } + + fragment = helper.URI_ASSOCIATION_DIALECT; + dialect = new Uri(fragment); } - else if (dialect.ToString().Equals(helper.ALIAS_SELECTOR, StringComparison.CurrentCultureIgnoreCase) || dialect.ToString().Equals(helper.URI_SELECTOR_DIALECT, StringComparison.CurrentCultureIgnoreCase)) + else if (dialect.ToString().Equals(helper.ALIAS_SELECTOR, StringComparison.OrdinalIgnoreCase) || dialect.ToString().Equals(helper.URI_SELECTOR_DIALECT, StringComparison.OrdinalIgnoreCase)) { - filter = GetFilter(); - fragment = helper.URI_SELECTOR_DIALECT; - dialect = new Uri(fragment); + filter = GetFilter(); + fragment = helper.URI_SELECTOR_DIALECT; + dialect = new Uri(fragment); } + obj = (IWSManEnumerator)wsmanSession.Enumerate(wsmanResourceLocator, filter, dialect.ToString(), flags); } else if (filter != null) { - fragment = helper.URI_WQL_DIALECT; - dialect = new Uri(fragment); - obj = (IWSManEnumerator)wsmanSession.Enumerate(wsmanResourceLocator, filter, dialect.ToString(), flags); + fragment = helper.URI_WQL_DIALECT; + dialect = new Uri(fragment); + obj = (IWSManEnumerator)wsmanSession.Enumerate(wsmanResourceLocator, filter, dialect.ToString(), flags); } else { - obj = (IWSManEnumerator)wsmanSession.Enumerate(wsmanResourceLocator, filter, null, flags); + obj = (IWSManEnumerator)wsmanSession.Enumerate(wsmanResourceLocator, filter, null, flags); } while (!obj.AtEndOfStream) { @@ -472,8 +558,8 @@ private void ReturnEnumeration(IWSManEx wsmanObject, IWSManResourceLocator wsman WriteError(er); } } - # endregion private - # region override + #endregion private + #region override /// /// ProcessRecord method. /// @@ -489,17 +575,17 @@ protected override void ProcessRecord() { try { - //in the format http(s)://server[:port/applicationname] - string[] constrsplit = connectionuri.OriginalString.Split(new string[] { ":" + port + "/" + applicationname }, StringSplitOptions.None); - string[] constrsplit1 = constrsplit[0].Split(new string[] { "//" }, StringSplitOptions.None); + // in the format http(s)://server[:port/applicationname] + string[] constrsplit = connectionuri.OriginalString.Split(":" + port + "/" + applicationname, StringSplitOptions.None); + string[] constrsplit1 = constrsplit[0].Split("//", StringSplitOptions.None); computername = constrsplit1[1].Trim(); } catch (IndexOutOfRangeException) { helper.AssertError(helper.GetResourceMsgFromResourcetext("NotProperURI"), false, connectionuri); } - } + try { IWSManResourceLocator m_resource = helper.InitializeResourceLocator(optionset, selectorset, fragment, dialect, m_wsmanObject, resourceuri); @@ -512,7 +598,7 @@ protected override void ProcessRecord() { xmldoc.LoadXml(m_session.Get(m_resource, 0)); } - catch(XmlException ex) + catch (XmlException ex) { helper.AssertError(ex.Message, false, computername); } @@ -537,18 +623,19 @@ protected override void ProcessRecord() helper.AssertError(ex.Message, false, computername); } } - } finally { - if (!String.IsNullOrEmpty(m_wsmanObject.Error)) + if (!string.IsNullOrEmpty(m_wsmanObject.Error)) { helper.AssertError(m_wsmanObject.Error, true, resourceuri); } - if (!String.IsNullOrEmpty(m_session.Error)) + + if (!string.IsNullOrEmpty(m_session.Error)) { helper.AssertError(m_session.Error, true, resourceuri); } + if (m_session != null) Dispose(m_session); } @@ -557,17 +644,17 @@ protected override void ProcessRecord() #region IDisposable Members /// - /// public dispose method + /// Public dispose method. /// public void Dispose() { - //CleanUp(); + // CleanUp(); GC.SuppressFinalize(this); } /// - /// public dispose method + /// Public dispose method. /// public void @@ -579,17 +666,13 @@ protected override void ProcessRecord() #endregion IDisposable Members - /// /// BeginProcessing method. /// protected override void EndProcessing() { - helper.CleanUp(); } - - } #endregion @@ -602,10 +685,10 @@ protected override void EndProcessing() /// Set-WSManInstance -Action StartService -ResourceURI wmicimv2/Win32_Service /// -SelectorSet {Name=Spooler} /// - [Cmdlet(VerbsCommon.Set, "WSManInstance", DefaultParameterSetName = "ComputerName", HelpUri = "https://go.microsoft.com/fwlink/?LinkId=141458")] + [Cmdlet(VerbsCommon.Set, "WSManInstance", DefaultParameterSetName = "ComputerName", HelpUri = "https://go.microsoft.com/fwlink/?LinkId=2096937")] + [OutputType(typeof(XmlElement), typeof(string))] public class SetWSManInstanceCommand : AuthenticatingWSManCommand, IDisposable { - #region Parameters /// /// The following is the definition of the input parameter "ApplicationName". @@ -613,12 +696,14 @@ public class SetWSManInstanceCommand : AuthenticatingWSManCommand, IDisposable /// [Parameter(ParameterSetName = "ComputerName")] [ValidateNotNullOrEmpty] - public String ApplicationName + public string ApplicationName { get { return applicationname; } + set { applicationname = value; } } - private String applicationname = null; + + private string applicationname = null; /// /// The following is the definition of the input parameter "ComputerName". @@ -628,19 +713,24 @@ public String ApplicationName /// [Parameter(ParameterSetName = "ComputerName")] [Alias("cn")] - public String ComputerName + public string ComputerName { - get { return computername; } + get + { + return computername; + } + set { computername = value; - if ((string.IsNullOrEmpty(computername)) || (computername.Equals(".", StringComparison.CurrentCultureIgnoreCase))) + if ((string.IsNullOrEmpty(computername)) || (computername.Equals(".", StringComparison.OrdinalIgnoreCase))) { computername = "localhost"; } } } - private String computername = null; + + private string computername = null; /// /// The following is the definition of the input parameter "ConnectionURI". @@ -654,60 +744,67 @@ public String ComputerName public Uri ConnectionURI { get { return connectionuri; } + set { connectionuri = value; } } + private Uri connectionuri; /// /// The following is the definition of the input parameter "Dialect". - /// Defines the dialect for the filter predicate + /// Defines the dialect for the filter predicate. /// [Parameter] [ValidateNotNullOrEmpty] public Uri Dialect { get { return dialect; } + set { dialect = value; } } + private Uri dialect; /// /// The following is the definition of the input parameter "FilePath". /// Updates the management resource specified by the ResourceURI and SelectorSet - /// via this input file + /// via this input file. /// [Parameter(ValueFromPipelineByPropertyName = true)] + [Alias("Path")] [ValidateNotNullOrEmpty] public string FilePath { get { return filepath; } + set { filepath = value; } } - private string filepath; + private string filepath; /// /// The following is the definition of the input parameter "Fragment". /// Specifies a section inside the instance that is to be updated or retrieved - /// for the given operation + /// for the given operation. /// [Parameter(ParameterSetName = "ComputerName")] [Parameter(ParameterSetName = "URI")] [ValidateNotNullOrEmpty] - public String Fragment + public string Fragment { get { return fragment; } + set { fragment = value; } } - private String fragment; + + private string fragment; /// /// The following is the definition of the input parameter "OptionSet". /// OptionSet is a hahs table which help modify or refine the nature of the /// request. These are similar to switches used in command line shells in that - /// they are service-specific + /// they are service-specific. /// - [Parameter] [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")] [Alias("os")] @@ -715,8 +812,10 @@ public String Fragment public Hashtable OptionSet { get { return optionset; } + set { optionset = value; } } + private Hashtable optionset; /// @@ -725,19 +824,20 @@ public Hashtable OptionSet /// [Parameter(ParameterSetName = "ComputerName")] [ValidateNotNullOrEmpty] - [ValidateRange(1, Int32.MaxValue)] - public Int32 Port + [ValidateRange(1, int.MaxValue)] + public int Port { get { return port; } + set { port = value; } } - private Int32 port = 0; + + private int port = 0; /// /// The following is the definition of the input parameter "ResourceURI". - /// URI of the resource class/instance representation + /// URI of the resource class/instance representation. /// - [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "URI")] [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Resourceuri")] @@ -747,15 +847,17 @@ public Int32 Port public Uri ResourceURI { get { return resourceuri; } + set { resourceuri = value; } } + private Uri resourceuri; /// /// The following is the definition of the input parameter "SelectorSet". /// SelectorSet is a hash table which helps in identify an instance of the - /// management resource if there are are more than 1 instance of the resource - /// class + /// management resource if there are more than 1 instance of the resource + /// class. /// [Parameter(Position = 1, ValueFromPipeline = true, @@ -765,14 +867,16 @@ public Uri ResourceURI public Hashtable SelectorSet { get { return selectorset; } + set { selectorset = value; } } + private Hashtable selectorset; /// /// The following is the definition of the input parameter "SessionOption". /// Defines a set of extended options for the WSMan session. This can be created - /// by using the cmdlet New-WSManSessionOption + /// by using the cmdlet New-WSManSessionOption. /// [Parameter] [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")] @@ -781,8 +885,10 @@ public Hashtable SelectorSet public SessionOption SessionOption { get { return sessionoption; } + set { sessionoption = value; } } + private SessionOption sessionoption; /// @@ -797,8 +903,10 @@ public SessionOption SessionOption public SwitchParameter UseSSL { get { return usessl; } + set { usessl = value; } } + private SwitchParameter usessl; /// @@ -812,15 +920,15 @@ public SwitchParameter UseSSL public Hashtable ValueSet { get { return valueset; } + set { valueset = value; } } - private Hashtable valueset; - + private Hashtable valueset; #endregion - private WSManHelper helper ; + private WSManHelper helper; /// /// ProcessRecord method. /// @@ -831,36 +939,35 @@ protected override void ProcessRecord() helper.WSManOp = "set"; IWSManSession m_session = null; - if (dialect != null) { - if (dialect.ToString().Equals(helper.ALIAS_WQL, StringComparison.CurrentCultureIgnoreCase)) + if (dialect.ToString().Equals(helper.ALIAS_WQL, StringComparison.OrdinalIgnoreCase)) dialect = new Uri(helper.URI_WQL_DIALECT); - if (dialect.ToString().Equals(helper.ALIAS_SELECTOR, StringComparison.CurrentCultureIgnoreCase)) + if (dialect.ToString().Equals(helper.ALIAS_SELECTOR, StringComparison.OrdinalIgnoreCase)) dialect = new Uri(helper.URI_SELECTOR_DIALECT); - if (dialect.ToString().Equals(helper.ALIAS_ASSOCIATION, StringComparison.CurrentCultureIgnoreCase)) + if (dialect.ToString().Equals(helper.ALIAS_ASSOCIATION, StringComparison.OrdinalIgnoreCase)) dialect = new Uri(helper.URI_ASSOCIATION_DIALECT); } try { - string connectionStr = String.Empty; + string connectionStr = string.Empty; connectionStr = helper.CreateConnectionString(connectionuri, port, computername, applicationname); if (connectionuri != null) { try { - //in the format http(s)://server[:port/applicationname] - string[] constrsplit = connectionuri.OriginalString.Split(new string[] { ":" + port + "/" + applicationname }, StringSplitOptions.None); - string[] constrsplit1 = constrsplit[0].Split(new string[] { "//" }, StringSplitOptions.None); + // in the format http(s)://server[:port/applicationname] + string[] constrsplit = connectionuri.OriginalString.Split(":" + port + "/" + applicationname, StringSplitOptions.None); + string[] constrsplit1 = constrsplit[0].Split("//", StringSplitOptions.None); computername = constrsplit1[1].Trim(); } catch (IndexOutOfRangeException) { helper.AssertError(helper.GetResourceMsgFromResourcetext("NotProperURI"), false, connectionuri); } - } + IWSManResourceLocator m_resource = helper.InitializeResourceLocator(optionset, selectorset, fragment, dialect, m_wsmanObject, resourceuri); m_session = helper.CreateSessionObject(m_wsmanObject, Authentication, sessionoption, Credential, connectionStr, CertificateThumbprint, usessl.IsPresent); string rootNode = helper.GetRootNodeName(helper.WSManOp, m_resource.ResourceUri, null); @@ -871,7 +978,7 @@ protected override void ProcessRecord() { xmldoc.LoadXml(m_session.Put(m_resource, input, 0)); } - catch(XmlException ex) + catch (XmlException ex) { helper.AssertError(ex.Message, false, computername); } @@ -882,44 +989,45 @@ protected override void ProcessRecord() { foreach (XmlNode node in xmldoc.DocumentElement.ChildNodes) { - if (node.Name.Equals(fragment, StringComparison.CurrentCultureIgnoreCase)) + if (node.Name.Equals(fragment, StringComparison.OrdinalIgnoreCase)) WriteObject(node.Name + " = " + node.InnerText); } } } else WriteObject(xmldoc.DocumentElement); - } finally { - if (!String.IsNullOrEmpty(m_wsmanObject.Error)) + if (!string.IsNullOrEmpty(m_wsmanObject.Error)) { helper.AssertError(m_wsmanObject.Error, true, resourceuri); } - if (!String.IsNullOrEmpty(m_session.Error)) + + if (!string.IsNullOrEmpty(m_session.Error)) { helper.AssertError(m_session.Error, true, resourceuri); } + if (m_session != null) Dispose(m_session); } - }//End ProcessRecord() + } #region IDisposable Members /// - /// public dispose method + /// Public dispose method. /// public void Dispose() { - //CleanUp(); + // CleanUp(); GC.SuppressFinalize(this); } /// - /// public dispose method + /// Public dispose method. /// public void @@ -938,14 +1046,10 @@ protected override void EndProcessing() { helper.CleanUp(); } - } - - #endregion - #region Remove-WsManInstance /// @@ -955,10 +1059,9 @@ protected override void EndProcessing() /// Set-WSManInstance -Action StartService -ResourceURI wmicimv2/Win32_Service /// -SelectorSet {Name=Spooler} /// - [Cmdlet(VerbsCommon.Remove, "WSManInstance", DefaultParameterSetName = "ComputerName", HelpUri = "https://go.microsoft.com/fwlink/?LinkId=141453")] + [Cmdlet(VerbsCommon.Remove, "WSManInstance", DefaultParameterSetName = "ComputerName", HelpUri = "https://go.microsoft.com/fwlink/?LinkId=2096721")] public class RemoveWSManInstanceCommand : AuthenticatingWSManCommand, IDisposable { - #region Parameters /// /// The following is the definition of the input parameter "ApplicationName". @@ -966,12 +1069,14 @@ public class RemoveWSManInstanceCommand : AuthenticatingWSManCommand, IDisposabl /// [Parameter(ParameterSetName = "ComputerName")] [ValidateNotNullOrEmpty] - public String ApplicationName + public string ApplicationName { get { return applicationname; } + set { applicationname = value; } } - private String applicationname = null; + + private string applicationname = null; /// /// The following is the definition of the input parameter "ComputerName". @@ -981,19 +1086,24 @@ public String ApplicationName /// [Parameter(ParameterSetName = "ComputerName")] [Alias("cn")] - public String ComputerName + public string ComputerName { - get { return computername; } + get + { + return computername; + } + set { computername = value; - if ((string.IsNullOrEmpty(computername)) || (computername.Equals(".", StringComparison.CurrentCultureIgnoreCase))) + if ((string.IsNullOrEmpty(computername)) || (computername.Equals(".", StringComparison.OrdinalIgnoreCase))) { computername = "localhost"; } } } - private String computername = null; + + private string computername = null; /// /// The following is the definition of the input parameter "ConnectionURI". @@ -1007,17 +1117,18 @@ public String ComputerName public Uri ConnectionURI { get { return connectionuri; } + set { connectionuri = value; } } + private Uri connectionuri; /// /// The following is the definition of the input parameter "OptionSet". /// OptionSet is a hahs table which help modify or refine the nature of the /// request. These are similar to switches used in command line shells in that - /// they are service-specific + /// they are service-specific. /// - [Parameter] [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")] [Alias("os")] @@ -1025,8 +1136,10 @@ public Uri ConnectionURI public Hashtable OptionSet { get { return optionset; } + set { optionset = value; } } + private Hashtable optionset; /// @@ -1035,19 +1148,20 @@ public Hashtable OptionSet /// [Parameter(ParameterSetName = "ComputerName")] [ValidateNotNullOrEmpty] - [ValidateRange(1, Int32.MaxValue)] - public Int32 Port + [ValidateRange(1, int.MaxValue)] + public int Port { get { return port; } + set { port = value; } } - private Int32 port = 0; + + private int port = 0; /// /// The following is the definition of the input parameter "ResourceURI". - /// URI of the resource class/instance representation + /// URI of the resource class/instance representation. /// - [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "URI")] [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Resourceuri")] @@ -1057,15 +1171,17 @@ public Int32 Port public Uri ResourceURI { get { return resourceuri; } + set { resourceuri = value; } } + private Uri resourceuri; /// /// The following is the definition of the input parameter "SelectorSet". /// SelectorSet is a hash table which helps in identify an instance of the - /// management resource if there are are more than 1 instance of the resource - /// class + /// management resource if there are more than 1 instance of the resource + /// class. /// [Parameter(Position = 1, Mandatory = true, ValueFromPipeline = true, @@ -1075,14 +1191,16 @@ public Uri ResourceURI public Hashtable SelectorSet { get { return selectorset; } + set { selectorset = value; } } + private Hashtable selectorset; /// /// The following is the definition of the input parameter "SessionOption". /// Defines a set of extended options for the WSMan session. This can be created - /// by using the cmdlet New-WSManSessionOption + /// by using the cmdlet New-WSManSessionOption. /// [Parameter] [SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")] @@ -1091,8 +1209,10 @@ public Hashtable SelectorSet public SessionOption SessionOption { get { return sessionoption; } + set { sessionoption = value; } } + private SessionOption sessionoption; /// @@ -1107,16 +1227,14 @@ public SessionOption SessionOption public SwitchParameter UseSSL { get { return usessl; } + set { usessl = value; } } - private SwitchParameter usessl; - - + private SwitchParameter usessl; #endregion - /// /// ProcessRecord method. /// @@ -1128,23 +1246,23 @@ protected override void ProcessRecord() IWSManSession m_session = null; try { - string connectionStr = String.Empty; + string connectionStr = string.Empty; connectionStr = helper.CreateConnectionString(connectionuri, port, computername, applicationname); if (connectionuri != null) { try { - //in the format http(s)://server[:port/applicationname] - string[] constrsplit = connectionuri.OriginalString.Split(new string[] { ":" + port + "/" + applicationname }, StringSplitOptions.None); - string[] constrsplit1 = constrsplit[0].Split(new string[] { "//" }, StringSplitOptions.None); + // in the format http(s)://server[:port/applicationname] + string[] constrsplit = connectionuri.OriginalString.Split(":" + port + "/" + applicationname, StringSplitOptions.None); + string[] constrsplit1 = constrsplit[0].Split("//", StringSplitOptions.None); computername = constrsplit1[1].Trim(); } catch (IndexOutOfRangeException) { helper.AssertError(helper.GetResourceMsgFromResourcetext("NotProperURI"), false, connectionuri); } - } + IWSManResourceLocator m_resource = helper.InitializeResourceLocator(optionset, selectorset, null, null, m_wsmanObject, resourceuri); m_session = helper.CreateSessionObject(m_wsmanObject, Authentication, sessionoption, Credential, connectionStr, CertificateThumbprint, usessl.IsPresent); string ResourceURI = helper.GetURIWithFilter(resourceuri.ToString(), null, selectorset, helper.WSManOp); @@ -1156,39 +1274,38 @@ protected override void ProcessRecord() { helper.AssertError(ex.Message, false, computername); } - } finally { - if (!String.IsNullOrEmpty(m_session.Error)) + if (!string.IsNullOrEmpty(m_session.Error)) { helper.AssertError(m_session.Error, true, resourceuri); } - if (!String.IsNullOrEmpty(m_wsmanObject.Error)) + + if (!string.IsNullOrEmpty(m_wsmanObject.Error)) { helper.AssertError(m_wsmanObject.Error, true, resourceuri); } + if (m_session != null) Dispose(m_session); - } - - }//End ProcessRecord() + } #region IDisposable Members /// - /// public dispose method + /// Public dispose method. /// public void Dispose() { - //CleanUp(); + // CleanUp(); GC.SuppressFinalize(this); } /// - /// public dispose method + /// Public dispose method. /// public void @@ -1199,20 +1316,17 @@ protected override void ProcessRecord() } #endregion IDisposable Members - - } - #endregion #region New-WsManInstance /// /// Creates an instance of a management resource identified by the resource URI - /// using specified ValueSet or input File + /// using specified ValueSet or input File. /// - - [Cmdlet(VerbsCommon.New, "WSManInstance", DefaultParameterSetName = "ComputerName", HelpUri = "https://go.microsoft.com/fwlink/?LinkId=141448")] + [Cmdlet(VerbsCommon.New, "WSManInstance", DefaultParameterSetName = "ComputerName", HelpUri = "https://go.microsoft.com/fwlink/?LinkId=2096933")] + [OutputType(typeof(XmlElement))] public class NewWSManInstanceCommand : AuthenticatingWSManCommand, IDisposable { /// @@ -1221,12 +1335,14 @@ public class NewWSManInstanceCommand : AuthenticatingWSManCommand, IDisposable /// [Parameter(ParameterSetName = "ComputerName")] [ValidateNotNullOrEmpty] - public String ApplicationName + public string ApplicationName { get { return applicationname; } + set { applicationname = value; } } - private String applicationname = null; + + private string applicationname = null; /// /// The following is the definition of the input parameter "ComputerName". @@ -1236,19 +1352,24 @@ public String ApplicationName /// [Parameter(ParameterSetName = "ComputerName")] [Alias("cn")] - public String ComputerName + public string ComputerName { - get { return computername; } + get + { + return computername; + } + set { computername = value; - if ((string.IsNullOrEmpty(computername)) || (computername.Equals(".", StringComparison.CurrentCultureIgnoreCase))) + if ((string.IsNullOrEmpty(computername)) || (computername.Equals(".", StringComparison.OrdinalIgnoreCase))) { computername = "localhost"; } } } - private String computername = null; + + private string computername = null; /// /// The following is the definition of the input parameter "ConnectionURI". @@ -1263,23 +1384,28 @@ public String ComputerName public Uri ConnectionURI { get { return connectionuri; } + set { connectionuri = value; } } + private Uri connectionuri; /// /// The following is the definition of the input parameter "FilePath". /// Updates the management resource specified by the ResourceURI and SelectorSet - /// via this input file + /// via this input file. /// [Parameter] [ValidateNotNullOrEmpty] - public String FilePath + [Alias("Path")] + public string FilePath { get { return filepath; } + set { filepath = value; } } - private String filepath; + + private string filepath; /// /// The following is the definition of the input parameter "OptionSet". @@ -1293,8 +1419,10 @@ public String FilePath public Hashtable OptionSet { get { return optionset; } + set { optionset = value; } } + private Hashtable optionset; /// @@ -1303,17 +1431,19 @@ public Hashtable OptionSet /// [Parameter(ParameterSetName = "ComputerName")] [ValidateNotNullOrEmpty] - [ValidateRange(1, Int32.MaxValue)] - public Int32 Port + [ValidateRange(1, int.MaxValue)] + public int Port { get { return port; } + set { port = value; } } - private Int32 port = 0; + + private int port = 0; /// /// The following is the definition of the input parameter "ResourceURI". - /// URI of the resource class/instance representation + /// URI of the resource class/instance representation. /// [Parameter(Mandatory = true, Position = 0)] [ValidateNotNullOrEmpty] @@ -1322,15 +1452,17 @@ public Int32 Port public Uri ResourceURI { get { return resourceuri; } + set { resourceuri = value; } } + private Uri resourceuri; /// /// The following is the definition of the input parameter "SelectorSet". /// SelectorSet is a hash table which helps in identify an instance of the - /// management resource if there are are more than 1 instance of the resource - /// class + /// management resource if there are more than 1 instance of the resource + /// class. /// [Parameter(Mandatory = true, Position = 1, ValueFromPipeline = true)] @@ -1339,8 +1471,10 @@ public Uri ResourceURI public Hashtable SelectorSet { get { return selectorset; } + set { selectorset = value; } } + private Hashtable selectorset; /// @@ -1354,8 +1488,10 @@ public Hashtable SelectorSet public SessionOption SessionOption { get { return sessionoption; } + set { sessionoption = value; } } + private SessionOption sessionoption; /// @@ -1369,8 +1505,10 @@ public SessionOption SessionOption public SwitchParameter UseSSL { get { return usessl; } + set { usessl = value; } } + private SwitchParameter usessl; /// @@ -1383,41 +1521,40 @@ public SwitchParameter UseSSL public Hashtable ValueSet { get { return valueset; } + set { valueset = value; } } + private Hashtable valueset; private WSManHelper helper; - IWSManEx m_wsmanObject = (IWSManEx)new WSManClass(); - IWSManSession m_session = null; - string connectionStr = String.Empty; + private readonly IWSManEx m_wsmanObject = (IWSManEx)new WSManClass(); + private IWSManSession m_session = null; + private string connectionStr = string.Empty; /// /// BeginProcessing method. /// protected override void BeginProcessing() { - helper = new WSManHelper(this ); + helper = new WSManHelper(this); helper.WSManOp = "new"; connectionStr = helper.CreateConnectionString(connectionuri, port, computername, applicationname); if (connectionuri != null) { try { - //in the format http(s)://server[:port/applicationname] - string[] constrsplit = connectionuri.OriginalString.Split(new string[] { ":" + port + "/" + applicationname }, StringSplitOptions.None); - string[] constrsplit1 = constrsplit[0].Split(new string[] { "//" }, StringSplitOptions.None); + // in the format http(s)://server[:port/applicationname] + string[] constrsplit = connectionuri.OriginalString.Split(":" + port + "/" + applicationname, StringSplitOptions.None); + string[] constrsplit1 = constrsplit[0].Split("//", StringSplitOptions.None); computername = constrsplit1[1].Trim(); } catch (IndexOutOfRangeException) { helper.AssertError(helper.GetResourceMsgFromResourcetext("NotProperURI"), false, connectionuri); } - } - - }//End BeginProcessing() - + } /// /// ProcessRecord method. @@ -1427,7 +1564,7 @@ protected override void ProcessRecord() try { IWSManResourceLocator m_resource = helper.InitializeResourceLocator(optionset, selectorset, null, null, m_wsmanObject, resourceuri); - //create the session object + // create the session object m_session = helper.CreateSessionObject(m_wsmanObject, Authentication, sessionoption, Credential, connectionStr, CertificateThumbprint, usessl.IsPresent); string rootNode = helper.GetRootNodeName(helper.WSManOp, m_resource.ResourceUri, null); string input = helper.ProcessInput(m_wsmanObject, filepath, helper.WSManOp, rootNode, valueset, m_resource, m_session); @@ -1446,36 +1583,37 @@ protected override void ProcessRecord() } finally { - if (!String.IsNullOrEmpty(m_wsmanObject.Error)) + if (!string.IsNullOrEmpty(m_wsmanObject.Error)) { helper.AssertError(m_wsmanObject.Error, true, resourceuri); } - if (!String.IsNullOrEmpty(m_session.Error)) + + if (!string.IsNullOrEmpty(m_session.Error)) { helper.AssertError(m_session.Error, true, resourceuri); } + if (m_session != null) { Dispose(m_session); } } - - }//End ProcessRecord() + } #region IDisposable Members /// - /// public dispose method + /// Public dispose method. /// public void Dispose() { - //CleanUp(); + // CleanUp(); GC.SuppressFinalize(this); } /// - /// public dispose method + /// Public dispose method. /// public void @@ -1493,9 +1631,8 @@ protected override void ProcessRecord() protected override void EndProcessing() { helper.CleanUp(); - - }//End EndProcessing() - }//End Class + } + } #endregion } diff --git a/src/Microsoft.WSMan.Management/WsManHelper.cs b/src/Microsoft.WSMan.Management/WsManHelper.cs index d33423ddf00..9249c7f4b88 100644 --- a/src/Microsoft.WSMan.Management/WsManHelper.cs +++ b/src/Microsoft.WSMan.Management/WsManHelper.cs @@ -1,37 +1,35 @@ -// -// Copyright (C) Microsoft. All rights reserved. -// +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + using System; -using System.Collections.Generic; -using System.Text; -using System.IO; -using System.Text.RegularExpressions; using System.Collections; -using System.Xml; -using System.Runtime.InteropServices; -using System.Reflection; -using System.Resources; -using System.Globalization; +using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -using Microsoft.Win32; +using System.Globalization; +using System.IO; using System.Management.Automation; using System.Management.Automation.Provider; +using System.Reflection; +using System.Resources; +using System.Runtime.InteropServices; +using System.Text; +using System.Text.RegularExpressions; using System.Threading; -#if CORECLR -using System.Xml.XPath; -#endif +using System.Xml; + +using Microsoft.Win32; namespace Microsoft.WSMan.Management { [SuppressMessage("Microsoft.Design", "CA1054:UriParametersShouldNotBeStrings", MessageId = "0#")] - internal class WSManHelper + internal sealed class WSManHelper { - //regular expressions + // regular expressions private const string PTRN_URI_LAST = @"([a-z_][-a-z0-9._]*)$"; private const string PTRN_OPT = @"^-([a-z]+):(.*)"; private const string PTRN_HASH_TOK = @"\s*([\w:]+)\s*=\s*(\$null|""([^""]*)"")\s*"; - //schemas + // schemas private const string URI_IPMI = @"http://schemas.dmtf.org/wbem/wscim/1/cim-schema"; private const string URI_WMI = @"http://schemas.microsoft.com/wbem/wsman/1/wmi"; private const string NS_IPMI = @"http://schemas.dmtf.org/wbem/wscim/1/cim-schema"; @@ -44,7 +42,7 @@ internal class WSManHelper private const string ALIAS_XPATH = @"xpath"; private const string URI_XPATH_DIALECT = @"http://www.w3.org/TR/1999/REC-xpath-19991116"; - //credSSP strings + // credSSP strings internal string CredSSP_RUri = "winrm/config/client/auth"; internal string CredSSP_XMLNmsp = "http://schemas.microsoft.com/wbem/wsman/1/config/client/auth"; internal string CredSSP_SNode = "/cfg:Auth/cfg:CredSSP"; @@ -58,18 +56,18 @@ internal class WSManHelper internal string Service_CredSSP_Uri = "winrm/config/service/auth"; internal string Service_CredSSP_XMLNmsp = "http://schemas.microsoft.com/wbem/wsman/1/config/service/auth"; - //gpo registry path and keys + // gpo registry path and keys internal string Registry_Path_Credentials_Delegation = @"SOFTWARE\Policies\Microsoft\Windows"; internal string Key_Allow_Fresh_Credentials = "AllowFreshCredentials"; internal string Key_Concatenate_Defaults_AllowFresh = "ConcatenateDefaults_AllowFresh"; internal string Delegate = "delegate"; internal string keyAllowcredssp = "AllowCredSSP"; - //'Constants for MS-XML + // 'Constants for MS-XML private const string NODE_ATTRIBUTE = "2"; private const int NODE_TEXT = 3; - //strings for dialects + // strings for dialects internal string ALIAS_WQL = @"wql"; internal string ALIAS_ASSOCIATION = @"association"; internal string ALIAS_SELECTOR = @"selector"; @@ -77,39 +75,38 @@ internal class WSManHelper internal string URI_SELECTOR_DIALECT = @"http://schemas.dmtf.org/wbem/wsman/1/wsman/SelectorFilter"; internal string URI_ASSOCIATION_DIALECT = @" http://schemas.dmtf.org/wbem/wsman/1/cimbinding/associationFilter"; - //string for operation + // string for operation internal string WSManOp = null; - private PSCmdlet cmdletname; - private NavigationCmdletProvider _provider; + private readonly PSCmdlet cmdletname; + private readonly NavigationCmdletProvider _provider; private FileStream _fs; private StreamReader _sr; - private static ResourceManager _resourceMgr = new ResourceManager("Microsoft.WSMan.Management.resources.WsManResources", typeof(WSManHelper).GetTypeInfo().Assembly); - + private static readonly ResourceManager _resourceMgr = new ResourceManager("Microsoft.WSMan.Management.resources.WsManResources", typeof(WSManHelper).GetTypeInfo().Assembly); // // - //Below class is just a static container which would release sessions in case this DLL is unloaded. - internal class Sessions + // Below class is just a static container which would release sessions in case this DLL is unloaded. + internal sealed class Sessions { /// - /// dictionary object to store the connection + /// Dictionary object to store the connection. /// - internal static Dictionary SessionObjCache = new Dictionary(); + internal static readonly Dictionary SessionObjCache = new Dictionary(); ~Sessions() { ReleaseSessions(); } } - internal static Sessions AutoSession = new Sessions(); + + internal static readonly Sessions AutoSession = new Sessions(); // // // - internal static void ReleaseSessions() { lock (Sessions.SessionObjCache) @@ -124,10 +121,12 @@ internal static void ReleaseSessions() } catch (ArgumentException) { - //Somehow the object was a null reference. Ignore the error + // Somehow the object was a null reference. Ignore the error } - sessionobj=null; + + sessionobj = null; } + Sessions.SessionObjCache.Clear(); } } @@ -162,7 +161,7 @@ internal string GetResourceMsgFromResourcetext(string rscname) return _resourceMgr.GetString(rscname); } - static internal string FormatResourceMsgFromResourcetextS(string rscname, + internal static string FormatResourceMsgFromResourcetextS(string rscname, params object[] args) { return FormatResourceMsgFromResourcetextS(_resourceMgr, rscname, args); @@ -174,39 +173,32 @@ internal string FormatResourceMsgFromResourcetext(string resourceName, return FormatResourceMsgFromResourcetextS(_resourceMgr, resourceName, args); } - static private string FormatResourceMsgFromResourcetextS( + private static string FormatResourceMsgFromResourcetextS( ResourceManager resourceManager, string resourceName, object[] args) { - if (resourceManager == null) - { - throw new ArgumentNullException("resourceManager"); - } - - if (String.IsNullOrEmpty(resourceName)) - { - throw new ArgumentNullException("resourceName"); - } + ArgumentNullException.ThrowIfNull(resourceManager); + ArgumentException.ThrowIfNullOrEmpty(resourceName); string template = resourceManager.GetString(resourceName); string result = null; - if (null != template) + if (template != null) { - result = String.Format(CultureInfo.CurrentCulture, + result = string.Format(CultureInfo.CurrentCulture, template, args); } + return result; } - /// - /// add a session to dictionary + /// Add a session to dictionary. /// - /// connection string - /// session object - internal void AddtoDictionary(string key, Object value) + /// Connection string. + /// Session object. + internal void AddtoDictionary(string key, object value) { key = key.ToLowerInvariant(); lock (Sessions.SessionObjCache) @@ -225,13 +217,13 @@ internal void AddtoDictionary(string key, Object value) } catch (ArgumentException) { - //Somehow the object was a null reference. Ignore the error + // Somehow the object was a null reference. Ignore the error } + Sessions.SessionObjCache.Remove(key); Sessions.SessionObjCache.Add(key, value); } } - } internal object RemoveFromDictionary(string computer) @@ -249,11 +241,13 @@ internal object RemoveFromDictionary(string computer) } catch (ArgumentException) { - //Somehow the object was a null reference. Ignore the error + // Somehow the object was a null reference. Ignore the error } + Sessions.SessionObjCache.Remove(computer); } } + return objsession; } @@ -283,6 +277,7 @@ internal static Dictionary GetSessionObjCache() catch (COMException) { } + return Sessions.SessionObjCache; } @@ -301,7 +296,7 @@ internal string GetRootNodeName(string operation, string resourceUri, string act if (operation.Equals("invoke", StringComparison.OrdinalIgnoreCase)) { sfx = "_INPUT"; - resultStr = String.Concat(actionStr, sfx); + resultStr = string.Concat(actionStr, sfx); } else { @@ -310,9 +305,10 @@ internal string GetRootNodeName(string operation, string resourceUri, string act } else { - //error + // error } } + return resultStr; } @@ -331,21 +327,19 @@ internal string ReadFile(string path) { throw new ArgumentException(GetResourceMsgFromResourcetext("InvalidFileName")); } + string strOut = null; try { - _fs = new FileStream(path, FileMode.Open, FileAccess.Read); // create stream Reader _sr = new StreamReader(_fs); strOut = _sr.ReadToEnd(); } - catch (ArgumentNullException e) { ErrorRecord er = new ErrorRecord(e, "ArgumentNullException", ErrorCategory.InvalidArgument, null); cmdletname.ThrowTerminatingError(er); - } catch (UnauthorizedAccessException e) { @@ -369,32 +363,25 @@ internal string ReadFile(string path) } finally { - if (_sr != null) - { - // _sr.Close(); - _sr.Dispose(); - } - if (_fs != null) - { - //_fs.Close(); - _fs.Dispose(); - } + _sr?.Dispose(); + _fs?.Dispose(); } + return strOut; } internal string ProcessInput(IWSManEx wsman, string filepath, string operation, string root, Hashtable valueset, IWSManResourceLocator resourceUri, IWSManSession sessionObj) { - string resultString = null; - //if file path is given + // if file path is given if (!string.IsNullOrEmpty(filepath) && valueset == null) { if (!File.Exists(filepath)) { throw new FileNotFoundException(_resourceMgr.GetString("InvalidFileName")); } + resultString = ReadFile(filepath); return resultString; } @@ -407,7 +394,7 @@ internal string ProcessInput(IWSManEx wsman, string filepath, string operation, string parameters = null, nilns = null; string xmlns = GetXmlNs(resourceUri.ResourceUri); - //if valueset is given, i.e hashtable + // if valueset is given, i.e hashtable if (valueset != null) { foreach (DictionaryEntry entry in valueset) @@ -418,9 +405,11 @@ internal string ProcessInput(IWSManEx wsman, string filepath, string operation, parameters = parameters + " " + ATTR_NIL; nilns = " " + NS_XSI; } + parameters = parameters + ">" + entry.Value.ToString() + ""; } } + resultString = "" + parameters + ""; break; @@ -438,7 +427,7 @@ internal string ProcessInput(IWSManEx wsman, string filepath, string operation, xpathString = @"/*/*[local-name()=""" + entry.Key + @"""]"; if (entry.Key.ToString().Equals("location", StringComparison.OrdinalIgnoreCase)) { - //'Ignore cim:Location + // 'Ignore cim:Location xpathString = @"/*/*[local-name()=""" + entry.Key + @""" and namespace-uri() != """ + NS_CIMBASE + @"""]"; } @@ -462,21 +451,22 @@ internal string ProcessInput(IWSManEx wsman, string filepath, string operation, } else { - XmlNode tmpNode = node.ChildNodes[0];//.Item[0]; + XmlNode tmpNode = node.ChildNodes[0]; //.Item[0]; if (!tmpNode.NodeType.ToString().Equals("text", StringComparison.OrdinalIgnoreCase)) { throw new ArgumentException(_resourceMgr.GetString("NOAttributeMatch")); } } } + if (string.IsNullOrEmpty(entry.Key.ToString())) { - //XmlNode newnode = xmlfile.CreateNode(XmlNodeType.Attribute, ATTR_NIL_NAME, NS_XSI_URI); - XmlAttribute newnode = xmlfile.CreateAttribute(XmlNodeType.Attribute.ToString(), ATTR_NIL_NAME, NS_XSI_URI); + // XmlNode newnode = xmlfile.CreateNode(XmlNodeType.Attribute, ATTR_NIL_NAME, NS_XSI_URI); + XmlAttribute newnode = xmlfile.CreateAttribute(nameof(XmlNodeType.Attribute), ATTR_NIL_NAME, NS_XSI_URI); newnode.Value = "true"; node.Attributes.Append(newnode); - //(newnode.Attributes.Item(0).FirstChild ); - node.Value = ""; + // (newnode.Attributes.Item(0).FirstChild ); + node.Value = string.Empty; } else { @@ -484,31 +474,19 @@ internal string ProcessInput(IWSManEx wsman, string filepath, string operation, node.InnerText = entry.Value.ToString(); } } + } + } - }//end for - }//end if valueset resultString = xmlfile.OuterXml; break; - }//end switch + } + return resultString; } internal string GetXmlNs(string resUri) { - - string tmpNs = null; - - if (resUri.ToLowerInvariant().Contains(URI_IPMI) || (resUri.ToLowerInvariant().Contains(URI_WMI))) - tmpNs = StripParams(resUri); - else - { - //tmpNs = StripParams(resUri) + ".xsd"; - //This was reported by Intel as an interop issue. So now we are not appending a .xsd in the end. - tmpNs = StripParams(resUri); - } - - return (@"xmlns:p=""" + tmpNs + @""""); - + return (@"xmlns:p=""" + StripParams(resUri) + @""""); } internal XmlNode GetXmlNode(string xmlString, string xpathpattern, string xmlnamespace) @@ -521,6 +499,7 @@ internal XmlNode GetXmlNode(string xmlString, string xpathpattern, string xmlnam { nsmgr.AddNamespace("cfg", xmlnamespace); } + node = xDoc.SelectSingleNode(xpathpattern, nsmgr); return node; } @@ -547,26 +526,27 @@ internal string CreateConnectionString(Uri ConnUri, int port, string computernam { ConnectionString = ConnectionString + ":" + port; } + if (applicationname != null) { ConnectionString = ConnectionString + "/" + applicationname; } } - return ConnectionString; + return ConnectionString; } internal IWSManResourceLocator InitializeResourceLocator(Hashtable optionset, Hashtable selectorset, string fragment, Uri dialect, IWSManEx wsmanObj, Uri resourceuri) { - string resource = null; if (resourceuri != null) { resource = resourceuri.ToString(); } + if (selectorset != null) { - resource = resource + "?"; + resource += "?"; int i = 0; foreach (DictionaryEntry entry in selectorset) { @@ -576,12 +556,12 @@ internal IWSManResourceLocator InitializeResourceLocator(Hashtable optionset, Ha resource += "+"; } } + IWSManResourceLocator m_resource = null; try { m_resource = (IWSManResourceLocator)wsmanObj.CreateResourceLocator(resource); - if (optionset != null) { foreach (DictionaryEntry entry in optionset) @@ -611,6 +591,7 @@ internal IWSManResourceLocator InitializeResourceLocator(Hashtable optionset, Ha { AssertError(ex.Message, false, null); } + return m_resource; } @@ -625,12 +606,12 @@ internal IWSManResourceLocator InitializeResourceLocator(Hashtable optionset, Ha /// /// If there is ambiguity as specified above. /// - static internal void ValidateSpecifiedAuthentication(AuthenticationMechanism authentication, PSCredential credential, string certificateThumbprint) + internal static void ValidateSpecifiedAuthentication(AuthenticationMechanism authentication, PSCredential credential, string certificateThumbprint) { if ((credential != null) && (certificateThumbprint != null)) { - String message = FormatResourceMsgFromResourcetextS( - "AmbiguosAuthentication", + string message = FormatResourceMsgFromResourcetextS( + "AmbiguousAuthentication", "CertificateThumbPrint", "credential"); throw new InvalidOperationException(message); @@ -640,8 +621,8 @@ static internal void ValidateSpecifiedAuthentication(AuthenticationMechanism aut (authentication != AuthenticationMechanism.ClientCertificate) && (certificateThumbprint != null)) { - String message = FormatResourceMsgFromResourcetextS( - "AmbiguosAuthentication", + string message = FormatResourceMsgFromResourcetextS( + "AmbiguousAuthentication", "CertificateThumbPrint", authentication.ToString()); throw new InvalidOperationException(message); @@ -659,46 +640,51 @@ internal IWSManSession CreateSessionObject(IWSManEx wsmanObject, AuthenticationM { if (authentication.Equals(AuthenticationMechanism.None)) { - sessionFlags = sessionFlags | (int)WSManSessionFlags.WSManFlagUseNoAuthentication; + sessionFlags |= (int)WSManSessionFlags.WSManFlagUseNoAuthentication; } + if (authentication.Equals(AuthenticationMechanism.Basic)) { sessionFlags = sessionFlags | (int)WSManSessionFlags.WSManFlagUseBasic | (int)WSManSessionFlags.WSManFlagCredUserNamePassword; } + if (authentication.Equals(AuthenticationMechanism.Negotiate)) { - sessionFlags = sessionFlags | (int)WSManSessionFlags.WSManFlagUseNegotiate; + sessionFlags |= (int)WSManSessionFlags.WSManFlagUseNegotiate; } + if (authentication.Equals(AuthenticationMechanism.Kerberos)) { - sessionFlags = sessionFlags | (int)WSManSessionFlags.WSManFlagUseKerberos; + sessionFlags |= (int)WSManSessionFlags.WSManFlagUseKerberos; } + if (authentication.Equals(AuthenticationMechanism.Digest)) { sessionFlags = sessionFlags | (int)WSManSessionFlags.WSManFlagUseDigest | (int)WSManSessionFlags.WSManFlagCredUserNamePassword; } + if (authentication.Equals(AuthenticationMechanism.Credssp)) { sessionFlags = sessionFlags | (int)WSManSessionFlags.WSManFlagUseCredSsp | (int)WSManSessionFlags.WSManFlagCredUserNamePassword; } + if (authentication.Equals(AuthenticationMechanism.ClientCertificate)) { - sessionFlags = sessionFlags | (int)WSManSessionFlags.WSManFlagUseClientCertificate; + sessionFlags |= (int)WSManSessionFlags.WSManFlagUseClientCertificate; } - } IWSManConnectionOptionsEx2 connObject = (IWSManConnectionOptionsEx2)wsmanObject.CreateConnectionOptions(); if (credential != null) { - //connObject = (IWSManConnectionOptionsEx2)wsmanObject.CreateConnectionOptions(); + // connObject = (IWSManConnectionOptionsEx2)wsmanObject.CreateConnectionOptions(); System.Net.NetworkCredential nwCredential = new System.Net.NetworkCredential(); if (credential.UserName != null) { nwCredential = credential.GetNetworkCredential(); - if (String.IsNullOrEmpty(nwCredential.Domain)) + if (string.IsNullOrEmpty(nwCredential.Domain)) { - if ( authentication.Equals(AuthenticationMechanism.Digest) || authentication.Equals(AuthenticationMechanism.Basic) ) + if (authentication.Equals(AuthenticationMechanism.Digest) || authentication.Equals(AuthenticationMechanism.Basic)) { connObject.UserName = nwCredential.UserName; } @@ -712,10 +698,11 @@ internal IWSManSession CreateSessionObject(IWSManEx wsmanObject, AuthenticationM { connObject.UserName = nwCredential.Domain + "\\" + nwCredential.UserName; } + connObject.Password = nwCredential.Password; if (!authentication.Equals(AuthenticationMechanism.Credssp) || !authentication.Equals(AuthenticationMechanism.Digest) || authentication.Equals(AuthenticationMechanism.Basic)) { - sessionFlags = sessionFlags | (int)WSManSessionFlags.WSManFlagCredUserNamePassword; + sessionFlags |= (int)WSManSessionFlags.WSManFlagCredUserNamePassword; } } } @@ -723,12 +710,11 @@ internal IWSManSession CreateSessionObject(IWSManEx wsmanObject, AuthenticationM if (certificateThumbprint != null) { connObject.CertificateThumbprint = certificateThumbprint; - sessionFlags = sessionFlags | (int)WSManSessionFlags.WSManFlagUseClientCertificate; + sessionFlags |= (int)WSManSessionFlags.WSManFlagUseClientCertificate; } if (sessionoption != null) { - if (sessionoption.ProxyAuthentication != 0) { int ProxyAccessflags = 0; @@ -762,6 +748,7 @@ internal IWSManSession CreateSessionObject(IWSManEx wsmanObject, AuthenticationM { ProxyAuthenticationFlags = connObject.ProxyAuthenticationUseDigest(); } + if (sessionoption.ProxyCredential != null) { try @@ -777,48 +764,52 @@ internal IWSManSession CreateSessionObject(IWSManEx wsmanObject, AuthenticationM { connObject.SetProxy((int)sessionoption.ProxyAccessType, (int)sessionoption.ProxyAuthentication, null, null); } - - } + if (sessionoption.SkipCACheck) { - sessionFlags = sessionFlags | (int)WSManSessionFlags.WSManFlagSkipCACheck; + sessionFlags |= (int)WSManSessionFlags.WSManFlagSkipCACheck; } + if (sessionoption.SkipCNCheck) { - sessionFlags = sessionFlags | (int)WSManSessionFlags.WSManFlagSkipCNCheck; + sessionFlags |= (int)WSManSessionFlags.WSManFlagSkipCNCheck; } + if (sessionoption.SPNPort > 0) { - sessionFlags = sessionFlags | (int)WSManSessionFlags.WSManFlagEnableSpnServerPort; + sessionFlags |= (int)WSManSessionFlags.WSManFlagEnableSpnServerPort; } + if (sessionoption.UseUtf16) { - sessionFlags = sessionFlags | (int)WSManSessionFlags.WSManFlagUtf16; + sessionFlags |= (int)WSManSessionFlags.WSManFlagUtf16; } else { - //If UseUtf16 is false, then default Encoding is Utf8 - sessionFlags = sessionFlags | (int)WSManSessionFlags.WSManFlagUtf8; + // If UseUtf16 is false, then default Encoding is Utf8 + sessionFlags |= (int)WSManSessionFlags.WSManFlagUtf8; } + if (!sessionoption.UseEncryption) { - sessionFlags = sessionFlags | (int)WSManSessionFlags.WSManFlagNoEncryption; + sessionFlags |= (int)WSManSessionFlags.WSManFlagNoEncryption; } + if (sessionoption.SkipRevocationCheck) { - sessionFlags = sessionFlags | (int)WSManSessionFlags.WSManFlagSkipRevocationCheck; + sessionFlags |= (int)WSManSessionFlags.WSManFlagSkipRevocationCheck; } } else { - //If SessionOption is null then, default Encoding is Utf8 - sessionFlags = sessionFlags | (int)WSManSessionFlags.WSManFlagUtf8; + // If SessionOption is null then, default Encoding is Utf8 + sessionFlags |= (int)WSManSessionFlags.WSManFlagUtf8; } if (usessl) { - sessionFlags = sessionFlags | (int)WSManSessionFlags.WSManFlagUseSsl; + sessionFlags |= (int)WSManSessionFlags.WSManFlagUseSsl; } IWSManSession m_SessionObj = null; @@ -837,24 +828,23 @@ internal IWSManSession CreateSessionObject(IWSManEx wsmanObject, AuthenticationM { AssertError(ex.Message, false, null); } + return m_SessionObj; } internal void CleanUp() { - - if (_sr != null) { _sr.Dispose(); _sr = null; } + if (_fs != null) { _fs.Dispose(); _fs = null; } - } internal string GetFilterString(Hashtable seletorset) @@ -865,11 +855,12 @@ internal string GetFilterString(Hashtable seletorset) if (entry.Key != null && entry.Value != null) { filter.Append(entry.Key.ToString()); - filter.Append("="); + filter.Append('='); filter.Append(entry.Value.ToString()); - filter.Append("+"); + filter.Append('+'); } } + filter.Remove(filter.ToString().Length - 1, 1); return filter.ToString(); } @@ -910,12 +901,12 @@ internal string GetURIWithFilter(string uri, string filter, Hashtable selectorse { StringBuilder sburi = new StringBuilder(); sburi.Append(uri); - sburi.Append("?"); + sburi.Append('?'); if (operation.Equals("remove", StringComparison.OrdinalIgnoreCase)) { sburi.Append(GetFilterString(selectorset)); - if (sburi.ToString().EndsWith("?", StringComparison.OrdinalIgnoreCase)) + if (sburi.ToString().EndsWith('?')) { sburi.Remove(sburi.Length - 1, 1); } @@ -925,7 +916,7 @@ internal string GetURIWithFilter(string uri, string filter, Hashtable selectorse } /// - /// This method is used by Connect-WsMan Cmdlet and New-Item of WsMan Provider to create connection to WsMan + /// This method is used by Connect-WsMan Cmdlet and New-Item of WsMan Provider to create connection to WsMan. /// /// /// @@ -945,18 +936,16 @@ internal void CreateWsManConnection(string ParameterSetName, Uri connectionuri, string connectionStr = CreateConnectionString(connectionuri, port, computername, applicationname); if (connectionuri != null) { - //in the format http(s)://server[:port/applicationname] - string[] constrsplit = connectionStr.Split(new string[] { ":" + port + "/" + applicationname }, StringSplitOptions.None); - string[] constrsplit1 = constrsplit[0].Split(new string[] { "//" }, StringSplitOptions.None); + // in the format http(s)://server[:port/applicationname] + string[] constrsplit = connectionStr.Split(":" + port + "/" + applicationname, StringSplitOptions.None); + string[] constrsplit1 = constrsplit[0].Split("//", StringSplitOptions.None); computername = constrsplit1[1].Trim(); } + IWSManSession m_session = CreateSessionObject(m_wsmanObject, authentication, sessionoption, credential, connectionStr, certificateThumbprint, usessl); m_session.Identify(0); - string key = computername; - if (key == null) - { - key = "localhost"; - } + string key = computername ?? "localhost"; + AddtoDictionary(key, m_session); } catch (IndexOutOfRangeException) @@ -969,11 +958,10 @@ internal void CreateWsManConnection(string ParameterSetName, Uri connectionuri, } finally { - if (!String.IsNullOrEmpty(m_wsmanObject.Error)) + if (!string.IsNullOrEmpty(m_wsmanObject.Error)) { AssertError(m_wsmanObject.Error, true, computername); } - } } @@ -1009,17 +997,14 @@ internal bool ValidateCredSSPRegistry(bool AllowFreshCredentialsValueShouldBePre { RegistryKey rGPOLocalMachineKey = Registry.LocalMachine.OpenSubKey( Registry_Path_Credentials_Delegation + @"\CredentialsDelegation", -#if !CORECLR RegistryKeyPermissionCheck.ReadWriteSubTree, -#endif System.Security.AccessControl.RegistryRights.FullControl); if (rGPOLocalMachineKey != null) { - rGPOLocalMachineKey = rGPOLocalMachineKey.OpenSubKey(Key_Allow_Fresh_Credentials, -#if !CORECLR + rGPOLocalMachineKey = rGPOLocalMachineKey.OpenSubKey( + Key_Allow_Fresh_Credentials, RegistryKeyPermissionCheck.ReadWriteSubTree, -#endif System.Security.AccessControl.RegistryRights.FullControl); if (rGPOLocalMachineKey == null) { @@ -1027,7 +1012,7 @@ internal bool ValidateCredSSPRegistry(bool AllowFreshCredentialsValueShouldBePre } string[] valuenames = rGPOLocalMachineKey.GetValueNames(); - if (valuenames.Length <= 0) + if (valuenames.Length == 0) { return !AllowFreshCredentialsValueShouldBePresent; } @@ -1077,13 +1062,10 @@ internal static void LoadResourceData() { try { - string filepath = System.Environment.ExpandEnvironmentVariables("%Windir%") + "\\System32\\Winrm\\" + -#if CORECLR - "0409" /* TODO: don't assume it is always English on CSS? */ -#else - String.Concat("0", String.Format(CultureInfo.CurrentCulture, "{0:x2}", checked((uint)CultureInfo.CurrentUICulture.LCID))) -#endif - + "\\" + "winrm.ini"; + string winDir = System.Environment.ExpandEnvironmentVariables("%Windir%"); + uint lcid = checked((uint)CultureInfo.CurrentUICulture.LCID); + string filepath = string.Create(CultureInfo.CurrentCulture, $@"{winDir}\System32\Winrm\0{lcid:x2}\winrm.ini"); + if (File.Exists(filepath)) { FileStream _fs = new FileStream(filepath, FileMode.Open, FileAccess.Read); @@ -1091,54 +1073,49 @@ internal static void LoadResourceData() while (!_sr.EndOfStream) { string Line = _sr.ReadLine(); - if (Line.Contains("=")) + if (Line.Contains('=')) { - string[] arr = Line.Split(new char[] { '=' }, 2); + string[] arr = Line.Split('=', count: 2); if (!ResourceValueCache.ContainsKey(arr[0].Trim())) { - string value = arr[1].TrimStart(new char[] { '"' }).TrimEnd(new char[] { '"' }); + string value = arr[1].Trim('"'); ResourceValueCache.Add(arr[0].Trim(), value.Trim()); } } } } } - - catch (IOException e) + catch (IOException) { - - throw (e); + throw; } - - } /// /// Get the resource value from WinRm.ini - /// from %windir%\system32\winrm\[Hexadecimal Language Folder]\winrm.ini + /// from %windir%\system32\winrm\[Hexadecimal Language Folder]\winrm.ini. /// /// /// internal static string GetResourceString(string Key) { - //Checks whether resource values already loaded and loads. - if (ResourceValueCache.Count <= 0) + // Checks whether resource values already loaded and loads. + if (ResourceValueCache.Count == 0) { LoadResourceData(); } - string value = ""; + + string value = string.Empty; if (ResourceValueCache.ContainsKey(Key.Trim())) { ResourceValueCache.TryGetValue(Key.Trim(), out value); } + return value.Trim(); } /// - /// /// - private static Dictionary ResourceValueCache = new Dictionary(); - - + private static readonly Dictionary ResourceValueCache = new Dictionary(); } } diff --git a/src/Microsoft.WSMan.Management/WsManSnapin.cs b/src/Microsoft.WSMan.Management/WsManSnapin.cs index b9741220403..4093caf12db 100644 --- a/src/Microsoft.WSMan.Management/WsManSnapin.cs +++ b/src/Microsoft.WSMan.Management/WsManSnapin.cs @@ -1,14 +1,14 @@ -/********************************************************************++ -Copyright (c) Microsoft Corporation. All rights reserved. ---********************************************************************/ +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. + using System; +using System.Collections.Generic; +using System.ComponentModel; using System.Diagnostics; -using System.Text; using System.Management; using System.Management.Automation; -using System.ComponentModel; -using System.Collections.Generic; using System.Runtime.InteropServices; +using System.Text; namespace Microsoft.WSMan.Management { diff --git a/src/Microsoft.WSMan.Management/resources/WsManResources.resx b/src/Microsoft.WSMan.Management/resources/WsManResources.resx index 31837076b26..a25f6c4801f 100644 --- a/src/Microsoft.WSMan.Management/resources/WsManResources.resx +++ b/src/Microsoft.WSMan.Management/resources/WsManResources.resx @@ -191,7 +191,7 @@ Do you want to enable CredSSP authentication? This command cannot be used because the parameter matches a non-text property on the ResourceURI.Check the input parameters and run your command. - + A {0} cannot be specified when {1} is specified. @@ -308,7 +308,7 @@ Do you want to continue? This command cannot be used from the current path. Move to root path of the provider using cd\ and run your command again. - This Windows PowerShell snap-in contains cmdlets (such as Get-WSManInstance and Set-WSManInstance) that are used by the Windows PowerShell host to manage WSMan operations. + This PowerShell snap-in contains cmdlets (such as Get-WSManInstance and Set-WSManInstance) that are used by the PowerShell host to manage WSMan operations. This command cannot be used to remove the computer 'localhost' because it will always be connected. Give some other connected computer and run your command. diff --git a/src/Microsoft.WSMan.Management/resources/WsManResources.txt b/src/Microsoft.WSMan.Management/resources/WsManResources.txt index 7599a12ece2..10702e5ccea 100644 --- a/src/Microsoft.WSMan.Management/resources/WsManResources.txt +++ b/src/Microsoft.WSMan.Management/resources/WsManResources.txt @@ -1,9 +1,9 @@ -# Copyright (c) 2008 Microsoft Corporation +# Copyright (c) Microsoft Corporation. # {0} = Delegate Vendor=Microsoft -Description=This Windows PowerShell snap-in contains cmdlets (such as Get-WSManInstance and Set-WSManInstance) that are used by the Windows PowerShell host to manage WSMan operations. +Description=This PowerShell snap-in contains cmdlets (such as Get-WSManInstance and Set-WSManInstance) that are used by the PowerShell host to manage WSMan operations. InvalidComputerName=The WinRM client cannot complete the operation.Check if the computer name is valid. InvalidFileName=This command cannot be used because file does not exist.Check the file existence and run your command. InvalidPath=This command cannot be used because root path does not exist.Check the root path and run your command. @@ -11,8 +11,8 @@ NoResourceMatch=This command cannot be used because the parameter does not match MultipleResourceMatch=This command cannot be used because the parameter matches multiple properties on the ResourceURI.Check the input parameters and run your command. NoAttributeMatch=This command cannot be used because the parameter matches a non-text property on the ResourceURI.Check the input parameters and run your command. WinrmNotConfigured=This command cannot be used because the configuration is corrupted. Run WinRM invoke Restore WinRM/config to restore the default configuration -DelegateFreshCred=The machine is configured to allow delegating fresh credentials to the following target(s): -NoDelegateFreshCred=The machine is not configured to allow delegating fresh credentials. +DelegateFreshCred=The machine is configured to allow delegating fresh credentials to the following target(s): +NoDelegateFreshCred=The machine is not configured to allow delegating fresh credentials. LocalHost=This command cannot be used to remove the computer 'localhost' because it will always be connected. Give some other connected computer and run your command. ConnectFailure=This command cannot be used from the current path. Move to root path of the provider using cd\\ and run your command again. DisconnectFailure=This command cannot be used from the current path. Move to root path of the provider using cd\\ and run your command again. @@ -56,7 +56,7 @@ CredSSPServiceConfigured=This computer is configured to receive credentials from CredSSPServiceNotConfigured=This computer is not configured to receive credentials from a remote client computer. QuickConfigContinueCaption=WinRM Quick Configuration QuickConfigContinueQuery=Running the Set-WSManQuickConfig command has significant security implications, as it enables remote management through the WinRM service on this computer.\nThis command:\n 1. Checks whether the WinRM service is running. If the WinRM service is not running, the service is started.\n 2. Sets the WinRM service startup type to automatic.\n 3. Creates a listener to accept requests on any IP address. By default, the transport is HTTP.\n 4. Enables a firewall exception for WS-Management traffic.\n 5. Enables Kerberos and Negotiate service authentication.\nDo you want to enable remote management through the WinRM service on this computer? -AmbiguosAuthentication=A {0} cannot be specified when {1} is specified. +AmbiguousAuthentication=A {0} cannot be specified when {1} is specified. CmdletNotAvailable=This PowerShell cmdlet is not available on for Windows XP and Windows Server 2003. InvalidValueType=This command cannot be used because the parameter value type is invalid. {0} configuration expects a value of Type {1}. Verify that the value is correct and try again. ClearItemOnRunAsPassword=The RunAsPassword value cannot be removed. Remove the values for RunAsUser and RunAsPassword in PowerShell by calling the Clear-Item cmdlet with the value for -Path attribute equal to the value of RunAsUser. @@ -65,4 +65,4 @@ SetItemWhatIfAndConfirmText= "Set-Item" on the WinRM configuration setting "{0}" SetItemWarnigForPPQ=The updated configuration is effective only if it is less than or equal to the value of global quota {0}. Verify the value for the global quota using the PowerShell cmdlet "Get-Item {0}". SetItemWarningForGlobalQuota=The updated configuration might affect the operation of the plugins having a per plugin quota value greater than {0}. Verify the configuration of all the registered plugins and change the per plugin quota values for the affected plugins. SetItemServiceRestartWarning= The configuration changes you made will only be effective after the WinRM service is restarted. To restart the WinRM service, run the following command: 'Restart-Service winrm' -SetItemServiceRestartWarningRemote= The configuration changes you made will only be effective after the WinRM service is restarted on {0}. \ No newline at end of file +SetItemServiceRestartWarningRemote= The configuration changes you made will only be effective after the WinRM service is restarted on {0}. diff --git a/src/Microsoft.WSMan.Runtime/Microsoft.WSMan.Runtime.csproj b/src/Microsoft.WSMan.Runtime/Microsoft.WSMan.Runtime.csproj index 8d9bac1d34d..2a9439c0274 100644 --- a/src/Microsoft.WSMan.Runtime/Microsoft.WSMan.Runtime.csproj +++ b/src/Microsoft.WSMan.Runtime/Microsoft.WSMan.Runtime.csproj @@ -1,26 +1,8 @@ - - - - + + - PowerShell Core's Microsoft.WSMan.Runtime project + PowerShell's Microsoft.WSMan.Runtime project Microsoft.WSMan.Runtime - - $(DefineConstants);CORECLR - - - - portable - - - - $(DefineConstants);UNIX - - - - full - - diff --git a/src/Microsoft.WSMan.Runtime/WSManSessionOption.cs b/src/Microsoft.WSMan.Runtime/WSManSessionOption.cs index 9c187e9bc10..13dc8bbea2c 100644 --- a/src/Microsoft.WSMan.Runtime/WSManSessionOption.cs +++ b/src/Microsoft.WSMan.Runtime/WSManSessionOption.cs @@ -1,212 +1,111 @@ -// -// Copyright (C) Microsoft. All rights reserved. -// -using System; -using System.IO; -using System.Xml; -using System.Net; -using System.Resources; -using System.Reflection; -using System.ComponentModel; - -using System.Collections; -using System.Collections.Generic; +// Copyright (c) Microsoft Corporation. +// Licensed under the MIT License. -using System.Runtime.InteropServices; +using System; using System.Diagnostics.CodeAnalysis; -using System.Runtime.CompilerServices; - - - - +using System.Net; [assembly: CLSCompliant(true)] + namespace Microsoft.WSMan.Management { - /// - /// Session option class + /// Session option class. /// - public sealed class SessionOption { - /// - /// property - /// - public bool SkipCACheck - { - get { return _SkipCACheck; } - set - { - _SkipCACheck = value; - } - } - private bool _SkipCACheck; + /// Property. + /// + public bool SkipCACheck { get; set; } /// - /// property - /// - public bool SkipCNCheck - { - get { return _SkipCNCheck; } - set - { - _SkipCNCheck = value; - } - } - private bool _SkipCNCheck; + /// Property. + /// + public bool SkipCNCheck { get; set; } /// - /// property + /// Property. /// - /// - public bool SkipRevocationCheck - { - get { return _SkipRevocationCheck; } - set - { - _SkipRevocationCheck = value; - } - - } - private bool _SkipRevocationCheck; + public bool SkipRevocationCheck { get; set; } /// - /// property - /// - public bool UseEncryption - { - get { return _useencryption; } - set - { - _useencryption = value; - } - } - private bool _useencryption = true; + /// Property. + /// + public bool UseEncryption { get; set; } = true; /// - /// property - /// - public bool UseUtf16 - { - get { return _UTF16; } - set - { - _UTF16 = value; - } - } - private bool _UTF16; + /// Property. + /// + public bool UseUtf16 { get; set; } /// - /// property - /// - public ProxyAuthentication ProxyAuthentication - { - get { return _ProxyAuthentication; } - set - { - _ProxyAuthentication = value; - } - } - private ProxyAuthentication _ProxyAuthentication; + /// Property. + /// + public ProxyAuthentication ProxyAuthentication { get; set; } /// - /// property + /// Property. /// [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "SPN")] - public int SPNPort - { - get { return _SPNPort; } - set - { - _SPNPort = value; - } - } - - private int _SPNPort; + public int SPNPort { get; set; } /// - /// property - /// - public int OperationTimeout - { - get { return _OperationTimeout; } - set - { - _OperationTimeout = value; - } - } - private int _OperationTimeout; + /// Property. + /// + public int OperationTimeout { get; set; } /// - /// property - /// - public NetworkCredential ProxyCredential - { - get { return _ProxyCredential; } - set - { - _ProxyCredential = value; - } - } - private NetworkCredential _ProxyCredential; + /// Property. + /// + public NetworkCredential ProxyCredential { get; set; } /// - /// property + /// Property. /// - public ProxyAccessType ProxyAccessType - { - get { return _proxyaccesstype; } - set - { - _proxyaccesstype = value; - } - } - - private ProxyAccessType _proxyaccesstype; + public ProxyAccessType ProxyAccessType { get; set; } } /// - /// property + /// Property. /// public enum ProxyAccessType { /// - /// property + /// Property. /// ProxyIEConfig = 0, /// - /// property + /// Property. /// ProxyWinHttpConfig = 1, /// - /// property + /// Property. /// ProxyAutoDetect = 2, /// - /// property + /// Property. /// ProxyNoProxyServer = 3 } /// - /// property + /// Property. /// [SuppressMessage("Microsoft.Design", "CA1027:MarkEnumsWithFlags")] [SuppressMessage("Microsoft.Design", "CA1008:EnumsShouldHaveZeroValue")] public enum ProxyAuthentication { /// - /// property + /// Property. /// Negotiate = 1, /// - /// property + /// Property. /// Basic = 2, /// - /// property + /// Property. /// Digest = 4 } diff --git a/src/Modules/PSGalleryModules.csproj b/src/Modules/PSGalleryModules.csproj new file mode 100644 index 00000000000..b6e5506e0b0 --- /dev/null +++ b/src/Modules/PSGalleryModules.csproj @@ -0,0 +1,22 @@ + + + + PowerShell + Microsoft Corporation + (c) Microsoft Corporation. + + net10.0 + + true + + + + + + + + + + + + diff --git a/src/Modules/README.md b/src/Modules/README.md index ccc66161d0e..5911951a5ad 100644 --- a/src/Modules/README.md +++ b/src/Modules/README.md @@ -11,11 +11,8 @@ Content files includes: These files are copied as-is by `dotnet` -- Shared (shared between all variations) -- Windows+Unix-Core -- Windows-Core -- Windows-Core+Full -- Windows-Full +- Shared (shared between Windows and Unix) +- Windows - Unix Notes diff --git a/src/Modules/Shared/Microsoft.PowerShell.Host/Microsoft.PowerShell.Host.psd1 b/src/Modules/Shared/Microsoft.PowerShell.Host/Microsoft.PowerShell.Host.psd1 index 3795749381b..3c2581795f7 100644 --- a/src/Modules/Shared/Microsoft.PowerShell.Host/Microsoft.PowerShell.Host.psd1 +++ b/src/Modules/Shared/Microsoft.PowerShell.Host/Microsoft.PowerShell.Host.psd1 @@ -1,14 +1,14 @@ -@{ -GUID="56D66100-99A0-4FFC-A12D-EEE9A6718AEF" -Author="Microsoft Corporation" -CompanyName="Microsoft Corporation" -Copyright=" Microsoft Corporation. All rights reserved." -ModuleVersion="3.0.0.0" -PowerShellVersion="3.0" -CLRVersion="4.0" -AliasesToExport = @() -FunctionsToExport = @() -CmdletsToExport="Start-Transcript", "Stop-Transcript" -NestedModules="Microsoft.PowerShell.ConsoleHost.dll" -HelpInfoURI = 'https://go.microsoft.com/fwlink/?linkid=390784' -} +@{ +GUID="56D66100-99A0-4FFC-A12D-EEE9A6718AEF" +Author="PowerShell" +CompanyName="Microsoft Corporation" +Copyright="Copyright (c) Microsoft Corporation." +ModuleVersion="7.0.0.0" +CompatiblePSEditions = @("Core") +PowerShellVersion="3.0" +FunctionsToExport = @() +CmdletsToExport="Start-Transcript", "Stop-Transcript" +AliasesToExport = @() +NestedModules="Microsoft.PowerShell.ConsoleHost.dll" +HelpInfoURI = 'https://aka.ms/powershell75-help' +} diff --git a/src/Modules/Shared/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psm1 b/src/Modules/Shared/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psm1 deleted file mode 100644 index 322c671fcb0..00000000000 --- a/src/Modules/Shared/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psm1 +++ /dev/null @@ -1,174 +0,0 @@ - -## Converts a SDDL string into an object-based representation of a security -## descriptor -function ConvertFrom-SddlString -{ - [CmdletBinding(HelpUri = "https://go.microsoft.com/fwlink/?LinkId=623636")] - param( - ## The string representing the security descriptor in SDDL syntax - [Parameter(Mandatory = $true, Position = 0, ValueFromPipeline = $true)] - [String] $Sddl, - - ## The type of rights that this SDDL string represents, if any. - [Parameter()] - [ValidateSet( - "FileSystemRights", "RegistryRights", "ActiveDirectoryRights", - "MutexRights", "SemaphoreRights", "CryptoKeyRights", - "EventWaitHandleRights")] - $Type - ) - - Begin - { - # On CoreCLR CryptoKeyRights and ActiveDirectoryRights are not supported. - if ($PSEdition -eq "Core" -and ($Type -eq "CryptoKeyRights" -or $Type -eq "ActiveDirectoryRights")) - { - $errorId = "TypeNotSupported" - $errorCategory = [System.Management.Automation.ErrorCategory]::InvalidArgument - $errorMessage = [Microsoft.PowerShell.Commands.UtilityResources]::TypeNotSupported -f $Type - $exception = [System.ArgumentException]::New($errorMessage) - $errorRecord = [System.Management.Automation.ErrorRecord]::New($exception, $errorId, $errorCategory, $null) - $PSCmdlet.ThrowTerminatingError($errorRecord) - } - - ## Translates a SID into a NT Account - function ConvertTo-NtAccount - { - param($Sid) - - if($Sid) - { - $securityIdentifier = [System.Security.Principal.SecurityIdentifier] $Sid - - try - { - $ntAccount = $securityIdentifier.Translate([System.Security.Principal.NTAccount]).ToString() - } - catch{} - - $ntAccount - } - } - - ## Gets the access rights that apply to an access mask, preferring right types - ## of 'Type' if specified. - function Get-AccessRights - { - param($AccessMask, $Type) - - if ($PSEdition -eq "Core") - { - ## All the types of access rights understood by .NET Core - $rightTypes = [Ordered] @{ - "FileSystemRights" = [System.Security.AccessControl.FileSystemRights] - "RegistryRights" = [System.Security.AccessControl.RegistryRights] - "MutexRights" = [System.Security.AccessControl.MutexRights] - "SemaphoreRights" = [System.Security.AccessControl.SemaphoreRights] - "EventWaitHandleRights" = [System.Security.AccessControl.EventWaitHandleRights] - } - } - else - { - ## All the types of access rights understood by .NET - $rightTypes = [Ordered] @{ - "FileSystemRights" = [System.Security.AccessControl.FileSystemRights] - "RegistryRights" = [System.Security.AccessControl.RegistryRights] - "ActiveDirectoryRights" = [System.DirectoryServices.ActiveDirectoryRights] - "MutexRights" = [System.Security.AccessControl.MutexRights] - "SemaphoreRights" = [System.Security.AccessControl.SemaphoreRights] - "CryptoKeyRights" = [System.Security.AccessControl.CryptoKeyRights] - "EventWaitHandleRights" = [System.Security.AccessControl.EventWaitHandleRights] - } - } - $typesToExamine = $rightTypes.Values - - ## If they know the access mask represents a certain type, prefer its names - ## (i.e.: CreateLink for the registry over CreateDirectories for the filesystem) - if($Type) - { - $typesToExamine = @($rightTypes[$Type]) + $typesToExamine - } - - - ## Stores the access types we've found that apply - $foundAccess = @() - - ## Store the access types we've already seen, so that we don't report access - ## flags that are essentially duplicate. Many of the access values in the different - ## enumerations have the same value but with different names. - $foundValues = @{} - - ## Go through the entries in the different right types, and see if they apply to the - ## provided access mask. If they do, then add that to the result. - foreach($rightType in $typesToExamine) - { - foreach($accessFlag in [Enum]::GetNames($rightType)) - { - $longKeyValue = [long] $rightType::$accessFlag - if(-not $foundValues.ContainsKey($longKeyValue)) - { - $foundValues[$longKeyValue] = $true - if(($AccessMask -band $longKeyValue) -eq ($longKeyValue)) - { - $foundAccess += $accessFlag - } - } - } - } - - $foundAccess | Sort-Object - } - - ## Converts an ACE into a string representation - function ConvertTo-AceString - { - param( - [Parameter(ValueFromPipeline)] - $Ace, - $Type - ) - - process - { - foreach($aceEntry in $Ace) - { - $AceString = (ConvertTo-NtAccount $aceEntry.SecurityIdentifier) + ": " + $aceEntry.AceQualifier - if($aceEntry.AceFlags -ne "None") - { - $AceString += " " + $aceEntry.AceFlags - } - - if($aceEntry.AccessMask) - { - $foundAccess = Get-AccessRights $aceEntry.AccessMask $Type - - if($foundAccess) - { - $AceString += " ({0})" -f ($foundAccess -join ", ") - } - } - - $AceString - } - } - } - } - - Process - { - $rawSecurityDescriptor = [Security.AccessControl.CommonSecurityDescriptor]::new($false,$false,$Sddl) - - $owner = ConvertTo-NtAccount $rawSecurityDescriptor.Owner - $group = ConvertTo-NtAccount $rawSecurityDescriptor.Group - $discretionaryAcl = ConvertTo-AceString $rawSecurityDescriptor.DiscretionaryAcl $Type - $systemAcl = ConvertTo-AceString $rawSecurityDescriptor.SystemAcl $Type - - [PSCustomObject] @{ - Owner = $owner - Group = $group - DiscretionaryAcl = @($discretionaryAcl) - SystemAcl = @($systemAcl) - RawDescriptor = $rawSecurityDescriptor - } - } -} diff --git a/src/Modules/Shared/PSReadLine/PSReadLine.psd1 b/src/Modules/Shared/PSReadLine/PSReadLine.psd1 deleted file mode 100644 index 6af2eea6f73..00000000000 --- a/src/Modules/Shared/PSReadLine/PSReadLine.psd1 +++ /dev/null @@ -1,17 +0,0 @@ -@{ -RootModule = 'PSReadLine.psm1' -NestedModules = @("Microsoft.PowerShell.PSReadLine.dll") -ModuleVersion = '1.2' -GUID = '5714753b-2afd-4492-a5fd-01d9e2cff8b5' -Author = 'Microsoft Corporation' -CompanyName = 'Microsoft Corporation' -Copyright = '(c) Microsoft Corporation. All rights reserved.' -Description = 'Great command line editing in the PowerShell console host' -PowerShellVersion = '3.0' -DotNetFrameworkVersion = '4.0' -CLRVersion = '4.0' -FunctionsToExport = 'PSConsoleHostReadline' -CmdletsToExport = 'Get-PSReadlineKeyHandler','Set-PSReadlineKeyHandler','Remove-PSReadlineKeyHandler', - 'Get-PSReadlineOption','Set-PSReadlineOption' -HelpInfoURI = 'https://go.microsoft.com/fwlink/?LinkId=528806' -} diff --git a/src/Modules/Shared/PSReadLine/PSReadLine.psm1 b/src/Modules/Shared/PSReadLine/PSReadLine.psm1 deleted file mode 100644 index 4f2bf37fe89..00000000000 --- a/src/Modules/Shared/PSReadLine/PSReadLine.psm1 +++ /dev/null @@ -1,5 +0,0 @@ -function PSConsoleHostReadline -{ - Microsoft.PowerShell.Core\Set-StrictMode -Off - [Microsoft.PowerShell.PSConsoleReadLine]::ReadLine($host.Runspace, $ExecutionContext) -} diff --git a/src/Modules/Shared/Pester b/src/Modules/Shared/Pester deleted file mode 160000 index aa243108e7d..00000000000 --- a/src/Modules/Shared/Pester +++ /dev/null @@ -1 +0,0 @@ -Subproject commit aa243108e7da50a8cf82513b6dd649b653c70b0e diff --git a/src/Modules/Unix/Microsoft.PowerShell.Management/Microsoft.PowerShell.Management.psd1 b/src/Modules/Unix/Microsoft.PowerShell.Management/Microsoft.PowerShell.Management.psd1 index 5bf08654f8e..21563c1da7c 100644 --- a/src/Modules/Unix/Microsoft.PowerShell.Management/Microsoft.PowerShell.Management.psd1 +++ b/src/Modules/Unix/Microsoft.PowerShell.Management/Microsoft.PowerShell.Management.psd1 @@ -1,54 +1,60 @@ -@{ -GUID="EEFCB906-B326-4E99-9F54-8B4BB6EF3C6D" -Author="Microsoft Corporation" -CompanyName="Microsoft Corporation" -Copyright=" Microsoft Corporation. All rights reserved." -ModuleVersion="3.1.0.0" -PowerShellVersion="3.0" -NestedModules="Microsoft.PowerShell.Commands.Management.dll" -HelpInfoURI = 'https://go.microsoft.com/fwlink/?linkid=390785' -AliasesToExport = @("gtz") -FunctionsToExport = @() -CmdletsToExport=@("Add-Content", - "Clear-Content", - "Clear-ItemProperty", - "Join-Path", - "Convert-Path", - "Copy-ItemProperty", - "Get-ChildItem", - "Get-Content", - "Get-ItemProperty", - "Get-ItemPropertyValue", - "Move-ItemProperty", - "Get-Location", - "Set-Location", - "Push-Location", - "Pop-Location", - "New-PSDrive", - "Remove-PSDrive", - "Get-PSDrive", - "Get-Item", - "New-Item", - "Set-Item", - "Remove-Item", - "Move-Item", - "Rename-Item", - "Copy-Item", - "Clear-Item", - "Invoke-Item", - "Get-PSProvider", - "New-ItemProperty", - "Split-Path", - "Test-Path", - "Get-Process", - "Stop-Process", - "Wait-Process", - "Debug-Process", - "Start-Process", - "Remove-ItemProperty", - "Rename-ItemProperty", - "Resolve-Path", - "Set-Content", - "Set-ItemProperty", - "Get-TimeZone") -} +@{ +GUID="EEFCB906-B326-4E99-9F54-8B4BB6EF3C6D" +Author="PowerShell" +CompanyName="Microsoft Corporation" +Copyright="Copyright (c) Microsoft Corporation." +ModuleVersion="7.0.0.0" +CompatiblePSEditions = @("Core") +PowerShellVersion="3.0" +NestedModules="Microsoft.PowerShell.Commands.Management.dll" +HelpInfoURI = 'https://aka.ms/powershell75-help' +FunctionsToExport = @() +AliasesToExport = @("gcb", "gtz", "scb") +CmdletsToExport=@("Add-Content", + "Clear-Content", + "Clear-ItemProperty", + "Join-Path", + "Convert-Path", + "Copy-ItemProperty", + "Get-ChildItem", + "Get-Clipboard", + "Set-Clipboard", + "Get-Content", + "Get-ItemProperty", + "Get-ItemPropertyValue", + "Move-ItemProperty", + "Get-Location", + "Set-Location", + "Push-Location", + "Pop-Location", + "New-PSDrive", + "Remove-PSDrive", + "Get-PSDrive", + "Get-Item", + "New-Item", + "Set-Item", + "Remove-Item", + "Move-Item", + "Rename-Item", + "Copy-Item", + "Clear-Item", + "Invoke-Item", + "Get-PSProvider", + "New-ItemProperty", + "Split-Path", + "Test-Path", + "Get-Process", + "Stop-Process", + "Wait-Process", + "Debug-Process", + "Start-Process", + "Test-Connection", + "Remove-ItemProperty", + "Rename-ItemProperty", + "Resolve-Path", + "Set-Content", + "Set-ItemProperty", + "Get-TimeZone", + "Stop-Computer", + "Restart-Computer") +} diff --git a/src/Modules/Unix/Microsoft.PowerShell.Security/Microsoft.PowerShell.Security.psd1 b/src/Modules/Unix/Microsoft.PowerShell.Security/Microsoft.PowerShell.Security.psd1 index 45859c96afa..adab0df2849 100644 --- a/src/Modules/Unix/Microsoft.PowerShell.Security/Microsoft.PowerShell.Security.psd1 +++ b/src/Modules/Unix/Microsoft.PowerShell.Security/Microsoft.PowerShell.Security.psd1 @@ -1,13 +1,14 @@ @{ -GUID="A94C8C7E-9810-47C0-B8AF-65089C13A35A" -Author="Microsoft Corporation" -CompanyName="Microsoft Corporation" -Copyright=" Microsoft Corporation. All rights reserved." -ModuleVersion="3.0.0.0" -PowerShellVersion="3.0" -AliasesToExport = @() +GUID = "A94C8C7E-9810-47C0-B8AF-65089C13A35A" +Author = "PowerShell" +CompanyName = "Microsoft Corporation" +Copyright = "Copyright (c) Microsoft Corporation." +ModuleVersion = "7.0.0.0" +CompatiblePSEditions = @("Core") +PowerShellVersion = "3.0" FunctionsToExport = @() -CmdletsToExport="Get-Credential", "Get-ExecutionPolicy", "Set-ExecutionPolicy", "ConvertFrom-SecureString", "ConvertTo-SecureString", "Get-PfxCertificate" -NestedModules="Microsoft.PowerShell.Security.dll" -HelpInfoURI = 'https://go.microsoft.com/fwlink/?linkid=390786' +CmdletsToExport = "Get-Credential", "Get-ExecutionPolicy", "Set-ExecutionPolicy", "ConvertFrom-SecureString", "ConvertTo-SecureString", "Get-PfxCertificate" , "Protect-CmsMessage", "Unprotect-CmsMessage", "Get-CmsMessage" +AliasesToExport = @() +NestedModules = "Microsoft.PowerShell.Security.dll" +HelpInfoURI = 'https://aka.ms/powershell75-help' } diff --git a/src/Modules/Unix/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1 b/src/Modules/Unix/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1 index 1add1c7038d..df841837696 100644 --- a/src/Modules/Unix/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1 +++ b/src/Modules/Unix/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1 @@ -1,30 +1,35 @@ -@{ -GUID="1DA87E53-152B-403E-98DC-74D7B4D63D59" -Author="Microsoft Corporation" -CompanyName="Microsoft Corporation" -Copyright="© Microsoft Corporation. All rights reserved." -ModuleVersion="3.1.0.0" -PowerShellVersion="3.0" -CmdletsToExport= "Format-List", "Format-Custom", "Format-Table", "Format-Wide", - "Out-File", "Out-String", "Get-FormatData", "Export-FormatData", "ConvertFrom-Json", "ConvertTo-Json", - "Invoke-RestMethod", "Invoke-WebRequest", "Register-ObjectEvent", "Register-EngineEvent", - "Wait-Event", "Get-Event", "Remove-Event", "Get-EventSubscriber", "Unregister-Event", "New-Guid", - "New-Event", "Add-Member", "Add-Type", "Compare-Object", "ConvertTo-Html", "ConvertFrom-StringData", - "Export-Csv", "Import-Csv", "ConvertTo-Csv", "ConvertFrom-Csv", "Export-Alias", "Invoke-Expression", - "Get-Alias", "Get-Culture", "Get-Date", "Get-Host", "Get-Member", "Get-Random", "Get-UICulture", - "Get-Unique", "Export-PSSession", "Import-PSSession", "Import-Alias", "Import-LocalizedData", - "Select-String", "Measure-Object", "New-Alias", "New-TimeSpan", "Read-Host", "Set-Alias", "Set-Date", - "Start-Sleep", "Tee-Object", "Measure-Command", "Update-TypeData", "Update-FormatData", - "Remove-TypeData", "Get-TypeData", "Write-Host", "Write-Progress", "New-Object", "Select-Object", - "Group-Object", "Sort-Object", "Get-Variable", "New-Variable", "Set-Variable", "Remove-Variable", - "Clear-Variable", "Export-Clixml", "Import-Clixml", "Import-PowerShellDataFile", "ConvertTo-Xml", "Select-Xml", "Write-Debug", - "Write-Verbose", "Write-Warning", "Write-Error", "Write-Information", "Write-Output", "Set-PSBreakpoint", - "Get-PSBreakpoint", "Remove-PSBreakpoint", "Enable-PSBreakpoint", "Disable-PSBreakpoint", "Get-PSCallStack", - "Send-MailMessage", "Get-TraceSource", "Set-TraceSource", "Trace-Command", "Get-FileHash", - "Get-Runspace", "Debug-Runspace", "Enable-RunspaceDebug", "Disable-RunspaceDebug", - "Get-RunspaceDebug", "Wait-Debugger" , "Get-Uptime", "New-TemporaryFile", "Get-Verb", "Format-Hex" -FunctionsToExport= "Import-PowerShellDataFile" -AliasesToExport= "fhx" -NestedModules="Microsoft.PowerShell.Commands.Utility.dll","Microsoft.PowerShell.Utility.psm1" -HelpInfoURI = 'https://go.microsoft.com/fwlink/?linkid=390787' -} +@{ +GUID = "1DA87E53-152B-403E-98DC-74D7B4D63D59" +Author = "PowerShell" +CompanyName = "Microsoft Corporation" +Copyright = "Copyright (c) Microsoft Corporation." +ModuleVersion = "7.0.0.0" +CompatiblePSEditions = @("Core") +PowerShellVersion = "3.0" +CmdletsToExport = @( + 'Export-Alias', 'Get-Alias', 'Import-Alias', 'New-Alias', 'Remove-Alias', 'Set-Alias', 'Export-Clixml', + 'Import-Clixml', 'Measure-Command', 'Trace-Command', 'ConvertFrom-Csv', 'ConvertTo-Csv', 'Export-Csv', + 'Import-Csv', 'Get-Culture', 'Format-Custom', 'Get-Date', 'Set-Date', 'Write-Debug', 'Wait-Debugger', + 'Register-EngineEvent', 'Write-Error', 'Get-Event', 'New-Event', 'Remove-Event', 'Unregister-Event', + 'Wait-Event', 'Get-EventSubscriber', 'Invoke-Expression', 'Out-File', 'Get-FileHash', 'Export-FormatData', + 'Get-FormatData', 'Update-FormatData', 'New-Guid', 'Format-Hex', 'Get-Host', 'Read-Host', 'Write-Host', + 'ConvertTo-Html', 'Write-Information', 'ConvertFrom-Json', 'ConvertTo-Json', 'Test-Json', 'Format-List', + 'Import-LocalizedData', 'Send-MailMessage', 'ConvertFrom-Markdown', 'Show-Markdown', 'Get-MarkdownOption', + 'Set-MarkdownOption', 'Add-Member', 'Get-Member', 'Compare-Object', 'Group-Object', 'Measure-Object', + 'New-Object', 'Select-Object', 'Sort-Object', 'Tee-Object', 'Register-ObjectEvent', 'Write-Output', + 'Import-PowerShellDataFile', 'Write-Progress', 'Disable-PSBreakpoint', 'Enable-PSBreakpoint', + 'Get-PSBreakpoint', 'Remove-PSBreakpoint', 'Set-PSBreakpoint', 'Get-PSCallStack', 'Export-PSSession', + 'Import-PSSession', 'Get-Random', 'Get-SecureRandom', 'Invoke-RestMethod', 'Debug-Runspace', 'Get-Runspace', + 'Disable-RunspaceDebug', 'Enable-RunspaceDebug', 'Get-RunspaceDebug', 'Start-Sleep', 'Join-String', + 'Out-String', 'Select-String', 'ConvertFrom-StringData', 'Format-Table', 'New-TemporaryFile', 'New-TimeSpan', + 'Get-TraceSource', 'Set-TraceSource', 'Add-Type', 'Get-TypeData', 'Remove-TypeData', 'Update-TypeData', + 'Get-UICulture', 'Get-Unique', 'Get-Uptime', 'Clear-Variable', 'Get-Variable', 'New-Variable', + 'Remove-Variable', 'Set-Variable', 'Get-Verb', 'Write-Verbose', 'Write-Warning', 'Invoke-WebRequest', + 'Format-Wide', 'ConvertTo-Xml', 'Select-Xml', 'Get-Error', 'Update-List', 'Unblock-File', 'ConvertTo-CliXml', + 'ConvertFrom-CliXml' +) +FunctionsToExport = @() +AliasesToExport = @('fhx') +NestedModules = @("Microsoft.PowerShell.Commands.Utility.dll") +HelpInfoURI = 'https://aka.ms/powershell75-help' +} diff --git a/src/Modules/Windows-Core+Full/Microsoft.PowerShell.Security/Microsoft.PowerShell.Security.psd1 b/src/Modules/Windows-Core+Full/Microsoft.PowerShell.Security/Microsoft.PowerShell.Security.psd1 deleted file mode 100644 index 65515324f4f..00000000000 --- a/src/Modules/Windows-Core+Full/Microsoft.PowerShell.Security/Microsoft.PowerShell.Security.psd1 +++ /dev/null @@ -1,14 +0,0 @@ -@{ -GUID="A94C8C7E-9810-47C0-B8AF-65089C13A35A" -Author="Microsoft Corporation" -CompanyName="Microsoft Corporation" -Copyright=" Microsoft Corporation. All rights reserved." -ModuleVersion="3.0.0.0" -PowerShellVersion="3.0" -CLRVersion="4.0" -AliasesToExport = @() -FunctionsToExport = @() -CmdletsToExport="Get-Acl", "Set-Acl", "Get-PfxCertificate", "Get-Credential", "Get-ExecutionPolicy", "Set-ExecutionPolicy", "Get-AuthenticodeSignature", "Set-AuthenticodeSignature", "ConvertFrom-SecureString", "ConvertTo-SecureString", "Get-CmsMessage", "Unprotect-CmsMessage", "Protect-CmsMessage" , "New-FileCatalog" , "Test-FileCatalog" -NestedModules="Microsoft.PowerShell.Security.dll" -HelpInfoURI = 'https://go.microsoft.com/fwlink/?linkid=390786' -} diff --git a/src/Modules/Windows-Core+Full/Microsoft.WSMan.Management/Microsoft.WSMan.Management.psd1 b/src/Modules/Windows-Core+Full/Microsoft.WSMan.Management/Microsoft.WSMan.Management.psd1 deleted file mode 100644 index 03cf980b35b..00000000000 --- a/src/Modules/Windows-Core+Full/Microsoft.WSMan.Management/Microsoft.WSMan.Management.psd1 +++ /dev/null @@ -1,15 +0,0 @@ -@{ -GUID="766204A6-330E-4263-A7AB-46C87AFC366C" -Author="Microsoft Corporation" -CompanyName="Microsoft Corporation" -Copyright=" Microsoft Corporation. All rights reserved." -ModuleVersion="3.0.0.0" -PowerShellVersion="3.0" -CLRVersion="4.0" -AliasesToExport = @() -FunctionsToExport = @() -CmdletsToExport="Disable-WSManCredSSP", "Enable-WSManCredSSP", "Get-WSManCredSSP", "Set-WSManQuickConfig", "Test-WSMan", "Invoke-WSManAction", "Connect-WSMan", "Disconnect-WSMan", "Get-WSManInstance", "Set-WSManInstance", "Remove-WSManInstance", "New-WSManInstance", "New-WSManSessionOption" -NestedModules="Microsoft.WSMan.Management.dll" -FormatsToProcess="WSMan.format.ps1xml" -HelpInfoURI = 'https://go.microsoft.com/fwlink/?linkid=390788' -} diff --git a/src/Modules/Windows-Core+Full/PSDiagnostics/PSDiagnostics.psd1 b/src/Modules/Windows-Core+Full/PSDiagnostics/PSDiagnostics.psd1 deleted file mode 100644 index 4533f0991b2..00000000000 --- a/src/Modules/Windows-Core+Full/PSDiagnostics/PSDiagnostics.psd1 +++ /dev/null @@ -1,13 +0,0 @@ -@{ - GUID="c61d6278-02a3-4618-ae37-a524d40a7f44 " - Author="Microsoft Corporation" - CompanyName="Microsoft Corporation" - Copyright="© Microsoft Corporation. All rights reserved." - ModuleVersion="1.0.0.0" - PowerShellVersion="2.0" - CLRVersion="2.0.50727" - ModuleToProcess="PSDiagnostics.psm1" - AliasesToExport = @() - CmdletsToExport = @() - FunctionsToExport="Disable-PSTrace","Disable-PSWSManCombinedTrace","Disable-WSManTrace","Enable-PSTrace","Enable-PSWSManCombinedTrace","Enable-WSManTrace","Get-LogProperties","Set-LogProperties","Start-Trace","Stop-Trace" -} diff --git a/src/Modules/Windows-Core+Full/PSDiagnostics/PSDiagnostics.psm1 b/src/Modules/Windows-Core+Full/PSDiagnostics/PSDiagnostics.psm1 deleted file mode 100644 index f662c25179a..00000000000 --- a/src/Modules/Windows-Core+Full/PSDiagnostics/PSDiagnostics.psm1 +++ /dev/null @@ -1,446 +0,0 @@ -<# - Windows PowerShell Diagnostics Module - This module contains a set of wrapper scripts that - enable a user to use ETW tracing in Windows - PowerShell. - #> - -$script:Logman="$env:windir\system32\logman.exe" -$script:wsmanlogfile = "$env:windir\system32\wsmtraces.log" -$script:wsmprovfile = "$env:windir\system32\wsmtraceproviders.txt" -$script:wsmsession = "wsmlog" -$script:pssession = "PSTrace" -$script:psprovidername="Microsoft-Windows-PowerShell" -$script:wsmprovidername = "Microsoft-Windows-WinRM" -$script:oplog = "/Operational" -$script:analyticlog="/Analytic" -$script:debuglog="/Debug" -$script:wevtutil="$env:windir\system32\wevtutil.exe" -$script:slparam = "sl" -$script:glparam = "gl" - -function Start-Trace -{ - Param( - [Parameter(Mandatory=$true, - Position=0)] - [string] - $SessionName, - [Parameter(Position=1)] - [ValidateNotNullOrEmpty()] - [string] - $OutputFilePath, - [Parameter(Position=2)] - [ValidateNotNullOrEmpty()] - [string] - $ProviderFilePath, - [Parameter()] - [Switch] - $ETS, - [Parameter()] - [ValidateSet("bin", "bincirc", "csv", "tsv", "sql")] - $Format, - [Parameter()] - [int] - $MinBuffers=0, - [Parameter()] - [int] - $MaxBuffers=256, - [Parameter()] - [int] - $BufferSizeInKB = 0, - [Parameter()] - [int] - $MaxLogFileSizeInMB=0 - ) - - Process - { - $executestring = " start $SessionName" - - if ($ETS) - { - $executestring += " -ets" - } - - if ($null -ne $OutputFilePath) - { - $executestring += " -o ""$OutputFilePath""" - } - - if ($null -ne $ProviderFilePath) - { - $executestring += " -pf ""$ProviderFilePath""" - } - - if ($null -ne $Format) - { - $executestring += " -f $Format" - } - - if ($MinBuffers -ne 0 -or $MaxBuffers -ne 256) - { - $executestring += " -nb $MinBuffers $MaxBuffers" - } - - if ($BufferSizeInKB -ne 0) - { - $executestring += " -bs $BufferSizeInKB" - } - - if ($MaxLogFileSizeInMB -ne 0) - { - $executestring += " -max $MaxLogFileSizeInMB" - } - - & $script:Logman $executestring.Split(" ") - } -} - -function Stop-Trace -{ - param( - [Parameter(Mandatory=$true, - Position=0)] - $SessionName, - [Parameter()] - [switch] - $ETS - ) - - Process - { - if ($ETS) - { - & $script:Logman update $SessionName -ets - & $script:Logman stop $SessionName -ets - } - else - { - & $script:Logman update $SessionName - & $script:Logman stop $SessionName - } - } -} - -function Enable-WSManTrace -{ - - # winrm - "{04c6e16d-b99f-4a3a-9b3e-b8325bbc781e} 0xffffffff 0xff" | out-file $script:wsmprovfile -encoding ascii - - # winrsmgr - "{c0a36be8-a515-4cfa-b2b6-2676366efff7} 0xffffffff 0xff" | out-file $script:wsmprovfile -encoding ascii -append - - # WinrsExe - "{f1cab2c0-8beb-4fa2-90e1-8f17e0acdd5d} 0xffffffff 0xff" | out-file $script:wsmprovfile -encoding ascii -append - - # WinrsCmd - "{03992646-3dfe-4477-80e3-85936ace7abb} 0xffffffff 0xff" | out-file $script:wsmprovfile -encoding ascii -append - - # IPMIPrv - "{651d672b-e11f-41b7-add3-c2f6a4023672} 0xffffffff 0xff" | out-file $script:wsmprovfile -encoding ascii -append - - #IpmiDrv - "{D5C6A3E9-FA9C-434e-9653-165B4FC869E4} 0xffffffff 0xff" | out-file $script:wsmprovfile -encoding ascii -append - - # WSManProvHost - "{6e1b64d7-d3be-4651-90fb-3583af89d7f1} 0xffffffff 0xff" | out-file $script:wsmprovfile -encoding ascii -append - - # Event Forwarding - "{6FCDF39A-EF67-483D-A661-76D715C6B008} 0xffffffff 0xff" | out-file $script:wsmprovfile -encoding ascii -append - - Start-Trace -SessionName $script:wsmsession -ETS -OutputFilePath $script:wsmanlogfile -Format bincirc -MinBuffers 16 -MaxBuffers 256 -BufferSizeInKb 64 -MaxLogFileSizeInMB 256 -ProviderFilePath $script:wsmprovfile -} - -function Disable-WSManTrace -{ - Stop-Trace $script:wsmsession -ets -} - -function Enable-PSWSManCombinedTrace -{ - param ( - [switch] $DoNotOverwriteExistingTrace - ) - - $provfile = [io.path]::GetTempFilename() - - $traceFileName = [string][Guid]::NewGuid() - if ($DoNotOverwriteExistingTrace) { - $fileName = [string][guid]::newguid() - $logfile = $pshome + "\\Traces\\PSTrace_$fileName.etl" - } else { - $logfile = $pshome + "\\Traces\\PSTrace.etl" - } - - "Microsoft-Windows-PowerShell 0 5" | out-file $provfile -encoding ascii - "Microsoft-Windows-WinRM 0 5" | out-file $provfile -encoding ascii -append - - if (!(Test-Path $pshome\Traces)) - { - mkdir -Force $pshome\Traces | out-null - } - - if (Test-Path $logfile) - { - Remove-Item -Force $logfile | out-null - } - - Start-Trace -SessionName $script:pssession -OutputFilePath $logfile -ProviderFilePath $provfile -ets - - remove-item $provfile -Force -ea 0 -} - -function Disable-PSWSManCombinedTrace -{ - Stop-Trace -SessionName $script:pssession -ets -} - -function Set-LogProperties -{ - param( - [Parameter(Mandatory=$true, Position=0, ValueFromPipeline=$true)] - [Microsoft.PowerShell.Diagnostics.LogDetails] - $LogDetails, - [switch] $Force - ) - - Process - { - if ($LogDetails.AutoBackup -and !$LogDetails.Retention) - { - throw (New-Object System.InvalidOperationException) - } - - $enabled = $LogDetails.Enabled.ToString() - $retention = $LogDetails.Retention.ToString() - $autobackup = $LogDetails.AutoBackup.ToString() - $maxLogSize = $LogDetails.MaxLogSize.ToString() - $osVersion = [Version] (Get-Ciminstance Win32_OperatingSystem).Version - - if (($LogDetails.Type -eq "Analytic") -or ($LogDetails.Type -eq "Debug")) - { - if ($LogDetails.Enabled) - { - if($osVersion -lt 6.3.7600) - { - & $script:wevtutil $script:slparam $LogDetails.Name -e:$Enabled - } - else - { - & $script:wevtutil /q:$Force $script:slparam $LogDetails.Name -e:$Enabled - } - } - else - { - if($osVersion -lt 6.3.7600) - { - & $script:wevtutil $script:slparam $LogDetails.Name -e:$Enabled -rt:$Retention -ms:$MaxLogSize - } - else - { - & $script:wevtutil /q:$Force $script:slparam $LogDetails.Name -e:$Enabled -rt:$Retention -ms:$MaxLogSize - } - } - } - else - { - if($osVersion -lt 6.3.7600) - { - & $script:wevtutil $script:slparam $LogDetails.Name -e:$Enabled -rt:$Retention -ab:$AutoBackup -ms:$MaxLogSize - } - else - { - & $script:wevtutil /q:$Force $script:slparam $LogDetails.Name -e:$Enabled -rt:$Retention -ab:$AutoBackup -ms:$MaxLogSize - } - } - } -} - -function ConvertTo-Bool([string]$value) -{ - if ($value -ieq "true") - { - return $true - } - else - { - return $false - } -} - -function Get-LogProperties -{ - param( - [Parameter(Mandatory=$true, ValueFromPipeline=$true, Position=0)] $Name - ) - - Process - { - $details = & $script:wevtutil $script:glparam $Name - $indexes = @(1,2,8,9,10) - $value = @() - foreach($index in $indexes) - { - $value += @(($details[$index].SubString($details[$index].IndexOf(":")+1)).Trim()) - } - - $enabled = ConvertTo-Bool $value[0] - $retention = ConvertTo-Bool $value[2] - $autobackup = ConvertTo-Bool $value[3] - - New-Object Microsoft.PowerShell.Diagnostics.LogDetails $Name, $enabled, $value[1], $retention, $autobackup, $value[4] - } -} - -function Enable-PSTrace -{ - param( - [switch] $Force, - [switch] $AnalyticOnly - ) - - $Properties = Get-LogProperties ($script:psprovidername + $script:analyticlog) - - if (!$Properties.Enabled) { - $Properties.Enabled = $true - if ($Force) { - Set-LogProperties $Properties -Force - } else { - Set-LogProperties $Properties - } - } - - if (!$AnalyticOnly) { - $Properties = Get-LogProperties ($script:psprovidername + $script:debuglog) - if (!$Properties.Enabled) { - $Properties.Enabled = $true - if ($Force) { - Set-LogProperties $Properties -Force - } else { - Set-LogProperties $Properties - } - } - } -} - -function Disable-PSTrace -{ - param( - [switch] $AnalyticOnly - ) - $Properties = Get-LogProperties ($script:psprovidername + $script:analyticlog) - if ($Properties.Enabled) { - $Properties.Enabled = $false - Set-LogProperties $Properties - } - - if (!$AnalyticOnly) { - $Properties = Get-LogProperties ($script:psprovidername + $script:debuglog) - if ($Properties.Enabled) { - $Properties.Enabled = $false - Set-LogProperties $Properties - } - } -} -add-type @" -using System; - -namespace Microsoft.PowerShell.Diagnostics -{ - public class LogDetails - { - public string Name - { - get - { - return name; - } - } - private string name; - - public bool Enabled - { - get - { - return enabled; - } - set - { - enabled = value; - } - } - private bool enabled; - - public string Type - { - get - { - return type; - } - } - private string type; - - public bool Retention - { - get - { - return retention; - } - set - { - retention = value; - } - } - private bool retention; - - public bool AutoBackup - { - get - { - return autoBackup; - } - set - { - autoBackup = value; - } - } - private bool autoBackup; - - public int MaxLogSize - { - get - { - return maxLogSize; - } - set - { - maxLogSize = value; - } - } - private int maxLogSize; - - public LogDetails(string name, bool enabled, string type, bool retention, bool autoBackup, int maxLogSize) - { - this.name = name; - this.enabled = enabled; - this.type = type; - this.retention = retention; - this.autoBackup = autoBackup; - this.maxLogSize = maxLogSize; - } - } -} -"@ - - -if ($psedition -eq 'Core') - { - # Currently we only support these cmdlets as logman.exe is not working on Nano/Lot system. - Export-ModuleMember Enable-PSTrace, Disable-PSTrace, Get-LogProperties, Set-LogProperties - } - else - { - Export-ModuleMember Start-Trace, Stop-Trace, Enable-WSManTrace, Disable-WSManTrace, Enable-PSTrace, Disable-PSTrace, Enable-PSWSManCombinedTrace, Disable-PSWSManCombinedTrace, Get-LogProperties, Set-LogProperties - } diff --git a/src/Modules/Windows-Core/Microsoft.PowerShell.Diagnostics/Diagnostics.format.ps1xml b/src/Modules/Windows-Core/Microsoft.PowerShell.Diagnostics/Diagnostics.format.ps1xml deleted file mode 100644 index aeab08d3a46..00000000000 --- a/src/Modules/Windows-Core/Microsoft.PowerShell.Diagnostics/Diagnostics.format.ps1xml +++ /dev/null @@ -1,79 +0,0 @@ - - - - - - - - Counter - - Microsoft.PowerShell.Commands.GetCounter.PerformanceCounterSampleSet - - - - - - 25 - left - - - - left - 100 - - - - - - - - Timestamp - - - Readings - - - - - - - - Counter - - Microsoft.PowerShell.Commands.GetCounter.CounterFileInfo - - - - - 30 - left - - - 30 - left - - - 30 - left - - - - - - - - OldestRecord - - - NewestRecord - - - SampleCount - - - - - - - - diff --git a/src/Modules/Windows-Core/Microsoft.PowerShell.Diagnostics/Event.format.ps1xml b/src/Modules/Windows-Core/Microsoft.PowerShell.Diagnostics/Event.format.ps1xml deleted file mode 100644 index d4e78a3d313..00000000000 --- a/src/Modules/Windows-Core/Microsoft.PowerShell.Diagnostics/Event.format.ps1xml +++ /dev/null @@ -1,121 +0,0 @@ - - - - - Default - - System.Diagnostics.Eventing.Reader.EventLogRecord - - - ProviderName - - - - - - 25 - - - 8 - right - - - 16 - - - - - - - - - TimeCreated - - - Id - - - LevelDisplayName - - - Message - - - - - - - - - Default - - System.Diagnostics.Eventing.Reader.EventLogConfiguration - - - - - - - 9 - - - - 18 - right - - - - 11 - right - - - - - - - - LogMode - - - MaximumSizeInBytes - - - RecordCount - - - LogName - - - - - - - - Default - - System.Diagnostics.Eventing.Reader.ProviderMetadata - - - - - - - Name - - - LogLinks - - - Opcodes - - - Tasks - - - - - - - - - diff --git a/src/Modules/Windows-Core/Microsoft.PowerShell.Diagnostics/GetEvent.types.ps1xml b/src/Modules/Windows-Core/Microsoft.PowerShell.Diagnostics/GetEvent.types.ps1xml deleted file mode 100644 index 29a42edfab1..00000000000 --- a/src/Modules/Windows-Core/Microsoft.PowerShell.Diagnostics/GetEvent.types.ps1xml +++ /dev/null @@ -1,139 +0,0 @@ - - - - - - System.Diagnostics.Eventing.Reader.EventLogConfiguration - - - PSStandardMembers - - - DefaultDisplayPropertySet - - LogName - MaximumSizeInBytes - RecordCount - LogMode - - - - - - - - System.Diagnostics.Eventing.Reader.EventLogRecord - - - PSStandardMembers - - - DefaultDisplayPropertySet - - TimeCreated - ProviderName - Id - Message - - - - - - - - System.Diagnostics.Eventing.Reader.ProviderMetadata - - - ProviderName - Name - - - PSStandardMembers - - - DefaultDisplayPropertySet - - Name - LogLinks - - - - - - - - Microsoft.PowerShell.Commands.GetCounter.CounterSet - - - Counter - Paths - - - - - Microsoft.PowerShell.Commands.GetCounter.PerformanceCounterSample - - - PSStandardMembers - - - DefaultDisplayPropertySet - - Path - InstanceName - CookedValue - - - - - - - - Microsoft.PowerShell.Commands.GetCounter.PerformanceCounterSampleSet - - - PSStandardMembers - - - DefaultDisplayPropertySet - - Timestamp - Readings - - - - - - - - Microsoft.PowerShell.Commands.GetCounter.PerformanceCounterSampleSet - - - Readings - - $strPaths = "" - foreach ($ctr in $this.CounterSamples) - { - $strPaths += ($ctr.Path + " :" + "`n") - $strPaths += ($ctr.CookedValue.ToString() + "`n`n") - } - return $strPaths - - - - - diff --git a/src/Modules/Windows-Core/Microsoft.PowerShell.Diagnostics/Microsoft.PowerShell.Diagnostics.psd1 b/src/Modules/Windows-Core/Microsoft.PowerShell.Diagnostics/Microsoft.PowerShell.Diagnostics.psd1 deleted file mode 100644 index daaea553726..00000000000 --- a/src/Modules/Windows-Core/Microsoft.PowerShell.Diagnostics/Microsoft.PowerShell.Diagnostics.psd1 +++ /dev/null @@ -1,13 +0,0 @@ -@{ -GUID="CA046F10-CA64-4740-8FF9-2565DBA61A4F" -Author="Microsoft Corporation" -CompanyName="Microsoft Corporation" -Copyright="© Microsoft Corporation. All rights reserved." -ModuleVersion="3.0.0.0" -PowerShellVersion="3.0" -CmdletsToExport="Get-WinEvent", "New-WinEvent" # Counter CmdLets Disabled #4272: "Get-Counter", "Import-Counter", "Export-Counter" -NestedModules="Microsoft.PowerShell.Commands.Diagnostics.dll" -TypesToProcess="GetEvent.types.ps1xml" -FormatsToProcess="Event.format.ps1xml", "Diagnostics.format.ps1xml" -HelpInfoURI = 'https://go.microsoft.com/fwlink/?linkid=390783' -} diff --git a/src/Modules/Windows-Core/Microsoft.PowerShell.Management/Microsoft.PowerShell.Management.psd1 b/src/Modules/Windows-Core/Microsoft.PowerShell.Management/Microsoft.PowerShell.Management.psd1 deleted file mode 100644 index 9a8bdff1565..00000000000 --- a/src/Modules/Windows-Core/Microsoft.PowerShell.Management/Microsoft.PowerShell.Management.psd1 +++ /dev/null @@ -1,68 +0,0 @@ -@{ -GUID="EEFCB906-B326-4E99-9F54-8B4BB6EF3C6D" -Author="Microsoft Corporation" -CompanyName="Microsoft Corporation" -Copyright=" Microsoft Corporation. All rights reserved." -ModuleVersion="3.1.0.0" -PowerShellVersion="3.0" -NestedModules="Microsoft.PowerShell.Commands.Management.dll" -HelpInfoURI = 'https://go.microsoft.com/fwlink/?linkid=390785' -AliasesToExport = @("gin", "gtz", "stz") -FunctionsToExport = @() -CmdletsToExport=@("Add-Content", - "Clear-Content", - "Clear-ItemProperty", - "Join-Path", - "Convert-Path", - "Copy-ItemProperty", - "Get-ChildItem", - "Get-Content", - "Get-ItemProperty", - "Get-ItemPropertyValue", - "Move-ItemProperty", - "Get-Location", - "Set-Location", - "Push-Location", - "Pop-Location", - "New-PSDrive", - "Remove-PSDrive", - "Get-PSDrive", - "Get-Item", - "New-Item", - "Set-Item", - "Remove-Item", - "Move-Item", - "Rename-Item", - "Copy-Item", - "Clear-Item", - "Invoke-Item", - "Get-PSProvider", - "New-ItemProperty", - "Split-Path", - "Test-Path", - "Get-Process", - "Stop-Process", - "Wait-Process", - "Debug-Process", - "Start-Process", - "Remove-ItemProperty", - "Rename-ItemProperty", - "Resolve-Path", - "Get-Service", - "Stop-Service", - "Start-Service", - "Suspend-Service", - "Resume-Service", - "Restart-Service", - "Set-Service", - "New-Service", - "Set-Content", - "Set-ItemProperty", - "Test-Connection", - "Restart-Computer", - "Stop-Computer", - "Rename-Computer", - "Get-ComputerInfo", - "Get-TimeZone", - "Set-TimeZone") -} diff --git a/src/Modules/Windows-Core/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1 b/src/Modules/Windows-Core/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1 deleted file mode 100644 index 1fc9d4e1ad3..00000000000 --- a/src/Modules/Windows-Core/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1 +++ /dev/null @@ -1,30 +0,0 @@ -@{ -GUID="1DA87E53-152B-403E-98DC-74D7B4D63D59" -Author="Microsoft Corporation" -CompanyName="Microsoft Corporation" -Copyright="© Microsoft Corporation. All rights reserved." -ModuleVersion="3.1.0.0" -PowerShellVersion="3.0" -CmdletsToExport= "Format-List", "Format-Custom", "Format-Table", "Format-Wide", - "Out-File", "Out-String", "Get-FormatData", "Export-FormatData", "ConvertFrom-Json", "ConvertTo-Json", - "Invoke-RestMethod", "Invoke-WebRequest", "Register-ObjectEvent", "Register-EngineEvent", - "Wait-Event", "Get-Event", "Remove-Event", "Get-EventSubscriber", "Unregister-Event", "New-Guid", - "New-Event", "Add-Member", "Add-Type", "Compare-Object", "ConvertTo-Html", "ConvertFrom-StringData", - "Export-Csv", "Import-Csv", "ConvertTo-Csv", "ConvertFrom-Csv", "Export-Alias", "Invoke-Expression", - "Get-Alias", "Get-Culture", "Get-Date", "Get-Host", "Get-Member", "Get-Random", "Get-UICulture", - "Get-Unique", "Export-PSSession", "Import-PSSession", "Import-Alias", "Import-LocalizedData", - "Select-String", "Measure-Object", "New-Alias", "New-TimeSpan", "Read-Host", "Set-Alias", "Set-Date", - "Start-Sleep", "Tee-Object", "Measure-Command", "Update-TypeData", "Update-FormatData", - "Remove-TypeData", "Get-TypeData", "Write-Host", "Write-Progress", "New-Object", "Select-Object", - "Group-Object", "Sort-Object", "Get-Variable", "New-Variable", "Set-Variable", "Remove-Variable", - "Clear-Variable", "Export-Clixml", "Import-Clixml", "Import-PowerShellDataFile","ConvertTo-Xml", "Select-Xml", "Write-Debug", - "Write-Verbose", "Write-Warning", "Write-Error", "Write-Information", "Write-Output", "Set-PSBreakpoint", - "Get-PSBreakpoint", "Remove-PSBreakpoint", "New-TemporaryFile", "Enable-PSBreakpoint", "Disable-PSBreakpoint", "Get-PSCallStack", - "Send-MailMessage", "Get-TraceSource", "Set-TraceSource", "Trace-Command", "Get-FileHash", - "Unblock-File", "Get-Runspace", "Debug-Runspace", "Enable-RunspaceDebug", "Disable-RunspaceDebug", - "Get-RunspaceDebug", "Wait-Debugger" , "Get-Uptime", "Get-Verb", "Format-Hex" -FunctionsToExport= "ConvertFrom-SddlString" -AliasesToExport= "fhx" -NestedModules="Microsoft.PowerShell.Commands.Utility.dll","Microsoft.PowerShell.Utility.psm1" -HelpInfoURI = 'https://go.microsoft.com/fwlink/?linkid=390787' -} diff --git a/src/Modules/Windows-Full/Microsoft.PowerShell.Diagnostics/Diagnostics.format.ps1xml b/src/Modules/Windows-Full/Microsoft.PowerShell.Diagnostics/Diagnostics.format.ps1xml deleted file mode 100644 index a290c620ce5..00000000000 --- a/src/Modules/Windows-Full/Microsoft.PowerShell.Diagnostics/Diagnostics.format.ps1xml +++ /dev/null @@ -1,79 +0,0 @@ - - - - - - - - Counter - - Microsoft.PowerShell.Commands.GetCounter.PerformanceCounterSampleSet - - - - - - 25 - left - - - - left - 100 - - - - - - - - Timestamp - - - Readings - - - - - - - - Counter - - Microsoft.PowerShell.Commands.GetCounter.CounterFileInfo - - - - - 30 - left - - - 30 - left - - - 30 - left - - - - - - - - OldestRecord - - - NewestRecord - - - SampleCount - - - - - - - - diff --git a/src/Modules/Windows-Full/Microsoft.PowerShell.Diagnostics/Event.format.ps1xml b/src/Modules/Windows-Full/Microsoft.PowerShell.Diagnostics/Event.format.ps1xml deleted file mode 100644 index 476af9f5e2d..00000000000 --- a/src/Modules/Windows-Full/Microsoft.PowerShell.Diagnostics/Event.format.ps1xml +++ /dev/null @@ -1,121 +0,0 @@ - - - - - Default - - System.Diagnostics.Eventing.Reader.EventLogRecord - - - ProviderName - - - - - - 25 - - - 8 - right - - - 16 - - - - - - - - - TimeCreated - - - Id - - - LevelDisplayName - - - Message - - - - - - - - - Default - - System.Diagnostics.Eventing.Reader.EventLogConfiguration - - - - - - - 9 - - - - 18 - right - - - - 11 - right - - - - - - - - LogMode - - - MaximumSizeInBytes - - - RecordCount - - - LogName - - - - - - - - Default - - System.Diagnostics.Eventing.Reader.ProviderMetadata - - - - - - - Name - - - LogLinks - - - Opcodes - - - Tasks - - - - - - - - - diff --git a/src/Modules/Windows-Full/Microsoft.PowerShell.Diagnostics/GetEvent.types.ps1xml b/src/Modules/Windows-Full/Microsoft.PowerShell.Diagnostics/GetEvent.types.ps1xml deleted file mode 100644 index 6a9bc8edafe..00000000000 --- a/src/Modules/Windows-Full/Microsoft.PowerShell.Diagnostics/GetEvent.types.ps1xml +++ /dev/null @@ -1,139 +0,0 @@ - - - - - - System.Diagnostics.Eventing.Reader.EventLogConfiguration - - - PSStandardMembers - - - DefaultDisplayPropertySet - - LogName - MaximumSizeInBytes - RecordCount - LogMode - - - - - - - - System.Diagnostics.Eventing.Reader.EventLogRecord - - - PSStandardMembers - - - DefaultDisplayPropertySet - - TimeCreated - ProviderName - Id - Message - - - - - - - - System.Diagnostics.Eventing.Reader.ProviderMetadata - - - ProviderName - Name - - - PSStandardMembers - - - DefaultDisplayPropertySet - - Name - LogLinks - - - - - - - - Microsoft.PowerShell.Commands.GetCounter.CounterSet - - - Counter - Paths - - - - - Microsoft.PowerShell.Commands.GetCounter.PerformanceCounterSample - - - PSStandardMembers - - - DefaultDisplayPropertySet - - Path - InstanceName - CookedValue - - - - - - - - Microsoft.PowerShell.Commands.GetCounter.PerformanceCounterSampleSet - - - PSStandardMembers - - - DefaultDisplayPropertySet - - Timestamp - Readings - - - - - - - - Microsoft.PowerShell.Commands.GetCounter.PerformanceCounterSampleSet - - - Readings - - $strPaths = "" - foreach ($ctr in $this.CounterSamples) - { - $strPaths += ($ctr.Path + " :" + "`n") - $strPaths += ($ctr.CookedValue.ToString() + "`n`n") - } - return $strPaths - - - - - diff --git a/src/Modules/Windows-Full/Microsoft.PowerShell.Diagnostics/Microsoft.PowerShell.Diagnostics.psd1 b/src/Modules/Windows-Full/Microsoft.PowerShell.Diagnostics/Microsoft.PowerShell.Diagnostics.psd1 deleted file mode 100644 index 9bfdb981d49..00000000000 --- a/src/Modules/Windows-Full/Microsoft.PowerShell.Diagnostics/Microsoft.PowerShell.Diagnostics.psd1 +++ /dev/null @@ -1,16 +0,0 @@ -@{ -GUID="CA046F10-CA64-4740-8FF9-2565DBA61A4F" -Author="Microsoft Corporation" -CompanyName="Microsoft Corporation" -Copyright=" Microsoft Corporation. All rights reserved." -ModuleVersion="3.0.0.0" -PowerShellVersion="3.0" -CLRVersion="4.0" -AliasesToExport = @() -FunctionsToExport = @() -CmdletsToExport="Get-WinEvent", "Get-Counter", "Import-Counter", "Export-Counter", "New-WinEvent" -NestedModules="Microsoft.PowerShell.Commands.Diagnostics.dll" -TypesToProcess="GetEvent.types.ps1xml" -FormatsToProcess="Event.format.ps1xml","Diagnostics.format.ps1xml" -HelpInfoURI = 'https://go.microsoft.com/fwlink/?linkid=390783' -} diff --git a/src/Modules/Windows-Full/Microsoft.PowerShell.LocalAccounts/LocalAccounts.format.ps1xml b/src/Modules/Windows-Full/Microsoft.PowerShell.LocalAccounts/LocalAccounts.format.ps1xml deleted file mode 100644 index 91b3b358a23..00000000000 --- a/src/Modules/Windows-Full/Microsoft.PowerShell.LocalAccounts/LocalAccounts.format.ps1xml +++ /dev/null @@ -1,93 +0,0 @@ - - - - - Microsoft.PowerShell.Commands.LocalUser - - Microsoft.PowerShell.Commands.LocalUser - - - - - - - - - - - - - - - Name - - - Enabled - - - Description - - - - - - - - Microsoft.PowerShell.Commands.LocalGroup - - Microsoft.PowerShell.Commands.LocalGroup - - - - - - - - - - - - - Name - - - Description - - - - - - - - Microsoft.PowerShell.Commands.LocalPrincipal - - Microsoft.PowerShell.Commands.LocalPrincipal - - - - - - - - - - - - - - - ObjectClass - - - Name - - - PrincipalSource - - - - - - - - \ No newline at end of file diff --git a/src/Modules/Windows-Full/Microsoft.PowerShell.LocalAccounts/Microsoft.PowerShell.LocalAccounts.psd1 b/src/Modules/Windows-Full/Microsoft.PowerShell.LocalAccounts/Microsoft.PowerShell.LocalAccounts.psd1 deleted file mode 100644 index b2b562f0002..00000000000 --- a/src/Modules/Windows-Full/Microsoft.PowerShell.LocalAccounts/Microsoft.PowerShell.LocalAccounts.psd1 +++ /dev/null @@ -1,32 +0,0 @@ -@{ -RootModule = 'Microsoft.Powershell.LocalAccounts' -ModuleVersion = '1.0.0.0' -GUID = '8e362604-2c0b-448f-a414-a6a690a644e2' -Author = 'Microsoft Corporation' -CompanyName = 'Microsoft Corporation' -Copyright = '© Microsoft Corporation. All rights reserved.' -Description = 'Provides cmdlets to work with local users and local groups' -PowerShellVersion = '3.0' -CLRVersion = '4.0' -FormatsToProcess = @('LocalAccounts.format.ps1xml') -CmdletsToExport = @( - 'Add-LocalGroupMember', - 'Disable-LocalUser', - 'Enable-LocalUser', - 'Get-LocalGroup', - 'Get-LocalGroupMember', - 'Get-LocalUser', - 'New-LocalGroup', - 'New-LocalUser', - 'Remove-LocalGroup', - 'Remove-LocalGroupMember', - 'Remove-LocalUser', - 'Rename-LocalGroup', - 'Rename-LocalUser', - 'Set-LocalGroup', - 'Set-LocalUser' - ) -AliasesToExport= @( "algm", "dlu", "elu", "glg", "glgm", "glu", "nlg", "nlu", "rlg", "rlgm", "rlu", "rnlg", "rnlu", "slg", "slu") -HelpInfoURI = 'https://go.microsoft.com/fwlink/?LinkId=717973' -} - diff --git a/src/Modules/Windows-Full/Microsoft.PowerShell.Management/Microsoft.PowerShell.Management.psd1 b/src/Modules/Windows-Full/Microsoft.PowerShell.Management/Microsoft.PowerShell.Management.psd1 deleted file mode 100644 index 6325b6fe97d..00000000000 --- a/src/Modules/Windows-Full/Microsoft.PowerShell.Management/Microsoft.PowerShell.Management.psd1 +++ /dev/null @@ -1,102 +0,0 @@ -@{ -GUID="EEFCB906-B326-4E99-9F54-8B4BB6EF3C6D" -Author="Microsoft Corporation" -CompanyName="Microsoft Corporation" -Copyright=" Microsoft Corporation. All rights reserved." -ModuleVersion="3.1.0.0" -PowerShellVersion="3.0" -CLRVersion="4.0" -NestedModules="Microsoft.PowerShell.Commands.Management.dll" -HelpInfoURI = 'https://go.microsoft.com/fwlink/?linkid=390785' -AliasesToExport = @("gcb", "scb", "gin", "gtz", "stz") -FunctionsToExport = @() -CmdletsToExport=@("Add-Content", - "Clear-Content", - "Clear-ItemProperty", - "Join-Path", - "Convert-Path", - "Copy-ItemProperty", - "Get-EventLog", - "Clear-EventLog", - "Write-EventLog", - "Limit-EventLog", - "Show-EventLog", - "New-EventLog", - "Remove-EventLog", - "Get-ChildItem", - "Get-Content", - "Get-ItemProperty", - "Get-ItemPropertyValue", - "Get-WmiObject", - "Invoke-WmiMethod", - "Move-ItemProperty", - "Get-Location", - "Set-Location", - "Push-Location", - "Pop-Location", - "New-PSDrive", - "Remove-PSDrive", - "Get-PSDrive", - "Get-Item", - "New-Item", - "Set-Item", - "Remove-Item", - "Move-Item", - "Rename-Item", - "Copy-Item", - "Clear-Item", - "Invoke-Item", - "Get-PSProvider", - "New-ItemProperty", - "Split-Path", - "Test-Path", - "Get-Process", - "Stop-Process", - "Wait-Process", - "Debug-Process", - "Start-Process", - "Remove-ItemProperty", - "Remove-WmiObject", - "Rename-ItemProperty", - "Register-WmiEvent", - "Resolve-Path", - "Get-Service", - "Stop-Service", - "Start-Service", - "Suspend-Service", - "Resume-Service", - "Restart-Service", - "Set-Service", - "New-Service", - "Set-Content", - "Set-ItemProperty", - "Set-WmiInstance", - "Get-Transaction", - "Start-Transaction", - "Complete-Transaction", - "Undo-Transaction", - "Use-Transaction", - "New-WebServiceProxy", - "Get-HotFix", - "Test-Connection", - "Enable-ComputerRestore", - "Disable-ComputerRestore", - "Checkpoint-Computer", - "Get-ComputerRestorePoint", - "Restart-Computer", - "Stop-Computer", - "Restore-Computer", - "Add-Computer", - "Remove-Computer", - "Test-ComputerSecureChannel", - "Reset-ComputerMachinePassword", - "Rename-Computer", - "Get-ControlPanelItem", - "Show-ControlPanelItem", - "Clear-Recyclebin", - "Get-Clipboard", - "Set-Clipboard", - "Get-ComputerInfo", - "Get-TimeZone", - "Set-TimeZone") -} diff --git a/src/Modules/Windows-Full/Microsoft.PowerShell.ODataUtils/Microsoft.PowerShell.ODataAdapter.ps1 b/src/Modules/Windows-Full/Microsoft.PowerShell.ODataUtils/Microsoft.PowerShell.ODataAdapter.ps1 deleted file mode 100644 index 22ad15fb766..00000000000 --- a/src/Modules/Windows-Full/Microsoft.PowerShell.ODataUtils/Microsoft.PowerShell.ODataAdapter.ps1 +++ /dev/null @@ -1,2070 +0,0 @@ -Import-LocalizedData LocalizedData -FileName Microsoft.PowerShell.ODataUtilsStrings.psd1 - - -# Add .NET classes used by the module -Add-Type -TypeDefinition $global:BaseClassDefinitions - -######################################################### -# Generates PowerShell module containing client side -# proxy cmdlets that can be used to interact with an -# OData based server side endpoint. -######################################################### -function ExportODataEndpointProxy -{ - param - ( - [string] $Uri, - [string] $OutputModule, - [string] $MetadataUri, - [PSCredential] $Credential, - [string] $CreateRequestMethod, - [string] $UpdateRequestMethod, - [string] $CmdletAdapter, - [Hashtable] $ResourceNameMapping, - [switch] $Force, - [Hashtable] $CustomData, - [switch] $AllowClobber, - [switch] $AllowUnsecureConnection, - [Hashtable] $Headers, - [string] $ProgressBarStatus, - [System.Management.Automation.PSCmdlet] $PSCmdlet - ) - - [xml] $metadataXML = GetMetaData $MetadataUri $PSCmdlet $Credential $Headers - - [ODataUtils.Metadata] $metaData = ParseMetadata $metadataXML $MetadataUri $CmdletAdapter $PSCmdlet - - VerifyMetaData $MetadataUri $metaData $AllowClobber.IsPresent $PSCmdlet $progressBarStatus $CmdletAdapter $CustomData $ResourceNameMapping - - GenerateClientSideProxyModule $metaData $MetadataUri $Uri $OutputModule $CreateRequestMethod $UpdateRequestMethod $CmdletAdapter $ResourceNameMapping $CustomData $ProgressBarStatus $PSCmdlet -} - -######################################################### -# ParseMetaData is a helper function used to parse the -# metadata to convert it in to an object structure for -# further consumption during proxy generation. -######################################################### -function ParseMetaData -{ - param - ( - [xml] $metadataXml, - [string] $metaDataUri, - [string] $cmdletAdapter, - [System.Management.Automation.PSCmdlet] $callerPSCmdlet - ) - - # $metaDataUri is already validated at the cmdlet layer. - if($null -eq $callerPSCmdlet) { throw ($LocalizedData.ArguementNullError -f "PSCmdlet", "ParseMetadata") } - - if($null -eq $metadataXml) - { - $errorMessage = ($LocalizedData.InValidXmlInMetadata -f $metaDataUri) - $exception = [System.InvalidOperationException]::new($errorMessage, $_.Exception) - $errorRecord = CreateErrorRecordHelper "ODataEndpointProxyInvalidMetadataUriFormat" $null ([System.Management.Automation.ErrorCategory]::InvalidArgument) $exception $metaDataUri - $callerPSCmdlet.ThrowTerminatingError($errorRecord) - } - - Write-Verbose $LocalizedData.VerboseParsingMetadata - - # Check the OData version in the fetched metadata to make sure that - # OData version (and hence the protocol) used in the metadata is - # supported by the adapter used for executing the generated - # proxy cmdlets. - if(($null -ne $metadataXML) -and ($null -ne $metadataXML.Edmx)) - { - if($null -eq $metadataXML.Edmx.Version) - { - $errorMessage = ($LocalizedData.ODataVersionNotFound -f $MetadataUri) - $exception = [System.InvalidOperationException]::new($errorMessage) - $errorRecord = CreateErrorRecordHelper "ODataEndpointProxyODataVersionNotFound" $null ([System.Management.Automation.ErrorCategory]::InvalidData) $exception $MetadataUri - $callerPSCmdlet.ThrowTerminatingError($errorRecord) - } - - $metaDataVersion = New-Object -TypeName System.Version -ArgumentList @($metadataXML.Edmx.Version) - - # When we support plug-in model, We would have to fetch the - # $minSupportedVersionString & $maxSupportedVersionString - # from the plug-in instead of using an hardcoded value. - $minSupportedVersionString = '1.0' - $maxSupportedVersionString = '3.0' - $minSupportedVersion = New-Object -TypeName System.Version -ArgumentList @($minSupportedVersionString) - $maxSupportedVersion = New-Object -TypeName System.Version -ArgumentList @($maxSupportedVersionString) - - $minVersionComparisonResult = $minSupportedVersion.CompareTo($metaDataVersion) - $maxVersionComparisonResult = $maxSupportedVersion.CompareTo($metaDataVersion) - - if(-not($minVersionComparisonResult -lt $maxVersionComparisonResult)) - { - $errorMessage = ($LocalizedData.ODataVersionNotSupported -f $metadataXML.Edmx.Version, $MetadataUri, $minSupportedVersionString, $maxSupportedVersionString, $CmdletAdapter) - $exception = [System.NotSupportedException]::new($errorMessage) - $errorRecord = CreateErrorRecordHelper "ODataEndpointProxyODataVersionNotSupported" $null ([System.Management.Automation.ErrorCategory]::InvalidData) $exception $MetadataUri - $callerPSCmdlet.ThrowTerminatingError($errorRecord) - } - } - else - { - $errorMessage = ($LocalizedData.InValidMetadata -f $MetadataUri) - $exception = [System.InvalidOperationException]::new($errorMessage) - $errorRecord = CreateErrorRecordHelper "ODataEndpointProxyInvalidMetadata" $null ([System.Management.Automation.ErrorCategory]::InvalidData) $exception $MetadataUri - $callerPSCmdlet.ThrowTerminatingError($errorRecord) - } - - foreach ($schema in $MetadataXML.Edmx.DataServices.Schema) - { - if (($null -ne $schema) -and [string]::IsNullOrEmpty($schema.NameSpace )) - { - $callerPSCmdlet = $callerPSCmdlet -as [System.Management.Automation.PSCmdlet] - $errorMessage = ($LocalizedData.InValidSchemaNamespace -f $metaDataUri) - $exception = [System.InvalidOperationException]::new($errorMessage) - $errorRecord = CreateErrorRecordHelper "ODataEndpointProxyInvalidSchemaNamespace" $null ([System.Management.Automation.ErrorCategory]::InvalidArgument) $exception $metaDataUri - $callerPSCmdlet.ThrowTerminatingError($errorRecord) - } - } - - $metaData = New-Object -TypeName ODataUtils.Metadata - - # this is a processing queue for those types that require base types that haven't been defined yet - $entityAndComplexTypesQueue = @{} - - foreach ($schema in $metadataXml.Edmx.DataServices.Schema) - { - if ($null -eq $schema) - { - Write-Error $LocalizedData.EmptySchema - continue - } - - if ($null -eq $metadata.Namespace) - { - $metaData.Namespace = $schema.Namespace - } - - foreach ($entityType in $schema.EntityType) - { - $baseType = $null - - if ($null -ne $entityType.BaseType) - { - # add it to the processing queue - $baseType = GetBaseType $entityType $metaData - if ($null -eq $baseType) - { - $entityAndComplexTypesQueue[$entityType.BaseType] += @(@{type='EntityType'; value=$entityType}) - continue - } - } - - [ODataUtils.EntityType] $newType = ParseMetadataTypeDefinition $entityType $baseType $metaData $schema.Namespace $true - $metaData.EntityTypes += $newType - AddDerivedTypes $newType $entityAndComplexTypesQueue $metaData $schema.Namespace - } - - foreach ($complexType in $schema.ComplexType) - { - $baseType = $null - - if ($null -ne $complexType.BaseType) - { - # add it to the processing queue - $baseType = GetBaseType $complexType $metaData - if ($null -eq $baseType) - { - $entityAndComplexTypesQueue[$entityType.BaseType] += @(@{type='ComplexType'; value=$complexType}) - continue - } - } - - [ODataUtils.EntityType] $newType = ParseMetadataTypeDefinition $complexType $baseType $metaData $schema.Namespace $false - $metaData.ComplexTypes += $newType - AddDerivedTypes $newType $entityAndComplexTypesQueue $metaData $schema.Namespace - } - } - - foreach ($schema in $metadataXml.Edmx.DataServices.Schema) - { - foreach ($entityContainer in $schema.EntityContainer) - { - if ($entityContainer.IsDefaultEntityContainer) - { - $metaData.DefaultEntityContainerName = $entityContainer.Name - } - - $entityTypeToEntitySetMapping = @{}; - foreach ($entitySet in $entityContainer.EntitySet) - { - $entityType = $metaData.EntityTypes | Where-Object { $_.Name -eq $entitySet.EntityType.Split('.')[-1] } - $entityTypeName = $entityType.Name - - if($entityTypeToEntitySetMapping.ContainsKey($entityTypeName)) - { - $existingEntitySetName = $entityTypeToEntitySetMapping[$entityTypeName] - - $errorMessage = ($LocalizedData.EntityNameConflictError -f $metaDataUri, $existingEntitySetName, $entitySet.Name, $entityTypeName) - $exception = [System.NotSupportedException]::new($errorMessage) - $errorRecord = CreateErrorRecordHelper "ODataEndpointProxyEntityTypeMappingError" $null ([System.Management.Automation.ErrorCategory]::InvalidData) $exception $metaDataUri - $callerPSCmdlet.ThrowTerminatingError($errorRecord) - } - else - { - $entityTypeToEntitySetMapping.Add($entityTypeName, $entitySet.Name) - } - - $newEntitySet = [ODataUtils.EntitySet] @{ - "Namespace" = $schema.Namespace; - "Name" = $entitySet.Name; - "Type" = $entityType; - } - - $metaData.EntitySets += $newEntitySet - } - } - } - - foreach ($schema in $metadataXml.Edmx.DataServices.Schema) - { - foreach ($association in $schema.Association) - { - $newAssociationType = [ODataUtils.AssociationType] @{ - "Namespace" = $schema.Namespace; - "EndType1" = $metaData.EntityTypes | Where-Object { $_.Name -eq $association.End[0].Type.Split('.')[-1] }; - "NavPropertyName1" = $association.End[0].Role; - "Multiplicity1" = $association.End[0].Multiplicity; - - "EndType2" = $metaData.EntityTypes | Where-Object { $_.Name -eq $association.End[1].Type.Split('.')[-1] }; - "NavPropertyName2" = $association.End[1].Role; - "Multiplicity2" = $association.End[1].Multiplicity; - } - - $newAssociation = [ODataUtils.AssociationSet] @{ - "Namespace" = $schema.Namespace; - "Name" = $association.Name; - "Type" = $newAssociationType; - } - - $metaData.Associations += $newAssociation - } - } - - foreach ($schema in $metadataXml.Edmx.DataServices.Schema) - { - foreach ($action in $schema.EntityContainer.FunctionImport) - { - # HttpMethod is only used for legacy Service Operations - if ($null -eq $action.HttpMethod) - { - if ($null -ne $action.IsSideEffecting) - { - $isSideEffecting = $action.IsSideEffecting - } - else - { - $isSideEffecting = $true - } - - $newAction = [ODataUtils.Action] @{ - "Namespace" = $schema.Namespace; - "Verb" = $action.Name; - "IsSideEffecting" = $isSideEffecting; - "IsBindable" = $action.IsBindable; - # we don't care about IsAlwaysBindable, since we populate actions information from $metaData - # so we can't know the state of the entity - } - - # Actions are always SideEffecting, otherwise it's an OData function - if ($newAction.IsSideEffecting -ne $false) - { - foreach ($parameter in $action.Parameter) - { - if ($null -ne $parameter.Nullable) - { - $parameterIsNullable = [System.Convert]::ToBoolean($parameter.Nullable); - } - - $newParameter = [ODataUtils.TypeProperty] @{ - "Name" = $parameter.Name; - "TypeName" = $parameter.Type; - "IsNullable" = $parameterIsNullable - } - - $newAction.Parameters += $newParameter - } - - # IsBindable means it operates on Entity/ies - if ($newAction.IsBindable) - { - $regex = "Collection\((.+)\)" - - if ($newAction.Parameters[0].TypeName -match $regex) - { - # action operating on a collection of entities - $insideTypeName = Convert-ODataTypeToCLRType $Matches[1] - - $newAction.EntitySet = $metaData.EntitySets | Where-Object { ($_.Type.Namespace + "." + $_.Type.Name) -eq $insideTypeName } - $newAction.IsSingleInstance = $false - } - else - { - # actions operating on a single instance - $newAction.EntitySet = $metaData.EntitySets | Where-Object { ($_.Type.Namespace + "." + $_.Type.Name) -eq $newAction.Parameters[0].TypeName } - - $newAction.IsSingleInstance = $true - } - } - - $metaData.Actions += $newAction - } - } - } - } - - $metaData -} - -######################################################### -# VerifyMetaData is a helper function used to validate -# the processed metadata to make sure client side proxy -# can be created for the supplied metadata. -######################################################### -function VerifyMetaData -{ - param - ( - [string] $metaDataUri, - [ODataUtils.Metadata] $metaData, - [boolean] $allowClobber, - [System.Management.Automation.PSCmdlet] $callerPSCmdlet, - [string] $progressBarStatus, - [string] $cmdletAdapter, - [Hashtable] $customData, - [Hashtable] $resourceNameMapping - ) - - # $metaDataUri & $cmdletAdapter is already validated at the cmdlet layer. - if($null -eq $metaData) { throw ($LocalizedData.ArguementNullError -f "metadata", "VerifyMetaData") } - if($null -eq $callerPSCmdlet) { throw ($LocalizedData.ArguementNullError -f "PSCmdlet", "VerifyMetaData") } - if($null -eq $progressBarStatus) { throw ($LocalizedData.ArguementNullError -f "ProgressBarStatus", "VerifyMetaData") } - - Write-Verbose $LocalizedData.VerboseVerifyingMetadata - - if ($metadata.EntitySets.Count -le 0) - { - $errorMessage = ($LocalizedData.NoEntitySets -f $metaDataUri) - $exception = [System.InvalidOperationException]::new($errorMessage) - $errorRecord = CreateErrorRecordHelper "ODataEndpointProxyInvalidMetaDataUri" $null ([System.Management.Automation.ErrorCategory]::InvalidArgument) $exception $metaDataUri - $callerPSCmdlet.ThrowTerminatingError($errorRecord) - } - - if ($metadata.EntityTypes.Count -le 0) - { - $errorMessage = ($LocalizedData.NoEntityTypes -f $metaDataUri) - $exception = [System.InvalidOperationException]::new($errorMessage) - $errorRecord = CreateErrorRecordHelper "ODataEndpointProxyInvalidMetaDataUri" $null ([System.Management.Automation.ErrorCategory]::InvalidArgument) $exception $metaDataUri - $callerPSCmdlet.ThrowTerminatingError($errorRecord) - } - - # All the generated proxy cmdlets would have the following parameters added. - # The ODataAdapter has the default implementation on how to handle the - # scenario when these parameters are used during proxy invocations. - # The default implementation can be overridden using adapter derivation model. - $reservedProperties = @("Filter", "IncludeTotalResponseCount", "OrderBy", "Select", "Skip", "Top", "ConnectionUri", "CertificateThumbprint", "Credential") - $validEntitySets = @() - $sessionCommands = Get-Command -All - - foreach ($entitySet in $metaData.EntitySets) - { - if ($null -eq $entitySet.Type) - { - $errorMessage = ($LocalizedData.EntitySetUndefinedType -f $metaDataUri, $entitySet.Name) - $exception = [System.InvalidOperationException]::new($errorMessage) - $errorRecord = CreateErrorRecordHelper "ODataEndpointProxyInvalidMetaDataUri" $null ([System.Management.Automation.ErrorCategory]::InvalidArgument) $exception $metaDataUri - $callerPSCmdlet.ThrowTerminatingError($errorRecord) - } - - if ($cmdletAdapter -eq "NetworkControllerAdapter" -And $customData -And $customData.Contains($entitySet.Name) -eq $false) - { - continue - } - - $hasConflictingProperty = $false - $hasConflictingCommand = $false - - $entityAndNavigationProperties = (GetAllProperties $entitySet.Type) + (GetAllProperties $entitySet.Type -IncludeOnlyNavigationProperties) - foreach($entityProperty in $entityAndNavigationProperties) - { - if($reservedProperties.Contains($entityProperty.Name)) - { - $hasConflictingProperty = $true - if(!$allowClobber) - { - # Write Error message and skip current Entity Set. - $errorMessage = ($LocalizedData.SkipEntitySetProxyCreation -f $entitySet.Name, $entitySet.Type.Name, $entityProperty.Name) - $exception = [System.InvalidOperationException]::new($errorMessage) - $errorRecord = CreateErrorRecordHelper "ODataEndpointDefaultPropertyCollision" $null ([System.Management.Automation.ErrorCategory]::InvalidOperation) $exception $metaDataUri - $callerPSCmdlet.WriteError($errorRecord) - } - else - { - $warningMessage = ($LocalizedData.EntitySetProxyCreationWithWarning -f $entitySet.Name, $entityProperty.Name, $entitySet.Type.Name) - $callerPSCmdlet.WriteWarning($warningMessage) - } - } - } - - foreach($currentCommand in $sessionCommands) - { - # The generated command Noun can be set using ResourceNameMapping - $generatedCommandName = $entitySet.Name - if ($resourceNameMapping -And $resourceNameMapping.Contains($entitySet.Name)) { - $generatedCommandName = $resourceNameMapping[$entitySet.Name] - } - - if(($null -ne $currentCommand.Noun -and $currentCommand.Noun -eq $generatedCommandName) -and - ($currentCommand.Verb -eq "Get" -or - $currentCommand.Verb -eq "Set" -or - $currentCommand.Verb -eq "New" -or - $currentCommand.Verb -eq "Remove")) - { - $hasConflictingCommand = $true - VerifyMetadataHelper $LocalizedData.SkipEntitySetConflictCommandCreation ` - $LocalizedData.EntitySetConflictCommandCreationWithWarning ` - $entitySet.Name $currentCommand.Name $metaDataUri $allowClobber $callerPSCmdlet - } - } - - foreach($currentAction in $metaData.Actions) - { - $actionCommand = "Invoke-" + "$($entitySet.Name)$($currentAction.Verb)" - - foreach($currentCommand in $sessionCommands) - { - if($actionCommand -eq $currentCommand.Name) - { - $hasConflictingCommand = $true - VerifyMetadataHelper $LocalizedData.SkipEntitySetConflictCommandCreation ` - $LocalizedData.EntitySetConflictCommandCreationWithWarning $entitySet.Name ` - $currentCommand.Name $metaDataUri $allowClobber $callerPSCmdlet - } - } - } - - if(!($hasConflictingProperty -or $hasConflictingCommand)-or $allowClobber) - { - $validEntitySets += $entitySet - } - } - - if ($cmdletAdapter -ne "NetworkControllerAdapter") { - - $metaData.EntitySets = $validEntitySets - - $validServiceActions = @() - $hasConflictingServiceActionCommand = $true - foreach($currentAction in $metaData.Actions) - { - $serviceActionCommand = "Invoke-" + "$($currentAction.Verb)" - - foreach($currentCommand in $sessionCommands) - { - if($serviceActionCommand -eq $currentCommand.Name) - { - $hasConflictingServiceActionCommand = $true - VerifyMetadataHelper $LocalizedData.SkipConflictServiceActionCommandCreation ` - $LocalizedData.ConflictServiceActionCommandCreationWithWarning $entitySet.Name ` - $currentCommand.Name $metaDataUri $allowClobber $callerPSCmdlet - } - } - - if(!$hasConflictingServiceActionCommand -or $allowClobber) - { - $validServiceActions += $currentAction - } - } - - $metaData.Actions = $validServiceActions - } - - # Update Progress bar. - ProgressBarHelper "Export-ODataEndpointProxy" $progressBarStatus 5 20 1 1 -} - -######################################################### -# GenerateClientSideProxyModule is a helper function used -# to generate a PowerShell module that serves as a client -# side proxy for interacting with the server side -# OData endpoint. The proxy module contains proxy cmdlets -# implemented in CDXML modules and they are exposed -# through module manifest as nested modules. -# One CDXML module is created for each EntitySet -# described in the metadata. Each CDXML module contains -# CRUD & Service Action specific proxy cmdlets targeting -# the underlying EntityType. There is 1:M mapping between -# EntitySet & its underlying EntityTypes (i.e., all -# entities with in the single EntitySet will be of the -# same EntityType but there can be multiple entities -# of the same type with in an EntitySet). -######################################################### -function GenerateClientSideProxyModule -{ - param - ( - [ODataUtils.Metadata] $metaData, - [string] $metaDataUri, - [string] $uri, - [string] $outputModule, - [string] $createRequestMethod, - [string] $updateRequestMethod, - [string] $cmdletAdapter, - [Hashtable] $resourceNameMapping, - [Hashtable] $customData, - [string] $progressBarStatus, - [System.Management.Automation.PSCmdlet] $callerPSCmdlet - ) - - # $uri, $outputModule, $metaDataUri, $createRequestMethod, $updateRequestMethod, & $cmdletAdapter is already validated at the cmdlet layer. - if($null -eq $metaData) { throw ($LocalizedData.ArguementNullError -f "metadata", "GenerateClientSideProxyModule") } - if($null -eq $callerPSCmdlet) { throw ($LocalizedData.ArguementNullError -f "PSCmdlet", "GenerateClientSideProxyModule") } - if($null -eq $progressBarStatus) { throw ($LocalizedData.ArguementNullError -f "ProgressBarStatus", "GenerateClientSideProxyModule") } - - # This function performs the following set of tasks - # while creating the client side proxy module: - # 1. If the server side endpoint exposes complex types, - # the client side proxy complex types are created - # as C# class in ComplexTypeDefinitions.psm1 - # 2. Creates proxy cmdlets for CRUD operations. - # 3. Creates proxy cmdlets for Service action operations. - # 4. Creates module manifest. - - Write-Verbose ($LocalizedData.VerboseSavingModule -f $outputModule) - - $typeDefinitionFileName = "ComplexTypeDefinitions.psm1" - $complexTypeMapping = GenerateComplexTypeDefinition $metaData $metaDataUri $outputModule $typeDefinitionFileName $cmdletAdapter $callerPSCmdlet - - ProgressBarHelper "Export-ODataEndpointProxy" $progressBarStatus 20 20 1 1 - - $complexTypeFileDefinitionPath = Join-Path -Path $outputModule -ChildPath $typeDefinitionFileName - - if(Test-Path -Path $complexTypeFileDefinitionPath) - { - $proxyFile = New-Object -TypeName System.IO.FileInfo -ArgumentList $complexTypeFileDefinitionPath | Get-Item - if($null -ne $callerPSCmdlet) - { - $callerPSCmdlet.WriteObject($proxyFile) - } - } - - $currentEntryCount = 0 - foreach ($entitySet in $metaData.EntitySets) - { - $currentEntryCount += 1 - if ($cmdletAdapter -eq "NetworkControllerAdapter" -And $customData -And $customData.Contains($entitySet.Name) -eq $false) - { - ProgressBarHelper "Export-ODataEndpointProxy" $progressBarStatus 40 20 $metaData.EntitySets.Count $currentEntryCount - continue - } - - GenerateCRUDProxyCmdlet $entitySet $metaData $uri $outputModule $createRequestMethod $updateRequestMethod $cmdletAdapter $resourceNameMapping $customData $complexTypeMapping "Export-ODataEndpointProxy" $progressBarStatus 40 20 $metaData.EntitySets.Count $currentEntryCount $callerPSCmdlet - } - - GenerateServiceActionProxyCmdlet $metaData $uri "$outputModule\ServiceActions.cdxml" $complexTypeMapping $progressBarStatus $callerPSCmdlet - - $moduleDirInfo = [System.IO.DirectoryInfo]::new($outputModule) - $moduleManifestName = $moduleDirInfo.Name + ".psd1" - GenerateModuleManifest $metaData $outputModule\$moduleManifestName @($typeDefinitionFileName, 'ServiceActions.cdxml') $resourceNameMapping $progressBarStatus $callerPSCmdlet -} - -######################################################### -# GenerateCRUDProxyCmdlet is a helper function used -# to generate Get, Set, New & Remove proxy cmdlet. -# The proxy cmdlet is generated in the CDXML -# compliant format. -######################################################### -function GenerateCRUDProxyCmdlet -{ - param - ( - [ODataUtils.EntitySet] $entitySet, - [ODataUtils.Metadata] $metaData, - [string] $uri, - [string] $outputModule, - [string] $createRequestMethod, - [string] $UpdateRequestMethod, - [string] $cmdletAdapter, - [Hashtable] $resourceNameMapping, - [Hashtable] $customData, - [Hashtable] $complexTypeMapping, - [string] $progressBarActivityName, - [string] $progressBarStatus, - [double] $previousSegmentWeight, - [double] $currentSegmentWeight, - [int] $totalNumberofEntries, - [int] $currentEntryCount, - [System.Management.Automation.PSCmdlet] $callerPSCmdlet - ) - - # $uri, $outputModule, $metaDataUri, $createRequestMethod, $updateRequestMethod, & $cmdletAdapter is already validated at the cmdlet layer. - if($null -eq $entitySet) { throw ($LocalizedData.ArguementNullError -f "EntitySet", "GenerateClientSideProxyModule") } - if($null -eq $metaData) { throw ($LocalizedData.ArguementNullError -f "metadata", "GenerateClientSideProxyModule") } - if($null -eq $callerPSCmdlet) { throw ($LocalizedData.ArguementNullError -f "PSCmdlet", "GenerateClientSideProxyModule") } - if($null -eq $progressBarStatus) { throw ($LocalizedData.ArguementNullError -f "ProgressBarStatus", "GenerateClientSideProxyModule") } - - $entitySetName = $entitySet.Name - if(($null -ne $resourceNameMapping) -and - $resourceNameMapping.ContainsKey($entitySetName)) - { - $entitySetName = $resourceNameMapping[$entitySetName] - } - else - { - $entitySetName = $entitySet.Type.Name - } - - $Path = "$OutputModule\$entitySetName.cdxml" - - $xmlWriter = New-Object System.XMl.XmlTextWriter($Path,$Null) - - if ($null -eq $xmlWriter) - { - throw ($LocalizedData.XmlWriterInitializationError -f $entitySet.Name) - } - - $xmlWriter = SaveCDXMLHeader $xmlWriter $uri $entitySet.Name $entitySetName $cmdletAdapter - - # Get the keys depending on whether the url contains variables or not - if ($CmdletAdapter -ne "NetworkControllerAdapter") - { - $keys = (GetAllProperties $entitySet.Type) | Where-Object { $_.IsKey } - } - else - { - $name = $entitySet.Name - $keys = GetKeys $entitySet $customData.$name 'Get' - } - - $navigationProperties = GetAllProperties $entitySet.Type -IncludeOnlyNavigationProperties - - GenerateGetProxyCmdlet $xmlWriter $metaData $keys $navigationProperties $cmdletAdapter $complexTypeMapping - - $nonKeyProperties = (GetAllProperties $entitySet.Type) | Where-Object { -not $_.isKey } - $nullableProperties = $nonKeyProperties | Where-Object { $_.isNullable } - $nonNullableProperties = $nonKeyProperties | Where-Object { -not $_.isNullable } - - $xmlWriter.WriteStartElement('StaticCmdlets') - - $keyProperties = $keys - - # Do operations specifically needed for NetworkController cmdlets - if ($CmdletAdapter -eq "NetworkControllerAdapter") - { - $keyProperties = GetKeys $entitySet $customData.$name 'New' - $additionalProperties = GetNetworkControllerAdditionalProperties $navigationProperties $metaData - $nullableProperties = UpdateNetworkControllerSpecificProperties $nullableProperties $additionalProperties $keyProperties $true - $nonNullableProperties = UpdateNetworkControllerSpecificProperties $nonNullableProperties $additionalProperties $keyProperties $false - } - - GenerateNewProxyCmdlet $xmlWriter $metaData $keyProperties $nonNullableProperties $nullableProperties $navigationProperties $cmdletAdapter $complexTypeMapping - - if ($CmdletAdapter -ne "NetworkControllerAdapter") - { - GenerateSetProxyCmdlet $xmlWriter $keyProperties $nonKeyProperties $complexTypeMapping - } - - if ($CmdletAdapter -eq "NetworkControllerAdapter") - { - $keyProperties = GetKeys $entitySet $customData.$name 'Remove' - } - - GenerateRemoveProxyCmdlet $xmlWriter $metaData $keyProperties $navigationProperties $cmdletAdapter $complexTypeMapping - - $entityActions = $metaData.Actions | Where-Object { ($_.EntitySet.Namespace -eq $entitySet.Namespace) -and ($_.EntitySet.Name -eq $entitySet.Name) } - - if ($entityActions.Length -gt 0) - { - foreach($action in $entityActions) - { - $xmlWriter = GenerateActionProxyCmdlet $xmlWriter $metaData $action $entitySet.Name $true $keys $complexTypeMapping - } - } - - $xmlWriter.WriteEndElement() - - $xmlWriter.WriteStartElement('CmdletAdapterPrivateData') - - $xmlWriter.WriteStartElement('Data') - $xmlWriter.WriteAttributeString('Name', 'EntityTypeName') - $xmlWriter.WriteString("$($entitySet.Type.Namespace).$($entitySet.Type.Name)") - $xmlWriter.WriteEndElement() - $xmlWriter.WriteStartElement('Data') - $xmlWriter.WriteAttributeString('Name', 'EntitySetName') - $xmlWriter.WriteString("$($entitySet.Namespace).$($entitySet.Name)") - $xmlWriter.WriteEndElement() - - # Add the customUri to privateData - if ($CmdletAdapter -eq "NetworkControllerAdapter") - { - $xmlWriter.WriteStartElement('Data') - $xmlWriter.WriteAttributeString('Name', "CustomUriSuffix") - $xmlWriter.WriteString($CustomData.$name) - $xmlWriter.WriteEndElement() - } - - # Add CreateRequestMethod and UpdateRequestMethod to privateData - $xmlWriter.WriteStartElement('Data') - $xmlWriter.WriteAttributeString('Name', 'CreateRequestMethod') - $xmlWriter.WriteString("$CreateRequestMethod") - $xmlWriter.WriteEndElement() - - $xmlWriter.WriteStartElement('Data') - $xmlWriter.WriteAttributeString('Name', 'UpdateRequestMethod') - $xmlWriter.WriteString("$UpdateRequestMethod") - $xmlWriter.WriteEndElement() - - $xmlWriter.WriteEndElement() - - SaveCDXMLFooter $xmlWriter - - ProcessStreamHelper ($LocalizedData.VerboseSavedCDXML -f $($entitySetName), $Path) $progressBarActivityName $progressBarStatus $previousSegmentWeight $currentSegmentWeight $totalNumberofEntries $currentEntryCount $Path $callerPSCmdlet -} - -######################################################### -# GenerateGetProxyCmdlet is a helper function used -# to generate Get-* proxy cmdlet. The proxy cmdlet is -# generated in the CDXML compliant format. -######################################################### -function GenerateGetProxyCmdlet -{ - param - ( - [System.XMl.XmlTextWriter] $xmlWriter, - [ODataUtils.Metadata] $metaData, - [object[]] $keys, - [object[]] $navigationProperties, - [string] $cmdletAdapter, - [Hashtable] $complexTypeMapping - ) - - # $cmdletAdapter is already validated at the cmdlet layer. - if($null -eq $xmlWriter) { throw ($LocalizedData.ArguementNullError -f "xmlWriter", "GenerateGetProxyCmdlet") } - if($null -eq $metaData) { throw ($LocalizedData.ArguementNullError -f "metadata", "GenerateGetProxyCmdlet") } - - $xmlWriter.WriteStartElement('InstanceCmdlets') - $xmlWriter.WriteStartElement('GetCmdletParameters') - $xmlWriter.WriteAttributeString('DefaultCmdletParameterSet', 'Default') - - # adding key parameters and association parameters to QueryableProperties, each in a different parameter set - # to be used by GET cmdlet - if (($null -ne $keys -and $keys.Length -gt 0) -or (($null -ne $navigationProperties -and $navigationProperties.Length -gt 0) -and $cmdletAdapter -ne "NetworkControllerAdapter")) - { - $xmlWriter.WriteStartElement('QueryableProperties') - $position = 0 - - $keys | Where-Object { $null -ne $_ } | ForEach-Object { - $xmlWriter.WriteStartElement('Property') - $xmlWriter.WriteAttributeString('PropertyName', $_.Name) - - $xmlWriter.WriteStartElement('Type') - $PSTypeName = Convert-ODataTypeToCLRType $_.TypeName $complexTypeMapping - $xmlWriter.WriteAttributeString('PSType', $PSTypeName) - $xmlWriter.WriteEndElement() - - $xmlWriter.WriteStartElement('RegularQuery') - $xmlWriter.WriteStartElement('CmdletParameterMetadata') - $xmlWriter.WriteAttributeString('PSName', $_.Name) - $xmlWriter.WriteAttributeString('CmdletParameterSets', 'Default') - $xmlWriter.WriteAttributeString('IsMandatory', $_.IsMandatory.ToString().ToLower()) - $xmlWriter.WriteAttributeString('Position', $position) - if($_.IsMandatory) - { - $xmlWriter.WriteAttributeString('ValueFromPipelineByPropertyName', 'true') - } - $xmlWriter.WriteEndElement() - $xmlWriter.WriteEndElement() - $xmlWriter.WriteEndElement() - - $position++ - } - - # This behaviour is different for NetworkController specific cmdlets. - if ($CmdletAdapter -ne "NetworkControllerAdapter") - { - $navigationProperties | Where-Object { $null -ne $_ } | ForEach-Object { - $associatedType = GetAssociatedType $metaData $_ - $associatedEntitySet = GetEntitySetForEntityType $metaData $associatedType - $nvgProperty = $_ - - (GetAllProperties $associatedType) | Where-Object { $_.IsKey } | ForEach-Object { - $xmlWriter.WriteStartElement('Property') - $xmlWriter.WriteAttributeString('PropertyName', $associatedEntitySet.Name + ':' + $_.Name + ':Key') - - $xmlWriter.WriteStartElement('Type') - $PSTypeName = Convert-ODataTypeToCLRType $_.TypeName $complexTypeMapping - $xmlWriter.WriteAttributeString('PSType', $PSTypeName) - $xmlWriter.WriteEndElement() - - $xmlWriter.WriteStartElement('RegularQuery') - $xmlWriter.WriteStartElement('CmdletParameterMetadata') - $xmlWriter.WriteAttributeString('PSName', 'Associated' + $nvgProperty.Name + $_.Name) - $xmlWriter.WriteAttributeString('CmdletParameterSets', $nvgProperty.AssociationName) - $xmlWriter.WriteAttributeString('IsMandatory', 'true') - $xmlWriter.WriteAttributeString('ValueFromPipelineByPropertyName', 'true') - $xmlWriter.WriteEndElement() - $xmlWriter.WriteEndElement() - $xmlWriter.WriteEndElement() - } - } - - - # Add Query Parameters (i.e., Top, Skip, OrderBy, Filter) to the generated Get-* cmdlets. - $queryParameters = - @{ - "Filter" = "Edm.String"; - "IncludeTotalResponseCount" = "switch"; - "OrderBy" = "Edm.String"; - "Select" = "Edm.String"; - "Skip" = "Edm.Int32"; - "Top" = "Edm.Int32"; - } - - foreach($currentQueryParameter in $queryParameters.Keys) - { - $xmlWriter.WriteStartElement('Property') - $xmlWriter.WriteAttributeString('PropertyName', "QueryOption:" + $currentQueryParameter) - $xmlWriter.WriteStartElement('Type') - $PSTypeName = Convert-ODataTypeToCLRType $queryParameters[$currentQueryParameter] - $xmlWriter.WriteAttributeString('PSType', $PSTypeName) - $xmlWriter.WriteEndElement() - $xmlWriter.WriteStartElement('RegularQuery') - $xmlWriter.WriteStartElement('CmdletParameterMetadata') - $xmlWriter.WriteAttributeString('PSName', $currentQueryParameter) - - if($queryParameters[$currentQueryParameter] -eq "Edm.String") - { - $xmlWriter.WriteStartElement('ValidateNotNullOrEmpty') - $xmlWriter.WriteEndElement() - } - - if($queryParameters[$currentQueryParameter] -eq "Edm.Int32") - { - $minValue = 1 - # For Skip Query parameter we want to support 0 as the - # minimum skip value in order to support client side paging. - if($currentQueryParameter -eq 'Skip') - { - $minValue = 0 - } - $xmlWriter.WriteStartElement('ValidateRange') - $xmlWriter.WriteAttributeString('Min', $minValue) - $xmlWriter.WriteAttributeString('Max', [int]::MaxValue) - $xmlWriter.WriteEndElement() - } - - $xmlWriter.WriteEndElement() - $xmlWriter.WriteEndElement() - $xmlWriter.WriteEndElement() - } - } - - $xmlWriter.WriteEndElement() - } - - $xmlWriter.WriteEndElement() - - $xmlWriter.WriteStartElement('GetCmdlet') - $xmlWriter.WriteStartElement('CmdletMetadata') - $xmlWriter.WriteAttributeString('Verb', 'Get') - $xmlWriter.WriteEndElement() - $xmlWriter.WriteEndElement() - - $xmlWriter.WriteEndElement() -} - -######################################################### -# GenerateNewProxyCmdlet is a helper function used -# to generate New-* proxy cmdlet. The proxy cmdlet is -# generated in the CDXML compliant format. -######################################################### -function GenerateNewProxyCmdlet -{ - param - ( - [System.XMl.XmlTextWriter] $xmlWriter, - [ODataUtils.Metadata] $metaData, - [object[]] $keyProperties, - [object[]] $nonNullableProperties, - [object[]] $nullableProperties, - [object[]] $navigationProperties, - [string] $cmdletAdapter, - [Hashtable] $complexTypeMapping - ) - - # $cmdletAdapter is already validated at the cmdlet layer. - if($null -eq $xmlWriter) { throw ($LocalizedData.ArguementNullError -f "xmlWriter", "GenerateNewProxyCmdlet") } - if($null -eq $metaData) { throw ($LocalizedData.ArguementNullError -f "metadata", "GenerateNewProxyCmdlet") } - - $xmlWriter.WriteStartElement('Cmdlet') - $xmlWriter.WriteStartElement('CmdletMetadata') - $xmlWriter.WriteAttributeString('Verb', 'New') - $xmlWriter.WriteAttributeString('DefaultCmdletParameterSet', 'Default') - $xmlWriter.WriteAttributeString('ConfirmImpact', 'Medium') - $xmlWriter.WriteEndElement() - - $xmlWriter.WriteStartElement('Method') - $xmlWriter.WriteAttributeString('MethodName', 'Create') - $xmlWriter.WriteAttributeString('CmdletParameterSet', 'Default') - - AddParametersNode $xmlWriter $keyProperties $nonNullableProperties $nullableProperties $null $true $true $complexTypeMapping - $xmlWriter.WriteEndElement() - - # This behaviour is different for NetworkControllerCmdlets - if ($CmdletAdapter -ne "NetworkControllerAdapter") - { - $navigationProperties | Where-Object { $null -ne $_ } | ForEach-Object { - $associatedType = GetAssociatedType $metaData $_ - $associatedEntitySet = GetEntitySetForEntityType $metaData $associatedType - - $xmlWriter.WriteStartElement('Method') - $xmlWriter.WriteAttributeString('MethodName', "Association:Create:$($associatedEntitySet.Name)") - $xmlWriter.WriteAttributeString('CmdletParameterSet', $_.Name) - - $associatedKeys = ((GetAllProperties $associatedType) | Where-Object { $_.isKey }) - - AddParametersNode $xmlWriter $associatedKeys $keyProperties $null "Associated$($_.Name)" $true $true $complexTypeMapping - $xmlWriter.WriteEndElement() - } - } - - $xmlWriter.WriteEndElement() -} - -######################################################### -# GenerateRemoveProxyCmdlet is a helper function used -# to generate Remove-* proxy cmdlet. The proxy cmdlet is -# generated in the CDXML compliant format. -######################################################### -function GenerateRemoveProxyCmdlet -{ - param - ( - - [System.XMl.XmlTextWriter] $xmlWriter, - [ODataUtils.Metadata] $metaData, - [object[]] $keyProperties, - [object[]] $navigationProperties, - [string] $cmdletAdapter, - [Hashtable] $complexTypeMapping - ) - - # $metaData, $cmdletAdapter & $cmdletAdapter are already validated at the cmdlet layer. - if($null -eq $xmlWriter) { throw ($LocalizedData.ArguementNullError -f "xmlWriter", "GenerateRemoveProxyCmdlet") } - if($null -eq $metaData) { throw ($LocalizedData.ArguementNullError -f "metadata", "GenerateRemoveProxyCmdlet") } - - $xmlWriter.WriteStartElement('Cmdlet') - $xmlWriter.WriteStartElement('CmdletMetadata') - $xmlWriter.WriteAttributeString('Verb', 'Remove') - $xmlWriter.WriteAttributeString('DefaultCmdletParameterSet', 'Default') - $xmlWriter.WriteAttributeString('ConfirmImpact', 'Medium') - $xmlWriter.WriteEndElement() - - $xmlWriter.WriteStartElement('Method') - $xmlWriter.WriteAttributeString('MethodName', 'Delete') - $xmlWriter.WriteAttributeString('CmdletParameterSet', 'Default') - - # This behaviour is different for NetworkControllerCmdlets - if ($CmdletAdapter -eq "NetworkControllerAdapter") - { - # Add etag for NetworkControllerCmdlets - $otherProperties = @([ODataUtils.TypeProperty] @{ - "Name" = "Etag"; - "TypeName" = "Edm.String"; - "IsNullable" = $true; - }) - - AddParametersNode $xmlWriter $keyProperties $null $otherProperties $null $true $true $complexTypeMapping - } - else - { - AddParametersNode $xmlWriter $keyProperties $null $null $null $true $true $complexTypeMapping - } - - $xmlWriter.WriteEndElement() - - # This behaviour is different for NetworkControllerCmdlets - if ($CmdletAdapter -ne "NetworkControllerAdapter") - { - $navigationProperties | Where-Object { $null -ne $_ } | ForEach-Object { - - $associatedType = GetAssociatedType $metaData $_ - $associatedEntitySet = GetEntitySetForEntityType $metaData $associatedType - - $xmlWriter.WriteStartElement('Method') - $xmlWriter.WriteAttributeString('MethodName', "Association:Delete:$($associatedEntitySet.Name)") - $xmlWriter.WriteAttributeString('CmdletParameterSet', $_.Name) - - $associatedType = GetAssociatedType $metaData $_ - $associatedKeys = ((GetAllProperties $associatedType) | Where-Object { $_.isKey }) - - AddParametersNode $xmlWriter $associatedKeys $keyProperties $null "Associated$($_.Name)" $true $true $complexTypeMapping - $xmlWriter.WriteEndElement() - } - } - $xmlWriter.WriteEndElement() -} - -######################################################### -# GenerateActionProxyCmdlet is a helper function used -# to generate Invoke-* proxy cmdlet. These proxy cmdlets -# support Instance/Service level actions. They are -# generated in the CDXML compliant format. -######################################################### -function GenerateActionProxyCmdlet -{ - param - ( - [System.Xml.XmlWriter] $xmlWriter, - [ODataUtils.Metadata] $metaData, - [ODataUtils.Action] $action, - [string] $noun, - [bool] $isInstanceAction, - [ODataUtils.TypeProperty] $keys, - [Hashtable] $complexTypeMapping - ) - - # $metaData is already validated at the cmdlet layer. - if($null -eq $xmlWriter) { throw ($LocalizedData.ArguementNullError -f "xmlWriter", "GenerateActionProxyCmdlet") } - if($null -eq $metaData) { throw ($LocalizedData.ArguementNullError -f "metadata", "GenerateActionProxyCmdlet") } - if($null -eq $action) { throw ($LocalizedData.ArguementNullError -f "Action", "GenerateActionProxyCmdlet") } - if($null -eq $noun) { throw ($LocalizedData.ArguementNullError -f "Noun", "GenerateActionProxyCmdlet") } - - $xmlWriter.WriteStartElement('Cmdlet') - - $xmlWriter.WriteStartElement('CmdletMetadata') - $xmlWriter.WriteAttributeString('Verb', 'Invoke') - $xmlWriter.WriteAttributeString('Noun', "$($noun)$($action.Verb)") - $xmlWriter.WriteAttributeString('ConfirmImpact', 'Medium') - $xmlWriter.WriteEndElement() - - $xmlWriter.WriteStartElement('Method') - $xmlWriter.WriteAttributeString('MethodName', "Action:$($action.Verb):$($action.EntitySet.Name)") - - $xmlWriter.WriteStartElement('Parameters') - - $keys | Where-Object { $null -ne $_ } | ForEach-Object { - $xmlWriter.WriteStartElement('Parameter') - $xmlWriter.WriteAttributeString('ParameterName', $_.Name + ':Key') - - $xmlWriter.WriteStartElement('Type') - $PSTypeName = Convert-ODataTypeToCLRType $_.TypeName $complexTypeMapping - $xmlWriter.WriteAttributeString('PSType', $PSTypeName) - $xmlWriter.WriteEndElement() - - $xmlWriter.WriteStartElement('CmdletParameterMetadata') - $xmlWriter.WriteAttributeString('PSName', $_.Name) - $xmlWriter.WriteAttributeString('IsMandatory', 'true') - $xmlWriter.WriteAttributeString('ValueFromPipelineByPropertyName', 'true') - $xmlWriter.WriteEndElement() - $xmlWriter.WriteEndElement() - } - - $i = -1 - foreach ($parameter in $action.Parameters) - { - $i++ - - # for Instance actions, first parameter is Entity Set which we refer to using keys - if ($isInstanceAction -and ($i -eq 0)) - { - continue - } - - $xmlWriter.WriteStartElement('Parameter') - $xmlWriter.WriteAttributeString('ParameterName', $parameter.Name) - - $xmlWriter.WriteStartElement('Type') - $PSTypeName = Convert-ODataTypeToCLRType $parameter.TypeName - $xmlWriter.WriteAttributeString('PSType', $PSTypeName) - $xmlWriter.WriteEndElement() - - $xmlWriter.WriteStartElement('CmdletParameterMetadata') - $xmlWriter.WriteAttributeString('PSName', $parameter.Name) - if (-not $parameter.IsNullable) - { - $xmlWriter.WriteAttributeString('IsMandatory', 'true') - $xmlWriter.WriteAttributeString('ValueFromPipelineByPropertyName', 'true') - } - $xmlWriter.WriteEndElement() - $xmlWriter.WriteEndElement() - } - - # Add -Force parameter to Service Action cmdlets. - AddParametersNode $xmlWriter $null $null $null $null $true $false $complexTypeMapping - $xmlWriter.WriteEndElement() - $xmlWriter.WriteEndElement() - - $xmlWriter.WriteEndElement() - - $xmlWriter -} - -######################################################### -# GenerateServiceActionProxyCmdlet is a helper function -# used to generate Invoke-* proxy cmdlet. These proxy -# cmdlets support all Service-level actions. They are -# generated in the CDXML compliant format. -######################################################### -function GenerateServiceActionProxyCmdlet -{ - param - ( - [Parameter(Mandatory=$true)] - [ODataUtils.Metadata] $metaData, - [Parameter(Mandatory=$true)] - [string] $uri, - [Parameter(Mandatory=$true)] - [string] $path, - [Hashtable] $complexTypeMapping, - [string] $progressBarStatus, - [System.Management.Automation.PSCmdlet] $callerPSCmdlet - ) - - # $uri is already validated at the cmdlet layer. - if($null -eq $metaData) { throw ($LocalizedData.ArguementNullError -f "metadata", "GenerateServiceActionProxyCmdlet") } - - $xmlWriter = New-Object System.XMl.XmlTextWriter($path,$Null) - - if ($null -eq $xmlWriter) - { - throw $LocalizedData.XmlWriterInitializationError -f "ServiceActions" - } - - $xmlWriter = SaveCDXMLHeader $xmlWriter $uri 'ServiceActions' 'ServiceActions' - - $actions = $metaData.Actions | Where-Object { $null -eq $_.EntitySet } - - if ($actions.Length -gt 0) - { - $xmlWriter.WriteStartElement('StaticCmdlets') - - foreach ($action in $actions) - { - $xmlWriter = GenerateActionProxyCmdlet $xmlWriter $metaData $action '' $false $null $complexTypeMapping - } - - $xmlWriter.WriteEndElement() - } - - $xmlWriter.WriteStartElement('CmdletAdapterPrivateData') - $xmlWriter.WriteStartElement('Data') - $xmlWriter.WriteAttributeString('Name', 'Namespace') - $xmlWriter.WriteString("$($EntitySet.Namespace)") - $xmlWriter.WriteEndElement() - $xmlWriter.WriteEndElement() - - SaveCDXMLFooter $xmlWriter - - ProcessStreamHelper ($LocalizedData.VerboseSavedServiceActions -f $path) "Export-ODataEndpointProxy" $progressBarStatus 60 20 1 1 $path $callerPSCmdlet -} - -######################################################### -# GenerateModuleManifest is a helper function used -# to generate a wrapper module manifest file. The -# generated module manifest is persisted to the disk at -# the specified OutPutModule path. When the module -# manifest is imported, the following commands will -# be imported: -# 1. Get, Set, New & Remove proxy cmdlets. -# 2. If the server side Odata endpoint exposes complex -# types, then the corresponding client side proxy -# complex types imported. -# 3. Service Action proxy cmdlets. -######################################################### -function GenerateModuleManifest -{ - param - ( - [ODataUtils.Metadata] $metaData, - [String] $modulePath, - [string[]] $additionalModules, - [Hashtable] $resourceNameMapping, - [string] $progressBarStatus, - [System.Management.Automation.PSCmdlet] $callerPSCmdlet - ) - - if($null -eq $metaData) { throw ($LocalizedData.ArguementNullError -f "metadata", "GenerateModuleManifest") } - if($null -eq $modulePath) { throw ($LocalizedData.ArguementNullError -f "ModulePath", "GenerateModuleManifest") } - if($null -eq $progressBarStatus) { throw ($LocalizedData.ArguementNullError -f "ProgressBarStatus", "GenerateModuleManifest") } - - $NestedModules = @() - foreach ($entitySet in $metaData.EntitySets) - { - $entitySetName = $entitySet.Name - if(($null -ne $resourceNameMapping) -and - $resourceNameMapping.ContainsKey($entitySetName)) - { - $entitySetName = $resourceNameMapping[$entitySetName] - } - else - { - $entitySetName = $entitySet.Type.Name - } - - $NestedModules += "$OutputModule\$($entitySetName).cdxml" - } - - New-ModuleManifest -Path $modulePath -NestedModules ($AdditionalModules + $NestedModules) - - ProcessStreamHelper ($LocalizedData.VerboseSavedModuleManifest -f $modulePath) "Export-ODataEndpointProxy" $progressBarStatus 80 20 1 1 $modulePath $callerPSCmdlet -} - -######################################################### -# GetBaseType is a helper function used to fetch the -# base type of the given type. -######################################################### -function GetBaseType -{ - param - ( - [System.Xml.XmlElement] $metadataEntityDefinition, - [ODataUtils.Metadata] $metaData - ) - - if ($null -ne $metadataEntityDefinition -and - $null -ne $metaData -and - $null -ne $metadataEntityDefinition.BaseType) - { - $baseType = $metaData.EntityTypes | Where-Object {$_.Namespace+"."+$_.Name -eq $metadataEntityDefinition.BaseType} - if ($null -eq $baseType) - { - $baseType = $metaData.ComplexTypes | Where-Object {$_.Namespace+"."+$_.Name -eq $metadataEntityDefinition.BaseType} - } - } - - if ($null -ne $baseType) - { - $baseType[0] - } -} - -######################################################### -# AddDerivedTypes is a helper function used to process -# derived types of a newly added type, that were -# previously waiting in the queue. -######################################################### -function AddDerivedTypes -{ - param - ( - [ODataUtils.EntityType] $baseType, - [Hashtable]$entityAndComplexTypesQueue, - [ODataUtils.Metadata] $metaData, - [string] $namespace - ) - - # $metaData is already validated at the cmdlet layer. - if($null -eq $baseType) { throw ($LocalizedData.ArguementNullError -f "BaseType", "AddDerivedTypes") } - if($null -eq $entityAndComplexTypesQueue) { throw ($LocalizedData.ArguementNullError -f "EntityAndComplexTypesQueue", "AddDerivedTypes") } - if($null -eq $namespace) { throw ($LocalizedData.ArguementNullError -f "Namespace", "AddDerivedTypes") } - - $baseTypeFullName = $baseType.Namespace + '.' + $baseType.Name - - if ($entityAndComplexTypesQueue.ContainsKey($baseTypeFullName)) - { - foreach ($type in $entityAndComplexTypesQueue[$baseTypeFullName]) - { - if ($type.type -eq 'EntityType') - { - $newType = ParseMetadataTypeDefinition ($type.value) $baseType $metaData $namespace $true - $metaData.EntityTypes += $newType - } - else - { - $newType = ParseMetadataTypeDefinition ($type.value) $baseType $metaData $namespace $false - $metaData.ComplexTypes += $newType - } - - AddDerivedTypes $newType $entityAndComplexTypesQueue $metaData $namespace - } - } -} - -######################################################### -# ParseMetadataTypeDefinition is a helper function used -# to parse types definitions element of metadata xml. -######################################################### -function ParseMetadataTypeDefinition -{ - param - ( - [Parameter(Mandatory=$true)] - [System.Xml.XmlElement] $metadataEntityDefinition, - [ODataUtils.EntityType] $baseType, - [ODataUtils.Metadata] $metaData, - [string] $namespace, - [bool] $isEntity - ) - - # $metaData is already validated at the cmdlet layer. - if($null -eq $metadataEntityDefinition) { throw ($LocalizedData.ArguementNullError -f "MetadataEntityDefinition", "ParseMetadataTypeDefinition") } - if($null -eq $namespace) { throw ($LocalizedData.ArguementNullError -f "Namespace", "ParseMetadataTypeDefinition") } - - $newEntityType = [ODataUtils.EntityType] @{ - "Namespace" = $namespace; - "Name" = $metadataEntityDefinition.Name; - "IsEntity" = $isEntity; - "BaseType" = $baseType; - } - - # properties defined on EntityType - $newEntityType.EntityProperties = $metadataEntityDefinition.Property | ForEach-Object { - if ($null -ne $_) - { - if ($null -ne $_.Nullable) - { - $newPropertyIsNullable = [System.Convert]::ToBoolean($_.Nullable) - } - else - { - $newPropertyIsNullable = $true - } - - [ODataUtils.TypeProperty] @{ - "Name" = $_.Name; - "TypeName" = $_.Type; - "IsNullable" = $newPropertyIsNullable; - } - } - } - - # navigation properties defined on EntityType - $newEntityType.NavigationProperties = $metadataEntityDefinition.NavigationProperty | ForEach-Object { - if ($null -ne $_) - { - ($AssociationNamespace, $AssociationName) = SplitNamespaceAndName $_.Relationship - [ODataUtils.NavigationProperty] @{ - "Name" = $_.Name; - "FromRole" = $_.FromRole; - "ToRole" = $_.ToRole; - "AssociationNamespace" = $AssociationNamespace; - "AssociationName" = $AssociationName; - } - } - } - - foreach ($entityTypeKey in $metadataEntityDefinition.Key.PropertyRef) - { - ((GetAllProperties $newEntityType) | Where-Object { $_.Name -eq $entityTypeKey.Name }).IsKey = $true - } - - $newEntityType -} - -######################################################### -# GetAllProperties is a helper function used to fetch -# the entity properties or navigation properties of -# the entity type as well as that of complete base -# type hierarchy. -######################################################### -function GetAllProperties -{ - param - ( - [ODataUtils.EntityType] $entityType, - [switch] $IncludeOnlyNavigationProperties - ) - - if($null -eq $entityType) { throw ($LocalizedData.ArguementNullError -f "EntityType", "GetAllProperties") } - - $requestedProperties = @() - - # Populate EntityType property from current EntityType as well - # as from the corresponding base types recursively if - # $IncludeOnlyNavigationProperties switch parameter is used then follow - # the same routine for navigation properties. - $currentEntityType = $entityType - while($null -ne $currentEntityType) - { - if($IncludeOnlyNavigationProperties.IsPresent) - { - $chosenProperties = $currentEntityType.NavigationProperties - } - else - { - $chosenProperties = $currentEntityType.EntityProperties - } - - $requestedProperties += $chosenProperties - $currentEntityType = $currentEntityType.BaseType - } - - return $requestedProperties -} - -######################################################### -# SplitNamespaceAndName is a helper function used -# to split Namespace and actual Name. -# e.g. "a.b.c" is namespace "a.b" and name "c" -######################################################### -function SplitNamespaceAndName -{ - param - ( - [string] $fullyQualifiedName - ) - - if($null -eq $fullyQualifiedName) { throw ($LocalizedData.ArguementNullError -f "FUllyQualifiedName", "SplitNamespaceAndName") } - - $sa = $fullyQualifiedName -split "(.*)\.(.*)" - - if ($sa.Length -gt 1) - { - # return Namespace - $sa[1] - - # return Name - $sa[2] - } - else - { - # return Namespace - "" - - # return Name - $sa[0] - } -} - -######################################################### -# GetEntitySetForEntityType is a helper function used -# to fetch EntitySet for a given EntityType by -# searching the inheritance hierarchy in the -# supplied metadata. -######################################################### -function GetEntitySetForEntityType -{ - param - ( - [ODataUtils.Metadata] $metaData, - [ODataUtils.EntityType] $entityType - ) - - # $metaData is already validated at the cmdlet layer. - if($null -eq $entityType) { throw ($LocalizedData.ArguementNullError -f "EntityType", "GetEntitySetForEntityType") } - - $result = $metaData.EntitySets | Where-Object { ($_.Type.Namespace -eq $entityType.Namespace) -and ($_.Type.Name -eq $entityType.Name) } - - if (($result.Count -eq 0) -and ($null -ne $entityType.BaseType)) - { - GetEntitySetForEntityType $metaData $entityType.BaseType - } - elseif ($result.Count -gt 1) - { - throw ($LocalizedData.WrongCountEntitySet -f (($entityType.Namespace + "." + $entityType.Name), $result.Count)) - } - - $result -} - -######################################################### -# ProcessStreamHelper is a helper function that performs -# the following utility tasks: -# 1. Writes verbose messages to the stream. -# 2. Writes FileInfo objects for the proxy modules -# saved to the disk. This is done to keep the user -# experience in consistent with Export-PSSession. -# 3. Updates progress bar. -######################################################### -function ProcessStreamHelper -{ - param - ( - [string] $verboseMessage, - [string] $progressBarActivityName, - [string] $status, - [double] $previousSegmentWeight, - [double] $currentSegmentWeight, - [int] $totalNumberofEntries, - [int] $currentEntryCount, - [string] $path, - [System.Management.Automation.PSCmdlet] $callerPSCmdlet - ) - - Write-Verbose -Message $verboseMessage - ProgressBarHelper $progressBarActivityName $status $previousSegmentWeight $currentSegmentWeight $totalNumberofEntries $currentEntryCount - $proxyFile = New-Object -TypeName System.IO.FileInfo -ArgumentList $path | Get-Item - if($null -ne $callerPSCmdlet) - { - $callerPSCmdlet.WriteObject($proxyFile) - } -} - -######################################################### -# GetAssociatedType is a helper function used -# to fetch associated instance's EntityType -# for a given Navigation property in the -# supplied metadata. -######################################################### -function GetAssociatedType -{ - param - ( - [ODataUtils.Metadata] $Metadata, - [ODataUtils.NavigationProperty] $navProperty - ) - - # $metaData is already validated at the cmdlet layer. - if($null -eq $navProperty) { throw ($LocalizedData.ArguementNullError -f "NavigationProperty", "GetAssociatedType") } - - $associationName = $navProperty.AssociationName - $association = $Metadata.Associations | Where-Object { $_.Name -eq $associationName } - $associationType = $association.Type - - if ($associationType.Count -lt 1) - { - throw ($LocalizedData.AssociationNotFound -f $associationName) - } - elseif ($associationType.Count -gt 1) - { - throw ($LocalizedData.TooManyMatchingAssociationTypes -f $associationType.Count, $associationName) - } - - if ($associationType.NavPropertyName1 -eq $navProperty.ToRole) - { - $associatedType = $associationType.EndType1 - } - elseif ($associationType.NavPropertyName2 -eq $navProperty.ToRole) - { - $associatedType = $associationType.EndType2 - } - else - { - throw ($LocalizedData.ZeroMatchingAssociationTypes -f $navProperty.ToRole, $association.Name) - } - - # return associated EntityType - $associatedType -} - -######################################################### -# AddParametersNode is a helper function used -# to add parameters to the generated proxy cmdlet, -# based on mandatoryProperties and otherProperties. -# PrefixForKeys is used by associations to append a -# prefix to PowerShell parameter name. -######################################################### -function AddParametersNode -{ - param - ( - [Parameter(Mandatory=$true)] - [System.Xml.XmlWriter] $xmlWriter, - [ODataUtils.TypeProperty[]] $keyProperties, - [ODataUtils.TypeProperty[]] $mandatoryProperties, - [ODataUtils.TypeProperty[]] $otherProperties, - [string] $prefixForKeys, - [boolean] $addForceParameter, - [boolean] $addParametersElement, - [Hashtable] $complexTypeMapping - ) - - if($null -eq $xmlWriter) { throw ($LocalizedData.ArguementNullError -f "xmlWriter", "AddParametersNode") } - - if(($keyProperties.Length -gt 0) -or - ($mandatoryProperties.Length -gt 0) -or - ($otherProperties.Length -gt 0) -or - ($addForceParameter)) - { - if($addParametersElement) - { - $xmlWriter.WriteStartElement('Parameters') - } - - $pos = 0 - - if ($null -ne $keyProperties) - { - $pos = AddParametersCDXML $xmlWriter $keyProperties $pos $true $prefixForKeys ":Key" $complexTypeMapping - } - - if ($null -ne $mandatoryProperties) - { - $pos = AddParametersCDXML $xmlWriter $mandatoryProperties $pos $true $null $null $complexTypeMapping - } - - if ($null -ne $otherProperties) - { - $pos = AddParametersCDXML $xmlWriter $otherProperties $pos $false $null $null $complexTypeMapping - } - - if($addForceParameter) - { - $forceParameter = [ODataUtils.TypeProperty] @{ - "Name" = "Force"; - "TypeName" = "switch"; - "IsNullable" = $false - } - - $pos = AddParametersCDXML $xmlWriter $forceParameter $pos $false $null $null $complexTypeMapping - } - - if($addParametersElement) - { - $xmlWriter.WriteEndElement() - } - } -} - -######################################################### -# AddParametersNode is a helper function used -# to add Parameter node to CDXML based on properties. -# Prefix is appended to PS parameter names, used for -# associations. Suffix is appended to all parameter -# names, for ex. to differentiate keys. returns new $pos -######################################################### -function AddParametersCDXML -{ - param - ( - [Parameter(Mandatory=$true)] - [System.Xml.XmlWriter] $xmlWriter, - [ODataUtils.TypeProperty[]] $properties, - [Parameter(Mandatory=$true)] - [int] $pos, - [bool] $isMandatory, - [string] $prefix, - [string] $suffix, - [Hashtable] $complexTypeMapping - ) - - $properties | Where-Object { $null -ne $_ } | ForEach-Object { - $xmlWriter.WriteStartElement('Parameter') - $xmlWriter.WriteAttributeString('ParameterName', $_.Name + $suffix) - $xmlWriter.WriteStartElement('Type') - $PSTypeName = Convert-ODataTypeToCLRType $_.TypeName $complexTypeMapping - $xmlWriter.WriteAttributeString('PSType', $PSTypeName) - $xmlWriter.WriteEndElement() - - $xmlWriter.WriteStartElement('CmdletParameterMetadata') - $xmlWriter.WriteAttributeString('PSName', $prefix + $_.Name) - $xmlWriter.WriteAttributeString('IsMandatory', ($isMandatory).ToString().ToLowerInvariant()) - $xmlWriter.WriteAttributeString('Position', $pos) - if($isMandatory) - { - $xmlWriter.WriteAttributeString('ValueFromPipelineByPropertyName', 'true') - } - $xmlWriter.WriteEndElement() - $xmlWriter.WriteEndElement() - - $pos++ - } - - $pos -} - -######################################################### -# GenerateComplexTypeDefinition is a helper function used -# to generate complex type definition from the metadata. -######################################################### -function GenerateComplexTypeDefinition -{ - param - ( - [ODataUtils.Metadata] $metaData, - [string] $metaDataUri, - [string] $OutputModule, - [string] $typeDefinitionFileName, - [string] $cmdletAdapter, - [System.Management.Automation.PSCmdlet] $callerPSCmdlet - ) - - #metadataUri, $OutputModule & $cmdletAdapter are already validated at the cmdlet layer. - if($null -eq $typeDefinationFileName) { throw ($LocalizedData.ArguementNullError -f "TypeDefinationFileName", "GenerateComplexTypeDefination") } - if($null -eq $metaData) { throw ($LocalizedData.ArguementNullError -f "metadata", "GenerateComplexTypeDefination") } - if($null -eq $callerPSCmdlet) { throw ($LocalizedData.ArguementNullError -f "PSCmdlet", "GenerateComplexTypeDefination") } - - $Path = "$OutputModule\$typeDefinitionFileName" - - # We are currently generating classes for EntityType & ComplexType - # definition exposed in the metadata. - $typesToBeGenerated = $metaData.EntityTypes+$metadata.ComplexTypes - - if($null -ne $typesToBeGenerated -and $typesToBeGenerated.Count -gt 0) - { - $complexTypeMapping = @{} - $entityTypeNameSpaceMapping = @{} - - foreach ($entityType in $typesToBeGenerated) - { - if ($null -ne $entityType) - { - $entityTypeFullName = $entityType.Namespace + '.' + $entityType.Name - if(!$complexTypeMapping.ContainsKey($entityTypeFullName)) - { - $complexTypeMapping.Add($entityTypeFullName, $entityType.Name) - } - - if(!$entityTypeNameSpaceMapping.ContainsKey($entityType.Namespace)) - { - $entityTypes = @() - $entityTypeNameSpaceMapping.Add($entityType.Namespace, $entityTypes) - } - - $entityTypeNameSpaceMapping[$entityType.Namespace] += $entityType - } - } - - if($entityTypeNameSpaceMapping.Count -gt 0) - { -$output = @" -`$typeDefinitions = @" -using System; -using System.Management.Automation; - -"@ - - foreach($currentNameSpace in $entityTypeNameSpaceMapping.Keys) - { - $entityTypes = $entityTypeNameSpaceMapping[$currentNameSpace] - - $output += "`r`nnamespace $(ValidateComplexTypeIdentifier $currentNameSpace $true $metaDataUri $callerPSCmdlet)`r`n{" - - foreach ($entityType in $entityTypes) - { - $entityTypeFullName = (ValidateComplexTypeIdentifier $entityType.Namespace $true $metaDataUri $callerPSCmdlet) + '.' + $entityType.Name - Write-Verbose ($LocalizedData.VerboseAddingTypeDefinationToGeneratedModule -f $entityTypeFullName, "$OutputModule\$typeDefinationFileName") - - if($null -ne $entityType.BaseType) - { - $entityBaseFullName = (ValidateComplexTypeIdentifier $entityType.BaseType.Namespace $true $metaDataUri $callerPSCmdlet) + '.' + (ValidateComplexTypeIdentifier $entityType.BaseType.Name $false $metaDataUri $callerPSCmdlet) - $output += "`r`n public class $(ValidateComplexTypeIdentifier $entityType.Name $false $metaDataUri $callerPSCmdlet) : $($entityBaseFullName)`r`n {" - } - else - { - $output += "`r`n public class $(ValidateComplexTypeIdentifier $entityType.Name $false $metaDataUri $callerPSCmdlet)`r`n {" - } - - $properties = $null - - for($index = 0; $index -lt $entityType.EntityProperties.Count; $index++) - { - $property = $entityType.EntityProperties[$index] - $typeName = Convert-ODataTypeToCLRType $property.TypeName $complexTypeMapping - $properties += "`r`n public $typeName $(ValidateComplexTypeIdentifier $property.Name $false $metaDataUri $callerPSCmdlet);" - } - - # Navigation properties are treated like any other property for NetworkController scenario. - if ($cmdletAdapter -eq "NetworkControllerAdapter") - { - for($index = 0; $index -lt $entityType.NavigationProperties.Count; $index++) - { - $property = $entityType.NavigationProperties[$index] - $navigationTypeName = GetNavigationPropertyTypeName $property $metaData - $typeName = Convert-ODataTypeToCLRType $navigationTypeName $complexTypeMapping - $properties += "`r`n public $typeName $(ValidateComplexTypeIdentifier $property.Name $false $metaDataUri $callerPSCmdlet);" - } - } - - $output += $properties - $output += "`r`n }`r`n" - } - - $output += "}`r`n" - } - $output += """@`r`n" - - $output += "Add-Type -TypeDefinition `$typeDefinitions `r`n" - $output | Out-File -FilePath $Path - Write-Verbose ($LocalizedData.VerboseSavedTypeDefinationModule -f $typeDefinationFileName, $OutputModule) - } - } - - return $complexTypeMapping -} - -# Creating a single instance of CSharpCodeProvider that would be used -# for Identifier validation in the ValidateComplexTypeIdentifier helper method. -$cSharpCodeProvider = [Microsoft.CSharp.CSharpCodeProvider]::new() - -######################################################### -# ValidateComplexTypeIdentifier is a helper function to -# make sure that the type names defined in the -# metadata are valid C# Identifier names. This validation -# is performed to make sure that there are no security -# threat from importing the generated complex type -# (which is created using the metadata file). -# This method return the identifier name if its a -# valid identifier, else a terminating error in thrown. -######################################################### -function ValidateComplexTypeIdentifier -{ - param - ( - [string] $identifierName, - [bool] $isNameSpaceName, - [string] $metaDataUri, - [System.Management.Automation.PSCmdlet] $callerPSCmdlet - ) - - if($null -eq $callerPSCmdletl) { throw ($LocalizedData.ArguementNullError -f "PSCmdlet", "ValidateComplexTypeIdentifier") } - - if($isNameSpaceName) - { - $independentIdentifiers = $identifierName.Split('.') - $result = $true - foreach($currentIdentifier in $independentIdentifiers) - { - if(![System.CodeDom.Compiler.CodeGenerator]::IsValidLanguageIndependentIdentifier($currentIdentifier)) - { - $result = $false - break - } - } - } - else - { - $result = $cSharpCodeProvider.IsValidIdentifier($identifierName) - } - - if(!$result) - { - $errorMessage = ($LocalizedData.InValidIdentifierInMetadata -f $metaDataUri, $identifierName) - $errorRecord = CreateErrorRecordHelper "ODataEndpointProxyInvalidIdentifier" $errorMessage ([System.Management.Automation.ErrorCategory]::InvalidData) $null $identifierName - $callerPSCmdlet.ThrowTerminatingError($errorRecord) - } - else - { - return $identifierName - } -} - -######################################################### -# GetKeys is a helper function used to -# return the keys for the entity if customUri -# is specified. -######################################################### -function GetKeys -{ - param - ( - [ODataUtils.EntitySet] $entitySet, - [string] $customUri, - [string] $actionName - ) - - # Get the original keys - $key = (GetAllProperties $entitySet.Type) | Where-Object { $_.IsKey } - - # Get the keys with delimiters - $keys = $customUri -split "/" | ForEach-Object { - if ($_ -match '{*}') - { - [ODataUtils.TypeProperty] @{ - "Name" = $_.Substring($_.IndexOf('{')+1,$_.IndexOf('}')-$_.IndexOf('{')-1); - "TypeName" = "Edm.String"; - "IsNullable" = $false; - "IsMandatory" = $true; - } - } - elseif ($_ -match '\[*\]') - { - if ($ActionName -eq 'Get') { - [ODataUtils.TypeProperty] @{ - "Name" = $_.Substring($_.IndexOf('[')+1,$_.IndexOf(']')-$_.IndexOf('[')-1); - "TypeName" = "Edm.String"; - "IsNullable" = $false; - "IsMandatory" = $false; - } - } - else { - [ODataUtils.TypeProperty] @{ - "Name" = $_.Substring($_.IndexOf('[')+1,$_.IndexOf(']')-$_.IndexOf('[')-1); - "TypeName" = "Edm.String"; - "IsNullable" = $false; - "IsMandatory" = $true; - } - } - } - } - - # Now combine the two keys and avoid duplication - # Make a list of names already present in the new keys - # Foreach old key check if that key is present in the new keyList - # Else add the key to new key list - $keyParams = $keys | ForEach-Object {$_.Name} - - if ($null -eq $keyParams -Or $keyParams.Count -eq 0) { - $keys = $key - } - else { - if ($keyParams.Count -eq 1) { - $keys = @($keys) - } - - $key | ForEach-Object { - if ($keyParams.Contains($_.Name) -eq $false) - { - $keys += $_ - } - } - } - - $keys -} - -######################################################### -# GetNetworkControllerAdditionalProperties is a helper -# function used to fetch network controller specific -# additional properties. -######################################################### -function GetNetworkControllerAdditionalProperties -{ - param - ( - $navigationProperties, - $metaData - ) - - # Additional properties contains the types present as navigation properties - - $additionalProperties = $navigationProperties | Where-Object { $null -ne $_ } | ForEach-Object { - $typeName = GetNavigationPropertyTypeName $_ $metaData - - if ($_.Name -eq "Properties") { - $isNullable = $false - } - else { - $isNullable = $true - } - - [ODataUtils.TypeProperty] @{ - "Name" = $_.Name; - "TypeName" = $typeName - "IsNullable" = $isNullable; - } - } - - # Add etag to the additionalProperties - - if ($null -ne $additionalProperties) - { - if ($additionalProperties.Count -eq 1) { - $additionalProperties = @($additionalProperties) - } - - $additionalProperties += [ODataUtils.TypeProperty] @{ - "Name" = "Etag"; - "TypeName" = "Edm.String"; - "IsNullable" = $true; - } - } - else - { - $additionalProperties = [ODataUtils.TypeProperty] @{ - "Name" = "Etag"; - "TypeName" = "Edm.String"; - "IsNullable" = $true; - } - } - - $additionalProperties -} - -######################################################### -# UpdateNetworkControllerSpecificProperties is a -# helper function used to append additionalProperties -# to nullable/nonNullable Properties. This is network controller -# specific logic. -######################################################### -function UpdateNetworkControllerSpecificProperties -{ - param - ( - $nullableProperties, - $additionalProperties, - $keyProperties, - $isNullable - ) - - if ($isNullable) { - $additionalProperties = $additionalProperties | Where-Object { $_.isNullable } - } - else { - $additionalProperties = $additionalProperties | Where-Object { -not $_.isNullable } - } - - if ($null -eq $nullableProperties) - { - $nullableProperties = $additionalProperties - } - else { - if ($nullableProperties.Count -eq 1) { - $nullableProperties = @($nullableProperties) - } - if ($null -ne $additionalProperties) { - $nullableProperties += $additionalProperties - } - } - - if ($null -ne $nullableProperties -And $null -ne $keyProperties) - { - if ($keyProperties.Count -eq 1) { - $keyProperties = @($keyProperties) - } - - $keys = $keyProperties | ForEach-Object {$_.Name} - - if ($keys.Count -eq 1) { - $keys = @($keys) - } - - $nullableProperties = $nullableProperties | Where-Object {$keys.Contains($_.Name) -eq $false} - } - - $nullableProperties -} - -######################################################### -# GetNavigationPropertyTypeName is a -# helper function used to fetch the type corresponding -# to navigation property in this metadata. This is -# network controller specific logic. -######################################################### -function GetNavigationPropertyTypeName -{ - param - ( - $navigationProperty, - $metaData - ) - - foreach($association in $metaData.Associations) - { - if ($association.Name -ne $navigationProperty.AssociationName -Or $association.Namespace -ne $navigationProperty.AssociationNamespace) - { - continue - } - - # Now get the type for this association - - if ($association.Type.NavPropertyName1 -eq $navigationProperty.Name) - { - $type = $association.Type.EndType1 - $multiplicity = $association.Type.Multiplicity1 - } - elseif ($associationType.NavPropertyName2 -eq $navigationProperty.Name) - { - $type = $association.Type.EndType2 - $multiplicity = $association.Type.Multiplicity2 - } - - break - } - - $fullName = $type.Namespace + '.' + $type.Name - - # Check the multiplicity and convert to array if needed - if ($multiplicity -eq "*") - { - $fullName = "Collection($fullName)" - } - - $fullName -} diff --git a/src/Modules/Windows-Full/Microsoft.PowerShell.ODataUtils/Microsoft.PowerShell.ODataUtils.psd1 b/src/Modules/Windows-Full/Microsoft.PowerShell.ODataUtils/Microsoft.PowerShell.ODataUtils.psd1 deleted file mode 100644 index 589b6cb824c..00000000000 --- a/src/Modules/Windows-Full/Microsoft.PowerShell.ODataUtils/Microsoft.PowerShell.ODataUtils.psd1 +++ /dev/null @@ -1,240 +0,0 @@ -# -# Module manifest for module 'Microsoft.PowerShell.ODataUtils' -# -# Generated on: 8/15/2013 -# - -@{ - -# Script module or binary module file associated with this manifest. -RootModule = 'Microsoft.PowerShell.ODataUtils.psm1' - -# Version number of this module. -ModuleVersion = '1.0' - -# ID used to uniquely identify this module -GUID = 'fa1606d1-94cb-4264-bfb6-def714420084' - -# Author of this module -Author = 'Microsoft Corporation' - -# Company or vendor of this module -CompanyName = 'Microsoft Corporation' - -# Copyright statement for this module -Copyright = '(c) 2014 Microsoft. All rights reserved.' - -# Description of the functionality provided by this module -# Description = '' - -# Minimum version of the Windows PowerShell engine required by this module -# PowerShellVersion = '' - -# Name of the Windows PowerShell host required by this module -# PowerShellHostName = '' - -# Minimum version of the Windows PowerShell host required by this module -# PowerShellHostVersion = '' - -# Minimum version of Microsoft .NET Framework required by this module -# DotNetFrameworkVersion = '' - -# Minimum version of the common language runtime (CLR) required by this module -# CLRVersion = '' - -# Processor architecture (None, X86, Amd64) required by this module -# ProcessorArchitecture = '' - -# Modules that must be imported into the global environment prior to importing this module -# RequiredModules = @() - -# Assemblies that must be loaded prior to importing this module -# RequiredAssemblies = @() - -# Script files (.ps1) that are run in the caller's environment prior to importing this module. -# ScriptsToProcess = @() - -# Type files (.ps1xml) to be loaded when importing this module -# TypesToProcess = @() - -# Format files (.ps1xml) to be loaded when importing this module -# FormatsToProcess = @() - -# Modules to import as nested modules of the module specified in RootModule/ModuleToProcess -# NestedModules = @() - -# Functions to export from this module -FunctionsToExport = @('Export-ODataEndpointProxy') - -# Cmdlets to export from this module -CmdletsToExport = '' - -# Variables to export from this module -VariablesToExport = '' - -# Aliases to export from this module -AliasesToExport = '' - -# List of all modules packaged with this module -# ModuleList = @() - -# List of all files packaged with this module -# FileList = @() - -# Private data to pass to the module specified in RootModule/ModuleToProcess -# PrivateData = '' - -# HelpInfo URI of this module -HelpInfoURI = 'https://go.microsoft.com/fwlink/?LinkId=509916' - -# Default prefix for commands exported from this module. Override the default prefix using Import-Module -Prefix. -# DefaultCommandPrefix = '' - -} - - -# SIG # Begin signature block -# MIIavwYJKoZIhvcNAQcCoIIasDCCGqwCAQExCzAJBgUrDgMCGgUAMGkGCisGAQQB -# gjcCAQSgWzBZMDQGCisGAQQBgjcCAR4wJgIDAQAABBAfzDtgWUsITrck0sYpfvNR -# AgEAAgEAAgEAAgEAAgEAMCEwCQYFKw4DAhoFAAQU5qxxE9NAWfUb5Y4oxi1pHiiU -# j+GgghWCMIIEwzCCA6ugAwIBAgITMwAAAHQNgGQOfWd9owAAAAAAdDANBgkqhkiG -# 9w0BAQUFADB3MQswCQYDVQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4G -# A1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSEw -# HwYDVQQDExhNaWNyb3NvZnQgVGltZS1TdGFtcCBQQ0EwHhcNMTUwMzIwMTczMjA1 -# WhcNMTYwNjIwMTczMjA1WjCBszELMAkGA1UEBhMCVVMxEzARBgNVBAgTCldhc2hp -# bmd0b24xEDAOBgNVBAcTB1JlZG1vbmQxHjAcBgNVBAoTFU1pY3Jvc29mdCBDb3Jw -# b3JhdGlvbjENMAsGA1UECxMETU9QUjEnMCUGA1UECxMebkNpcGhlciBEU0UgRVNO -# OjdEMkUtMzc4Mi1CMEY3MSUwIwYDVQQDExxNaWNyb3NvZnQgVGltZS1TdGFtcCBT -# ZXJ2aWNlMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA4NFrifjVvo5Y -# gN/jD+4M6zszXn3GnmZHP9AerBSCDRiftpwnIvG2hpREQXSJkW8X9t+Y5jbLX3iS -# 6XJ+S7kExWIUc3HGf2NBW+tk8r1cVWJGzA9ewQnEr9nxvyV94BegUO4lqkXl48Z+ -# vxBZqcGPPtn77GQbY1u1p7jq681X6xtD9WWRv1D1+cEGvH2qzDfnBqmgzLH1M8wN -# ssh1ZgDRbTCTR8+OomdEXhoTf/McHucPncG8SPyBgW1UauJpE8bO9ZdnMmxIyhHC -# VjrW3Dpi9PwQl2RIC4pc8RbClfDLYBukA5sMyfe7kr8Ac2czHKJ673VKGUZaDH6a -# W6A6HVQ16wIDAQABo4IBCTCCAQUwHQYDVR0OBBYEFCUsOGYFtEU5DmC29u69PuDd -# r4wNMB8GA1UdIwQYMBaAFCM0+NlSRnAK7UD7dvuzK7DDNbMPMFQGA1UdHwRNMEsw -# SaBHoEWGQ2h0dHA6Ly9jcmwubWljcm9zb2Z0LmNvbS9wa2kvY3JsL3Byb2R1Y3Rz -# L01pY3Jvc29mdFRpbWVTdGFtcFBDQS5jcmwwWAYIKwYBBQUHAQEETDBKMEgGCCsG -# AQUFBzAChjxodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpL2NlcnRzL01pY3Jv -# c29mdFRpbWVTdGFtcFBDQS5jcnQwEwYDVR0lBAwwCgYIKwYBBQUHAwgwDQYJKoZI -# hvcNAQEFBQADggEBAEEG50j6xJHcMBMNInjC0iPTszPL+yYh1978CncY+4Nyzu/U -# LIaP4xXj1RICZ1xbN9MDe02RW0FTZgn9457fLHgJORo2HYqBocllfJx7kbIPSptB -# 3cdEC2EFyUwu8rRrKKoIR+4IrGZUF1aQiMbpddAhEDh5yT+7VTDFpjmmU7/NXFbS -# ThcUvGISy+lL8MWR3J2EypjWDttWFGht21OLMM+6J2V1oDFvk6N1EGDqqu7uduvl -# jAup0655zzS+SR8i0MT1o+/zrjDcjohGI4ygqjyXrwfbdug2VN+Ls4mewOospGBr -# 8d/DthI6rzM4elFxNTXm5AjiUZaC+b7hG4N8e2cwggTsMIID1KADAgECAhMzAAAA -# ymzVMhI1xOFVAAEAAADKMA0GCSqGSIb3DQEBBQUAMHkxCzAJBgNVBAYTAlVTMRMw -# EQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25kMR4wHAYDVQQKExVN -# aWNyb3NvZnQgQ29ycG9yYXRpb24xIzAhBgNVBAMTGk1pY3Jvc29mdCBDb2RlIFNp -# Z25pbmcgUENBMB4XDTE0MDQyMjE3MzkwMFoXDTE1MDcyMjE3MzkwMFowgYMxCzAJ -# BgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYDVQQHEwdSZWRtb25k -# MR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xDTALBgNVBAsTBE1PUFIx -# HjAcBgNVBAMTFU1pY3Jvc29mdCBDb3Jwb3JhdGlvbjCCASIwDQYJKoZIhvcNAQEB -# BQADggEPADCCAQoCggEBAJZxXe0GRvqEy51bt0bHsOG0ETkDrbEVc2Cc66e2bho8 -# P/9l4zTxpqUhXlaZbFjkkqEKXMLT3FIvDGWaIGFAUzGcbI8hfbr5/hNQUmCVOlu5 -# WKV0YUGplOCtJk5MoZdwSSdefGfKTx5xhEa8HUu24g/FxifJB+Z6CqUXABlMcEU4 -# LYG0UKrFZ9H6ebzFzKFym/QlNJj4VN8SOTgSL6RrpZp+x2LR3M/tPTT4ud81MLrs -# eTKp4amsVU1Mf0xWwxMLdvEH+cxHrPuI1VKlHij6PS3Pz4SYhnFlEc+FyQlEhuFv -# 57H8rEBEpamLIz+CSZ3VlllQE1kYc/9DDK0r1H8wQGcCAwEAAaOCAWAwggFcMBMG -# A1UdJQQMMAoGCCsGAQUFBwMDMB0GA1UdDgQWBBQfXuJdUI1Whr5KPM8E6KeHtcu/ -# gzBRBgNVHREESjBIpEYwRDENMAsGA1UECxMETU9QUjEzMDEGA1UEBRMqMzE1OTUr -# YjQyMThmMTMtNmZjYS00OTBmLTljNDctM2ZjNTU3ZGZjNDQwMB8GA1UdIwQYMBaA -# FMsR6MrStBZYAck3LjMWFrlMmgofMFYGA1UdHwRPME0wS6BJoEeGRWh0dHA6Ly9j -# cmwubWljcm9zb2Z0LmNvbS9wa2kvY3JsL3Byb2R1Y3RzL01pY0NvZFNpZ1BDQV8w -# OC0zMS0yMDEwLmNybDBaBggrBgEFBQcBAQROMEwwSgYIKwYBBQUHMAKGPmh0dHA6 -# Ly93d3cubWljcm9zb2Z0LmNvbS9wa2kvY2VydHMvTWljQ29kU2lnUENBXzA4LTMx -# LTIwMTAuY3J0MA0GCSqGSIb3DQEBBQUAA4IBAQB3XOvXkT3NvXuD2YWpsEOdc3wX -# yQ/tNtvHtSwbXvtUBTqDcUCBCaK3cSZe1n22bDvJql9dAxgqHSd+B+nFZR+1zw23 -# VMcoOFqI53vBGbZWMrrizMuT269uD11E9dSw7xvVTsGvDu8gm/Lh/idd6MX/YfYZ -# 0igKIp3fzXCCnhhy2CPMeixD7v/qwODmHaqelzMAUm8HuNOIbN6kBjWnwlOGZRF3 -# CY81WbnYhqgA/vgxfSz0jAWdwMHVd3Js6U1ZJoPxwrKIV5M1AHxQK7xZ/P4cKTiC -# 095Sl0UpGE6WW526Xxuj8SdQ6geV6G00DThX3DcoNZU6OJzU7WqFXQ4iEV57MIIF -# vDCCA6SgAwIBAgIKYTMmGgAAAAAAMTANBgkqhkiG9w0BAQUFADBfMRMwEQYKCZIm -# iZPyLGQBGRYDY29tMRkwFwYKCZImiZPyLGQBGRYJbWljcm9zb2Z0MS0wKwYDVQQD -# EyRNaWNyb3NvZnQgUm9vdCBDZXJ0aWZpY2F0ZSBBdXRob3JpdHkwHhcNMTAwODMx -# MjIxOTMyWhcNMjAwODMxMjIyOTMyWjB5MQswCQYDVQQGEwJVUzETMBEGA1UECBMK -# V2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEeMBwGA1UEChMVTWljcm9zb2Z0 -# IENvcnBvcmF0aW9uMSMwIQYDVQQDExpNaWNyb3NvZnQgQ29kZSBTaWduaW5nIFBD -# QTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALJyWVwZMGS/HZpgICBC -# mXZTbD4b1m/My/Hqa/6XFhDg3zp0gxq3L6Ay7P/ewkJOI9VyANs1VwqJyq4gSfTw -# aKxNS42lvXlLcZtHB9r9Jd+ddYjPqnNEf9eB2/O98jakyVxF3K+tPeAoaJcap6Vy -# c1bxF5Tk/TWUcqDWdl8ed0WDhTgW0HNbBbpnUo2lsmkv2hkL/pJ0KeJ2L1TdFDBZ -# +NKNYv3LyV9GMVC5JxPkQDDPcikQKCLHN049oDI9kM2hOAaFXE5WgigqBTK3S9dP -# Y+fSLWLxRT3nrAgA9kahntFbjCZT6HqqSvJGzzc8OJ60d1ylF56NyxGPVjzBrAlf -# A9MCAwEAAaOCAV4wggFaMA8GA1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFMsR6MrS -# tBZYAck3LjMWFrlMmgofMAsGA1UdDwQEAwIBhjASBgkrBgEEAYI3FQEEBQIDAQAB -# MCMGCSsGAQQBgjcVAgQWBBT90TFO0yaKleGYYDuoMW+mPLzYLTAZBgkrBgEEAYI3 -# FAIEDB4KAFMAdQBiAEMAQTAfBgNVHSMEGDAWgBQOrIJgQFYnl+UlE/wq4QpTlVnk -# pDBQBgNVHR8ESTBHMEWgQ6BBhj9odHRwOi8vY3JsLm1pY3Jvc29mdC5jb20vcGtp -# L2NybC9wcm9kdWN0cy9taWNyb3NvZnRyb290Y2VydC5jcmwwVAYIKwYBBQUHAQEE -# SDBGMEQGCCsGAQUFBzAChjhodHRwOi8vd3d3Lm1pY3Jvc29mdC5jb20vcGtpL2Nl -# cnRzL01pY3Jvc29mdFJvb3RDZXJ0LmNydDANBgkqhkiG9w0BAQUFAAOCAgEAWTk+ -# fyZGr+tvQLEytWrrDi9uqEn361917Uw7LddDrQv+y+ktMaMjzHxQmIAhXaw9L0y6 -# oqhWnONwu7i0+Hm1SXL3PupBf8rhDBdpy6WcIC36C1DEVs0t40rSvHDnqA2iA6VW -# 4LiKS1fylUKc8fPv7uOGHzQ8uFaa8FMjhSqkghyT4pQHHfLiTviMocroE6WRTsgb -# 0o9ylSpxbZsa+BzwU9ZnzCL/XB3Nooy9J7J5Y1ZEolHN+emjWFbdmwJFRC9f9Nqu -# 1IIybvyklRPk62nnqaIsvsgrEA5ljpnb9aL6EiYJZTiU8XofSrvR4Vbo0HiWGFzJ -# NRZf3ZMdSY4tvq00RBzuEBUaAF3dNVshzpjHCe6FDoxPbQ4TTj18KUicctHzbMrB -# 7HCjV5JXfZSNoBtIA1r3z6NnCnSlNu0tLxfI5nI3EvRvsTxngvlSso0zFmUeDord -# EN5k9G/ORtTTF+l5xAS00/ss3x+KnqwK+xMnQK3k+eGpf0a7B2BHZWBATrBC7E7t -# s3Z52Ao0CW0cgDEf4g5U3eWh++VHEK1kmP9QFi58vwUheuKVQSdpw5OPlcmN2Jsh -# rg1cnPCiroZogwxqLbt2awAdlq3yFnv2FoMkuYjPaqhHMS+a3ONxPdcAfmJH0c6I -# ybgY+g5yjcGjPa8CQGr/aZuW4hCoELQ3UAjWwz0wggYHMIID76ADAgECAgphFmg0 -# AAAAAAAcMA0GCSqGSIb3DQEBBQUAMF8xEzARBgoJkiaJk/IsZAEZFgNjb20xGTAX -# BgoJkiaJk/IsZAEZFgltaWNyb3NvZnQxLTArBgNVBAMTJE1pY3Jvc29mdCBSb290 -# IENlcnRpZmljYXRlIEF1dGhvcml0eTAeFw0wNzA0MDMxMjUzMDlaFw0yMTA0MDMx -# MzAzMDlaMHcxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYD -# VQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xITAf -# BgNVBAMTGE1pY3Jvc29mdCBUaW1lLVN0YW1wIFBDQTCCASIwDQYJKoZIhvcNAQEB -# BQADggEPADCCAQoCggEBAJ+hbLHf20iSKnxrLhnhveLjxZlRI1Ctzt0YTiQP7tGn -# 0UytdDAgEesH1VSVFUmUG0KSrphcMCbaAGvoe73siQcP9w4EmPCJzB/LMySHnfL0 -# Zxws/HvniB3q506jocEjU8qN+kXPCdBer9CwQgSi+aZsk2fXKNxGU7CG0OUoRi4n -# rIZPVVIM5AMs+2qQkDBuh/NZMJ36ftaXs+ghl3740hPzCLdTbVK0RZCfSABKR2YR -# JylmqJfk0waBSqL5hKcRRxQJgp+E7VV4/gGaHVAIhQAQMEbtt94jRrvELVSfrx54 -# QTF3zJvfO4OToWECtR0Nsfz3m7IBziJLVP/5BcPCIAsCAwEAAaOCAaswggGnMA8G -# A1UdEwEB/wQFMAMBAf8wHQYDVR0OBBYEFCM0+NlSRnAK7UD7dvuzK7DDNbMPMAsG -# A1UdDwQEAwIBhjAQBgkrBgEEAYI3FQEEAwIBADCBmAYDVR0jBIGQMIGNgBQOrIJg -# QFYnl+UlE/wq4QpTlVnkpKFjpGEwXzETMBEGCgmSJomT8ixkARkWA2NvbTEZMBcG -# CgmSJomT8ixkARkWCW1pY3Jvc29mdDEtMCsGA1UEAxMkTWljcm9zb2Z0IFJvb3Qg -# Q2VydGlmaWNhdGUgQXV0aG9yaXR5ghB5rRahSqClrUxzWPQHEy5lMFAGA1UdHwRJ -# MEcwRaBDoEGGP2h0dHA6Ly9jcmwubWljcm9zb2Z0LmNvbS9wa2kvY3JsL3Byb2R1 -# Y3RzL21pY3Jvc29mdHJvb3RjZXJ0LmNybDBUBggrBgEFBQcBAQRIMEYwRAYIKwYB -# BQUHMAKGOGh0dHA6Ly93d3cubWljcm9zb2Z0LmNvbS9wa2kvY2VydHMvTWljcm9z -# b2Z0Um9vdENlcnQuY3J0MBMGA1UdJQQMMAoGCCsGAQUFBwMIMA0GCSqGSIb3DQEB -# BQUAA4ICAQAQl4rDXANENt3ptK132855UU0BsS50cVttDBOrzr57j7gu1BKijG1i -# uFcCy04gE1CZ3XpA4le7r1iaHOEdAYasu3jyi9DsOwHu4r6PCgXIjUji8FMV3U+r -# kuTnjWrVgMHmlPIGL4UD6ZEqJCJw+/b85HiZLg33B+JwvBhOnY5rCnKVuKE5nGct -# xVEO6mJcPxaYiyA/4gcaMvnMMUp2MT0rcgvI6nA9/4UKE9/CCmGO8Ne4F+tOi3/F -# NSteo7/rvH0LQnvUU3Ih7jDKu3hlXFsBFwoUDtLaFJj1PLlmWLMtL+f5hYbMUVbo -# nXCUbKw5TNT2eb+qGHpiKe+imyk0BncaYsk9Hm0fgvALxyy7z0Oz5fnsfbXjpKh0 -# NbhOxXEjEiZ2CzxSjHFaRkMUvLOzsE1nyJ9C/4B5IYCeFTBm6EISXhrIniIh0EPp -# K+m79EjMLNTYMoBMJipIJF9a6lbvpt6Znco6b72BJ3QGEe52Ib+bgsEnVLaxaj2J -# oXZhtG6hE6a/qkfwEm/9ijJssv7fUciMI8lmvZ0dhxJkAj0tr1mPuOQh5bWwymO0 -# eFQF1EEuUKyUsKV4q7OglnUa2ZKHE3UiLzKoCG6gW4wlv6DvhMoh1useT8ma7kng -# 9wFlb4kLfchpyOZu6qeXzjEp/w7FW1zYTRuh2Povnj8uVRZryROj/TGCBKcwggSj -# AgEBMIGQMHkxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAwDgYD -# VQQHEwdSZWRtb25kMR4wHAYDVQQKExVNaWNyb3NvZnQgQ29ycG9yYXRpb24xIzAh -# BgNVBAMTGk1pY3Jvc29mdCBDb2RlIFNpZ25pbmcgUENBAhMzAAAAymzVMhI1xOFV -# AAEAAADKMAkGBSsOAwIaBQCggcAwGQYJKoZIhvcNAQkDMQwGCisGAQQBgjcCAQQw -# HAYKKwYBBAGCNwIBCzEOMAwGCisGAQQBgjcCARUwIwYJKoZIhvcNAQkEMRYEFFNt -# DhScbWHVDFMkTLRd2qRP8wFeMGAGCisGAQQBgjcCAQwxUjBQoCaAJABXAGkAbgBk -# AG8AdwBzACAAUABvAHcAZQByAFMAaABlAGwAbKEmgCRodHRwOi8vd3d3Lm1pY3Jv -# c29mdC5jb20vcG93ZXJzaGVsbCAwDQYJKoZIhvcNAQEBBQAEggEAddyT//E7Ysbi -# U9kfhQrYkjrhhZCKOzQPVAZSNxWx246MveRe1aj3A+Kr868dYH3x8or8g7MpeJig -# 0WOx4O+mw4fUCdTT6fLqo+W8Q+5qNpWjpfpP5eq7firhhh5D8jB1h7tJWI7fkvHN -# VwadYG4t4BxGIFgsn6YIgPn8ZipmOLb8zvCaDPpg9Xr5U5YKKUrA3sgiuW+zf0aK -# r506K+pfuC56XItbX25VEvf+hjazJr2UasFTweV4mCgKHoAG1UluKUZaX8B+KaKB -# DGMUJ3pCAqyt9RCTSQC9xZxWyK+g0byzn2dpCNxWDXHI7SxCs8ejBhp5yYtlBXl7 -# vkGAXdlJ9aGCAigwggIkBgkqhkiG9w0BCQYxggIVMIICEQIBATCBjjB3MQswCQYD -# VQQGEwJVUzETMBEGA1UECBMKV2FzaGluZ3RvbjEQMA4GA1UEBxMHUmVkbW9uZDEe -# MBwGA1UEChMVTWljcm9zb2Z0IENvcnBvcmF0aW9uMSEwHwYDVQQDExhNaWNyb3Nv -# ZnQgVGltZS1TdGFtcCBQQ0ECEzMAAAB0DYBkDn1nfaMAAAAAAHQwCQYFKw4DAhoF -# AKBdMBgGCSqGSIb3DQEJAzELBgkqhkiG9w0BBwEwHAYJKoZIhvcNAQkFMQ8XDTE1 -# MDUxMTE4MTE1M1owIwYJKoZIhvcNAQkEMRYEFJl30ao3ese0x5O4lPs3SVZVIOdq -# MA0GCSqGSIb3DQEBBQUABIIBAESrx9oY0vI7nyBV0AwzpSYTA/jQF/+MGVokP7ak -# 5i7x25UMc7+RRMROW9VxhzYpzPtotmF8H4rfZsJLxPRlbFF2pu+8MKiNAKiP851m -# tsD1Cw8AN7T31LG8Syk3yKtEvsvnc3yzZy6sXUbkn02yjHNp0PMsrQJNw9ALRc/p -# s3mHzZTqYkmFeHUHzsRa97ByExmjPnP4vcfK2HdZ+oq2EiLjGICooqimt2ys/BPy -# 7nYZaeHaKaNJtnOQHM2BqN38OcH7X7K4IzxCNceXEION6gZE6wqvp+dkvpN5wasL -# OkQhubomAuN/S2TGKwjx3H45G1dpl3LXqihqtqF/Sed7MZs= -# SIG # End signature block diff --git a/src/Modules/Windows-Full/Microsoft.PowerShell.ODataUtils/Microsoft.PowerShell.ODataUtils.psm1 b/src/Modules/Windows-Full/Microsoft.PowerShell.ODataUtils/Microsoft.PowerShell.ODataUtils.psm1 deleted file mode 100644 index 611e45d815a..00000000000 --- a/src/Modules/Windows-Full/Microsoft.PowerShell.ODataUtils/Microsoft.PowerShell.ODataUtils.psm1 +++ /dev/null @@ -1,231 +0,0 @@ -Import-LocalizedData LocalizedData -FileName Microsoft.PowerShell.ODataUtilsStrings.psd1 - - -# This module doesn't support Arm because of Add-Type cmdlet -$ProcessorArchitecture = (Get-WmiObject -query "Select Architecture from Win32_Processor").Architecture - -# 0 = x86 -# 1 = MIPS -# 2 = Alpha -# 3 = PowerPC -# 5 = ARM -# 6 = Itanium -# 9 = x64 -if ($ProcessorArchitecture -eq 5) -{ - throw $LocalizedData.ArchitectureNotSupported -f "ARM" -} - -. "$PSScriptRoot\Microsoft.PowerShell.ODataUtilsHelper.ps1" - -######################################################### -# Generates PowerShell module containing client side -# proxy cmdlets that can be used to interact with an -# OData based server side endpoint. -######################################################### -function Export-ODataEndpointProxy -{ - [CmdletBinding( - DefaultParameterSetName='CDXML', - SupportsShouldProcess=$true, - HelpUri="https://go.microsoft.com/fwlink/?LinkId=510069")] - [OutputType([System.IO.FileInfo])] - param - ( - [Parameter(Position=0, Mandatory=$true, ValueFromPipeline=$true, ValueFromPipelineByPropertyName=$true)] - [ValidateNotNullOrEmpty()] - [string] $Uri, - - [Parameter(Position=1, Mandatory=$true, ValueFromPipelineByPropertyName=$true)] - [ValidateNotNullOrEmpty()] - [string] $OutputModule, - - [Parameter(Position=2, ValueFromPipelineByPropertyName=$true)] - [ValidateNotNullOrEmpty()] - [string] $MetadataUri, - - [Parameter(Position=3, ValueFromPipelineByPropertyName=$true)] - [PSCredential] $Credential, - - [Parameter(Position=4, ValueFromPipelineByPropertyName=$true)] - [ValidateSet('Put', 'Post', 'Patch')] - [string] $CreateRequestMethod='Post', - - [Parameter(Position=5, ValueFromPipelineByPropertyName=$true)] - [ValidateSet('Put', 'Post', 'Patch')] - [string] $UpdateRequestMethod='Patch', - - [Parameter(Position=6, ValueFromPipelineByPropertyName=$true)] - [ValidateSet('ODataAdapter', 'NetworkControllerAdapter', 'ODataV4Adapter')] - [string] $CmdletAdapter='ODataAdapter', - - [Parameter(Position=7, ValueFromPipelineByPropertyName=$true)] - [Hashtable] $ResourceNameMapping, - - [parameter (Position=8,ValueFromPipelineByPropertyName=$true)] - [switch] $Force, - - [Parameter(Position=9, ValueFromPipelineByPropertyName=$true)] - [Hashtable] $CustomData, - - [parameter (Position=10,ValueFromPipelineByPropertyName=$true)] - [switch] $AllowClobber, - - [parameter (Position=11,ValueFromPipelineByPropertyName=$true)] - [switch] $AllowUnsecureConnection, - - [parameter (Position=12,ValueFromPipelineByPropertyName=$true)] - [ValidateNotNull()] - [Hashtable] $Headers - ) - - BEGIN - { - if (!$MetadataUri) - { - $Uri = $Uri.TrimEnd('/') - $MetadataUri = $Uri + '/$metadata' - $PSBoundParameters["MetadataUri"] = $MetadataUri - } - - # Validate to make sure that a valid URI is supplied as input. - try - { - $connectionUri = [System.Uri]::new($Uri) - } - catch - { - $errorMessage = ($LocalizedData.InValidUri -f $Uri) - $exception = [System.InvalidOperationException]::new($errorMessage, $_.Exception) - $errorRecord = CreateErrorRecordHelper "ODataEndpointProxyInvalidUriFormat" $null ([System.Management.Automation.ErrorCategory]::InvalidArgument) $exception $Uri - $PSCmdlet.ThrowTerminatingError($errorRecord) - } - - # Block Redfish-support for in-box version of the module and advise user to use a version from PS Gallery instead. - # According to Redfish specification DSP0266v1.0.1 Redfish Service Metadata Document and Redfish Service Root URIs (used by Export-ODataEndpointProxy) are required to start with '/redfish/v1' (section "6.3 Redfish-Defined URIs and Relative URI Rules"). - # We use this as indicator of whether Export-ODataEndpointProxy was attempted against a Redfish endpoint. - if($connectionUri.AbsolutePath.StartsWith('/redfish/',[StringComparison]::OrdinalIgnoreCase)) - { - $errorMessage = $LocalizedData.RedfishNotEnabled - $exception = [System.InvalidOperationException]::new($errorMessage) - $errorRecord = CreateErrorRecordHelper "ODataEndpointProxyRedfishNotEnabled" $errorMessage ([System.Management.Automation.ErrorCategory]::NotEnabled) $exception $Uri - $PSCmdlet.ThrowTerminatingError($errorRecord) - } - - if($connectionUri.Scheme -eq "http" -and !$AllowUnsecureConnection.IsPresent) - { - $errorMessage = ($LocalizedData.AllowUnsecureConnectionMessage -f $PSCmdlet.MyInvocation.MyCommand.Name, $Uri, "Uri") - $exception = [System.InvalidOperationException]::new($errorMessage, $_.Exception) - $errorRecord = CreateErrorRecordHelper "ODataEndpointProxyUnSecureConnection" $null ([System.Management.Automation.ErrorCategory]::InvalidArgument) $exception $Uri - $PSCmdlet.ThrowTerminatingError($errorRecord) - } - - $OutputModuleExists = Test-Path -Path $OutputModule -PathType Container - - if($OutputModuleExists -and ($Force -eq $false)) - { - $errorMessage = ($LocalizedData.ModuleAlreadyExistsAndForceParameterIsNotSpecified -f $OutputModule) - $errorRecord = CreateErrorRecordHelper "ODataEndpointProxyOutputModuleExists" $errorMessage ([System.Management.Automation.ErrorCategory]::ResourceExists) $null $OutputModule - $PSCmdlet.ThrowTerminatingError($errorRecord) - } - - $isWhatIf = $psboundparameters.ContainsKey("WhatIf") - - if(!$OutputModuleExists) - { - if(!$isWhatIf) - { - $OutputModule = (New-Item -Path $OutputModule -ItemType Directory).FullName - } - } - else - { - $resolvedOutputModulePath = Resolve-Path -Path $OutputModule -ErrorAction Stop -Verbose - if($resolvedOutputModulePath.Count -gt 1) - { - $errorMessage = ($LocalizedData.OutputModulePathIsNotUnique -f $OutputModule) - $errorRecord = CreateErrorRecordHelper "ODataEndpointProxyOutputModulePathIsNotUnique" $errorMessage ([System.Management.Automation.ErrorCategory]::InvalidArgument) $null $OutputModule - $PSCmdlet.ThrowTerminatingError($errorRecord) - } - - # Make sure that the path specified is a valid file system directory path. - if([system.IO.Directory]::Exists($resolvedOutputModulePath)) - { - $OutputModule = $resolvedOutputModulePath - } - else - { - $errorMessage = ($LocalizedData.OutputModulePathIsNotFileSystemPath -f $OutputModule) - $errorRecord = CreateErrorRecordHelper "ODataEndpointProxyPathIsNotFileSystemPath" $errorMessage ([System.Management.Automation.ErrorCategory]::InvalidArgument) $null $OutputModule - $PSCmdlet.ThrowTerminatingError($errorRecord) - } - } - - $rootDir = [System.IO.Directory]::GetDirectoryRoot($OutputModule) - - if($rootDir -eq $OutputModule) - { - $errorMessage = ($LocalizedData.InvalidOutputModulePath -f $OutputModule) - $errorRecord = CreateErrorRecordHelper "ODataEndpointProxyInvalidOutputModulePath" $errorMessage ([System.Management.Automation.ErrorCategory]::InvalidArgument) $null $OutputModule - $PSCmdlet.ThrowTerminatingError($errorRecord) - } - - if(!$isWhatIf) - { - $progressBarStatus = ($LocalizedData.ProgressBarMessage -f $Uri) - ProgressBarHelper "Export-ODataEndpointProxy" $progressBarStatus 0 100 100 1 - } - - # Add parameters to $PSBoundParameters, which were not passed by user, but the default value is set - $parametersWithDefaultValue = @("CreateRequestMethod", "UpdateRequestMethod", "CmdletAdapter") - - foreach ($parameterWithDefaultValue in $parametersWithDefaultValue) - { - if (!$PSBoundParameters.ContainsKey($parameterWithDefaultValue)) - { - $PSBoundParameters.Add($parameterWithDefaultValue, (Get-Variable $parameterWithDefaultValue).Value) - } - } - } - - END - { - if($pscmdlet.ShouldProcess($Uri)) - { - try - { - $PSBoundParameters.Add("ProgressBarStatus", $progressBarStatus) - $PSBoundParameters.Add("PSCmdlet", $PSCmdlet) - - # Import module based on selected CmdletAdapter - $adapterToImport = $CmdletAdapter - - # NetworkControllerAdapter relies on ODataAdapter - if ($CmdletAdapter -eq 'NetworkControllerAdapter') - { - $adapterToImport = 'ODataAdapter' - } - - Write-Debug ($LocalizedData.SelectedAdapter -f $adapterPSScript) - - $adapterPSScript = "$PSScriptRoot\Microsoft.PowerShell." + $adapterToImport + ".ps1" - - . $adapterPSScript - ExportODataEndpointProxy @PSBoundParameters - } - catch - { - $errorMessage = ($LocalizedData.InValidMetadata -f $Uri) - $exception = [System.InvalidOperationException]::new($errorMessage, $_.Exception) - $errorRecord = CreateErrorRecordHelper "ODataEndpointProxyInvalidInput" $null ([System.Management.Automation.ErrorCategory]::InvalidData) $exception $Uri - $PSCmdlet.ThrowTerminatingError($errorRecord) - } - finally - { - Write-Progress -Activity "Export-ODataEndpointProxy" -Completed - } - } - } -} - -Export-ModuleMember -Function @('Export-ODataEndpointProxy') diff --git a/src/Modules/Windows-Full/Microsoft.PowerShell.ODataUtils/Microsoft.PowerShell.ODataUtilsHelper.ps1 b/src/Modules/Windows-Full/Microsoft.PowerShell.ODataUtils/Microsoft.PowerShell.ODataUtilsHelper.ps1 deleted file mode 100644 index 8a4c65424f1..00000000000 --- a/src/Modules/Windows-Full/Microsoft.PowerShell.ODataUtils/Microsoft.PowerShell.ODataUtilsHelper.ps1 +++ /dev/null @@ -1,782 +0,0 @@ -# Base class definitions used by the actual Adapter modules -$global:BaseClassDefinitions = @" -using System; - -namespace ODataUtils -{ - public class TypeProperty - { - public String Name; - - // OData Type Name, e.g. Edm.Int32 - public String TypeName; - - public bool IsKey; - public bool IsMandatory; - public bool? IsNullable; - } - - public class NavigationProperty - { - public String Name; - - public String FromRole; - public String ToRole; - public String AssociationNamespace; - public String AssociationName; - } - - public class EntityTypeBase - { - public String Namespace; - public String Name; - public TypeProperty[] EntityProperties; - public bool IsEntity; - public EntityTypeBase BaseType; - } - - public class AssociationType - { - public String Namespace; - - public EntityTypeBase EndType1; - public String Multiplicity1; - public String NavPropertyName1; - - public EntityTypeBase EndType2; - public String Multiplicity2; - public String NavPropertyName2; - } - - public class EntitySet - { - public String Namespace; - public String Name; - public EntityTypeBase Type; - } - - public class AssociationSet - { - public String Namespace; - public String Name; - public AssociationType Type; - } - - public class Action - { - public String Namespace; - public String Verb; - public EntitySet EntitySet; - public Boolean IsSideEffecting; - public Boolean IsBindable; - public Boolean IsSingleInstance; - public TypeProperty[] Parameters; - } - - public class MetadataBase - { - // Desired destination namespace - public String Namespace; - } - - public class CmdletParameter - { - public CmdletParameter() - { - } - - public CmdletParameter(String type, String name) - { - this.Type = type; - this.Name = name; - this.Qualifiers = new String[] { "Parameter(ValueFromPipelineByPropertyName=`$true)" }; - } - - public String[] Qualifiers; - public String Type; - public String Name; - } - - public class CmdletParameters - { - public CmdletParameter[] Parameters; - } - - public class ReferentialConstraint - { - public String Property; - public String ReferencedProperty; - } - - public class OnDelete - { - public String Action; - } - - public class NavigationPropertyV4 - { - public String Name; - public String Type; - public bool Nullable; - public String Partner; - public bool ContainsTarget; - public ReferentialConstraint[] ReferentialConstraints; - public OnDelete OnDelete; - } - - public class NavigationPropertyBinding - { - public String Path; - public String Target; - } - - public class EntityTypeV4 : EntityTypeBase - { - public String Alias; - public NavigationPropertyV4[] NavigationProperties; - public String BaseTypeStr; - } - - public class SingletonType - { - public String Namespace; - public String Alias; - public String Name; - public String Type; - public NavigationPropertyBinding[] NavigationPropertyBindings; - } - - public class EntitySetV4 - { - public String Namespace; - public String Alias; - public String Name; - public EntityTypeV4 Type; - } - - public class EnumMember - { - public String Name; - public String Value; - } - - public class EnumType - { - public String Namespace; - public String Alias; - public String Name; - public String UnderlyingType; - public bool IsFlags; - public EnumMember[] Members; - } - - public class ActionV4 - { - public String Namespace; - public String Alias; - public String Name; - public String Action; - public EntitySetV4 EntitySet; - public TypeProperty[] Parameters; - } - - public class FunctionV4 - { - public String Namespace; - public String Alias; - public String Name; - public bool Function; - public String EntitySet; - public String ReturnType; - public Parameter[] Parameters; - } - - public class Parameter - { - public String Name; - public String Type; - public bool Nullable; - } - - public class ReferenceInclude - { - public String Namespace; - public String Alias; - } - - public class Reference - { - public String Uri; - } - - public class MetadataV4 : MetadataBase - { - public string ODataVersion; - public string Uri; - public string MetadataUri; - public string Alias; - public Reference[] References; - public string DefaultEntityContainerName; - public EntitySetV4[] EntitySets; - public EntityTypeV4[] EntityTypes; - public SingletonType[] SingletonTypes; - public EntityTypeV4[] ComplexTypes; - public EntityTypeV4[] TypeDefinitions; - public EnumType[] EnumTypes; - public ActionV4[] Actions; - public FunctionV4[] Functions; - } - - public class ReferencedMetadata - { - public System.Collections.ArrayList References; - } - - public class ODataEndpointProxyParameters - { - public String Uri; - public String MetadataUri; - public System.Management.Automation.PSCredential Credential; - public String OutputModule; - - public bool Force; - public bool AllowClobber; - public bool AllowUnsecureConnection; - } - - public class EntityType : EntityTypeBase - { - public NavigationProperty[] NavigationProperties; - } - - public class Metadata : MetadataBase - { - public String DefaultEntityContainerName; - public EntitySet[] EntitySets; - public EntityType[] EntityTypes; - public EntityType[] ComplexTypes; - public AssociationSet[] Associations; - public Action[] Actions; - } -} -"@ - -######################################################### -# GetMetaData is a helper function used to fetch metadata -# from the specified file or web URL. -######################################################### -function GetMetaData -{ - param - ( - [string] $metaDataUri, - [System.Management.Automation.PSCmdlet] $callerPSCmdlet, - [PSCredential] $credential, - [Hashtable] $headers - ) - - # $metaDataUri is already validated at the cmdlet layer. - if($null -eq $callerPSCmdletl) { throw ($LocalizedData.ArguementNullError -f "PSCmdlet", "GetMetaData") } - Write-Verbose ($LocalizedData.VerboseReadingMetadata -f $metaDataUri) - - try - { - $uri = [System.Uri]::new($metadataUri) - - # By default, proxy generation is supported on secured Uri (i.e., https). - # However if the user trusts the unsecure http uri, then they can override - # the security check by specifying -AllowSecureConnection parameter during - # proxy generation. - if($uri.Scheme -eq "http" -and !$AllowUnsecureConnection.IsPresent) - { - $errorMessage = ($LocalizedData.AllowUnsecureConnectionMessage -f $callerPSCmdlet.MyInvocation.MyCommand.Name, $uri, "MetaDataUri") - $exception = [System.InvalidOperationException]::new($errorMessage, $_.Exception) - $errorRecord = CreateErrorRecordHelper "ODataEndpointProxyUnSecureConnection" $null ([System.Management.Automation.ErrorCategory]::InvalidArgument) $exception $uri - $callerPSCmdlet.ThrowTerminatingError($errorRecord) - } - } - catch - { - $errorMessage = ($LocalizedData.InValidMetadata -f $MetadataUri) - $exception = [System.InvalidOperationException]::new($errorMessage, $_.Exception) - $errorRecord = CreateErrorRecordHelper "ODataEndpointProxyInvalidMetadataUriFormat" $null ([System.Management.Automation.ErrorCategory]::InvalidArgument) $exception $MetadataUri - $callerPSCmdlet.ThrowTerminatingError($errorRecord) - } - - if($uri.IsFile) - { - if ($null -ne $credential) - { - $fileExists = Test-Path -Path $metaDataUri -PathType Leaf -Credential $credential -ErrorAction Stop - } - else - { - $fileExists = Test-Path -Path $metaDataUri -PathType Leaf -ErrorAction Stop - } - - if($fileExists) - { - $metaData = Get-Content -Path $metaDataUri -ErrorAction Stop - } - else - { - $errorMessage = ($LocalizedData.MetadataUriDoesNotExist -f $MetadataUri) - $errorRecord = CreateErrorRecordHelper "ODataEndpointProxyMetadataFileDoesNotExist" $errorMessage ([System.Management.Automation.ErrorCategory]::InvalidArgument) $null $MetadataUri - $callerPSCmdlet.ThrowTerminatingError($errorRecord) - } - } - else - { - try - { - $cmdParams = @{'Uri'= $metaDataUri ; 'UseBasicParsing'=$true; 'ErrorAction'= 'Stop'} - - if ($null -ne $credential) - { - $cmdParams.Add('Credential', $credential) - } - - if ($null -ne $headers) - { - $cmdParams.Add('Headers', $headers) - } - - $webResponse = Invoke-WebRequest @cmdParams - } - catch - { - $errorMessage = ($LocalizedData.MetadataUriDoesNotExist -f $MetadataUri) - $exception = [System.InvalidOperationException]::new($errorMessage, $_.Exception) - $errorRecord = CreateErrorRecordHelper "ODataEndpointProxyMetadataUriDoesNotExist" $null ([System.Management.Automation.ErrorCategory]::InvalidArgument) $exception $MetadataUri - $callerPSCmdlet.ThrowTerminatingError($errorRecord) - } - - if($null -ne $webResponse) - { - if ($webResponse.StatusCode -eq 200) - { - $metaData = $webResponse.Content - - if ($null -eq $metadata) - { - $errorMessage = ($LocalizedData.EmptyMetadata -f $MetadataUri) - $errorRecord = CreateErrorRecordHelper "ODataEndpointProxyMetadataIsEmpty" $errorMessage ([System.Management.Automation.ErrorCategory]::InvalidArgument) $null $MetadataUri - $callerPSCmdlet.ThrowTerminatingError($errorRecord) - } - } - else - { - $errorMessage = ($LocalizedData.InvalidEndpointAddress -f $MetadataUri, $webResponse.StatusCode) - $errorRecord = CreateErrorRecordHelper "ODataEndpointProxyInvalidEndpointAddress" $errorMessage ([System.Management.Automation.ErrorCategory]::InvalidArgument) $null $MetadataUri - $callerPSCmdlet.ThrowTerminatingError($errorRecord) - } - } - } - - if($null -ne $metaData) - { - try - { - [xml] $metadataXML = $metaData - } - catch - { - $errorMessage = ($LocalizedData.InValidMetadata -f $MetadataUri) - $exception = [System.InvalidOperationException]::new($errorMessage, $_.Exception) - $errorRecord = CreateErrorRecordHelper "ODataEndpointProxyInvalidMetadata" $null ([System.Management.Automation.ErrorCategory]::InvalidData) $exception $MetadataUri - $callerPSCmdlet.ThrowTerminatingError($errorRecord) - } - } - - return $metadataXML -} - -######################################################### -# VerifyMetadataHelper is a helper function used to -# validate if Error/Warning message has to be displayed -# during command collision. -######################################################### -function VerifyMetadataHelper -{ - param - ( - [string] $localizedDataErrorString, - [string] $localizedDataWarningString, - [string] $entitySetName, - [string] $currentCommandName, - [string] $metaDataUri, - [boolean] $allowClobber, - [System.Management.Automation.PSCmdlet] $callerPSCmdlet - ) - - if($null -eq $localizedDataErrorString) { throw ($LocalizedData.ArguementNullError -f "localizedDataErrorString", "VerifyMetadataHelper") } - if($null -eq $localizedDataWarningString) { throw ($LocalizedData.ArguementNullError -f "localizedDataWarningString", "VerifyMetadataHelper") } - - if(!$allowClobber) - { - # Write Error message and skip current Entity Set. - $errorMessage = ($localizedDataErrorString -f $entitySetName, $currentCommandName) - $exception = [System.InvalidOperationException]::new($errorMessage) - $errorRecord = CreateErrorRecordHelper "ODataEndpointDefaultPropertyCollision" $null ([System.Management.Automation.ErrorCategory]::InvalidOperation) $exception $metaDataUri - $callerPSCmdlet.WriteError($errorRecord) - } - else - { - $warningMessage = ($localizedDataWarningString -f $entitySetName, $currentCommandName) - $callerPSCmdlet.WriteWarning($warningMessage) - } -} - -######################################################### -# CreateErrorRecordHelper is a helper function used to -# create an error record. -######################################################### -function CreateErrorRecordHelper -{ - param - ( - [string] $errorId, - [string] $errorMessage, - [System.Management.Automation.ErrorCategory] $errorCategory, - [Exception] $exception, - [object] $targetObject - ) - - if($null -eq $exception) - { - $exception = New-Object System.IO.IOException $errorMessage - } - - $errorRecord = New-Object System.Management.Automation.ErrorRecord $exception, $errorId, $errorCategory, $targetObject - return $errorRecord -} - -######################################################### -# ProgressBarHelper is a helper function used to -# used to display progress message. -######################################################### -function ProgressBarHelper -{ - param - ( - [string] $cmdletName, - [string] $status, - [double] $previousSegmentWeight, - [double] $currentSegmentWeight, - [int] $totalNumberofEntries, - [int] $currentEntryCount - ) - - if($null -eq $cmdletName) { throw ($LocalizedData.ArguementNullError -f "CmdletName", "ProgressBarHelper") } - if($null -eq $status) { throw ($LocalizedData.ArguementNullError -f "Status", "ProgressBarHelper") } - - if($currentEntryCount -gt 0 -and - $totalNumberofEntries -gt 0 -and - $previousSegmentWeight -ge 0 -and - $currentSegmentWeight -gt 0) - { - $entryDefaultWeight = $currentSegmentWeight/[double]$totalNumberofEntries - $percentComplete = $previousSegmentWeight + ($entryDefaultWeight * $currentEntryCount) - Write-Progress -Activity $cmdletName -Status $status -PercentComplete $percentComplete - } -} - -######################################################### -# Convert-ODataTypeToCLRType is a helper function used to -# Convert OData type to its CLR equivalent. -######################################################### -function Convert-ODataTypeToCLRType -{ - param - ( - [string] $typeName, - [Hashtable] $complexTypeMapping - ) - - if($null -eq $typeName) { throw ($LocalizedData.ArguementNullError -f "TypeName", "Convert-ODataTypeToCLRType ") } - - switch ($typeName) - { - "Edm.Binary" {"Byte[]"} - "Edm.Boolean" {"Boolean"} - "Edm.Byte" {"Byte"} - "Edm.DateTime" {"DateTime"} - "Edm.Decimal" {"Decimal"} - "Edm.Double" {"Double"} - "Edm.Single" {"Single"} - "Edm.Guid" {"Guid"} - "Edm.Int16" {"Int16"} - "Edm.Int32" {"Int32"} - "Edm.Int64" {"Int64"} - "Edm.SByte" {"SByte"} - "Edm.String" {"String"} - "Edm.PropertyPath" {"String"} - "switch" {"switch"} - "Edm.DateTimeOffset" {"DateTimeOffset"} - default - { - if($null -ne $complexTypeMapping -and - $complexTypeMapping.Count -gt 0 -and - $complexTypeMapping.ContainsKey($typeName)) - { - $typeName - } - else - { - $regex = "Collection\((.+)\)" - if ($typeName -match $regex) - { - $insideTypeName = Convert-ODataTypeToCLRType $Matches[1] $complexTypeMapping - "$insideTypeName[]" - } - else - { - "PSObject" - } - } - } - } -} - -######################################################### -# SaveCDXMLHeader is a helper function used -# to save CDXML headers common to all -# PSODataUtils modules. -######################################################### -function SaveCDXMLHeader -{ - param - ( - [System.Xml.XmlWriter] $xmlWriter, - [string] $uri, - [string] $className, - [string] $defaultNoun, - [string] $cmdletAdapter - ) - - # $uri & $cmdletAdapter are already validated at the cmdlet layer. - if($null -eq $xmlWriter) { throw ($LocalizedData.ArguementNullError -f "xmlWriter", "SaveCDXMLHeader") } - if($null -eq $defaultNoun) { throw ($LocalizedData.ArguementNullError -f "DefaultNoun", "SaveCDXMLHeader") } - - if ($className -eq 'ServiceActions' -Or $cmdletAdapter -eq "NetworkControllerAdapter") - { - $entityName = '' - } - else - { - $entityName = $className - } - - if ($uri[-1] -ne '/') - { - $fullName = "$uri/$entityName" - } - else - { - $fullName = "$uri$entityName" - } - - $xmlWriter.Formatting = 'Indented' - $xmlWriter.Indentation = 2 - $xmlWriter.IndentChar = ' ' - - $xmlWriter.WriteStartDocument() - - $today=Get-Date - $xmlWriter.WriteComment("This module was autogenerated by PSODataUtils on $today.") - - $xmlWriter.WriteStartElement('PowerShellMetadata') - $xmlWriter.WriteAttributeString('xmlns', 'http://schemas.microsoft.com/cmdlets-over-objects/2009/11') - - $xmlWriter.WriteStartElement('Class') - $xmlWriter.WriteAttributeString('ClassName', $fullName) - $xmlWriter.WriteAttributeString('ClassVersion', '1.0.0') - - $DotNetAdapter = 'Microsoft.PowerShell.Cmdletization.OData.ODataCmdletAdapter' - - if ($CmdletAdapter -eq "NetworkControllerAdapter") { - $DotNetAdapter = 'Microsoft.PowerShell.Cmdletization.OData.NetworkControllerCmdletAdapter' - } - elseif ($CmdletAdapter -eq "ODataV4Adapter") { - $DotNetAdapter = 'Microsoft.PowerShell.Cmdletization.OData.ODataV4CmdletAdapter' - } - - $xmlWriter.WriteAttributeString('CmdletAdapter', $DotNetAdapter + ', Microsoft.PowerShell.Cmdletization.OData, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35') - - $xmlWriter.WriteElementString('Version', '1.0') - $xmlWriter.WriteElementString('DefaultNoun', $defaultNoun) - - $xmlWriter -} - -######################################################### -# SaveCDXMLFooter is a helper function used -# to save CDXML closing attributes corresponding -# to SaveCDXMLHeader function. -######################################################### -function SaveCDXMLFooter -{ - param - ( - [System.Xml.XmlWriter] $xmlWriter - ) - - if($null -eq $xmlWriter) { throw ($LocalizedData.ArguementNullError -f "xmlWriter", "SaveCDXMLFooter") } - - $xmlWriter.WriteEndElement() - $xmlWriter.WriteEndElement() - $xmlWriter.WriteEndDocument() - - $xmlWriter.Flush() - $xmlWriter.Close() -} - -######################################################### -# AddParametersNode is a helper function used -# to add parameters to the generated proxy cmdlet, -# based on mandatoryProperties and otherProperties. -# PrefixForKeys is used by associations to append a -# prefix to PowerShell parameter name. -######################################################### -function AddParametersNode -{ - param - ( - [Parameter(Mandatory=$true)] - [System.Xml.XmlWriter] $xmlWriter, - [ODataUtils.TypeProperty[]] $keyProperties, - [ODataUtils.TypeProperty[]] $mandatoryProperties, - [ODataUtils.TypeProperty[]] $otherProperties, - [string] $prefixForKeys, - [boolean] $addForceParameter, - [boolean] $addParametersElement, - [Hashtable] $complexTypeMapping - ) - - if($null -eq $xmlWriter) { throw ($LocalizedData.ArguementNullError -f "xmlWriter", "AddParametersNode") } - - if(($keyProperties.Length -gt 0) -or - ($mandatoryProperties.Length -gt 0) -or - ($otherProperties.Length -gt 0) -or - ($addForceParameter)) - { - if($addParametersElement) - { - $xmlWriter.WriteStartElement('Parameters') - } - - $pos = 0 - - if ($null -ne $keyProperties) - { - $pos = AddParametersCDXML $xmlWriter $keyProperties $pos $true $prefixForKeys ":Key" $complexTypeMapping - } - - if ($null -ne $mandatoryProperties) - { - $pos = AddParametersCDXML $xmlWriter $mandatoryProperties $pos $true $null $null $complexTypeMapping - } - - if ($null -ne $otherProperties) - { - $pos = AddParametersCDXML $xmlWriter $otherProperties $pos $false $null $null $complexTypeMapping - } - - if($addForceParameter) - { - $forceParameter = [ODataUtils.TypeProperty] @{ - "Name" = "Force"; - "TypeName" = "switch"; - "IsNullable" = $false - } - - $pos = AddParametersCDXML $xmlWriter $forceParameter $pos $false $null $null $complexTypeMapping - } - - if($addParametersElement) - { - $xmlWriter.WriteEndElement() - } - } -} - -######################################################### -# AddParametersCDXML is a helper function used -# to add Parameter node to CDXML based on properties. -# Prefix is appended to PS parameter names, used for -# associations. Suffix is appended to all parameter -# names, for ex. to differentiate keys. returns new $pos -######################################################### -function AddParametersCDXML -{ - param - ( - [Parameter(Mandatory=$true)] - [System.Xml.XmlWriter] $xmlWriter, - [ODataUtils.TypeProperty[]] $properties, - [Parameter(Mandatory=$true)] - [int] $pos, - [bool] $isMandatory, - [string] $prefix, - [string] $suffix, - [Hashtable] $complexTypeMapping - ) - - $properties | Where-Object { $null -ne $_ } | ForEach-Object { - $xmlWriter.WriteStartElement('Parameter') - $xmlWriter.WriteAttributeString('ParameterName', $_.Name + $suffix) - $xmlWriter.WriteStartElement('Type') - $PSTypeName = Convert-ODataTypeToCLRType $_.TypeName $complexTypeMapping - $xmlWriter.WriteAttributeString('PSType', $PSTypeName) - $xmlWriter.WriteEndElement() - - $xmlWriter.WriteStartElement('CmdletParameterMetadata') - $xmlWriter.WriteAttributeString('PSName', $prefix + $_.Name) - $xmlWriter.WriteAttributeString('IsMandatory', ($isMandatory).ToString().ToLowerInvariant()) - $xmlWriter.WriteAttributeString('Position', $pos) - if($isMandatory) - { - $xmlWriter.WriteAttributeString('ValueFromPipelineByPropertyName', 'true') - } - $xmlWriter.WriteEndElement() - $xmlWriter.WriteEndElement() - - $pos++ - } - - $pos -} - -######################################################### -# GenerateSetProxyCmdlet is a helper function used -# to generate Set-* proxy cmdlet. The proxy cmdlet is -# generated in the CDXML compliant format. -######################################################### -function GenerateSetProxyCmdlet -{ - param - ( - [System.XMl.XmlTextWriter] $xmlWriter, - [object[]] $keyProperties, - [object[]] $nonKeyProperties, - [Hashtable] $complexTypeMapping - ) - - # $cmdletAdapter is already validated at the cmdlet layer. - if($null -eq $xmlWriter) { throw ($LocalizedData.ArguementNullError -f "xmlWriter", "GenerateSetProxyCmdlet") } - - $xmlWriter.WriteStartElement('Cmdlet') - $xmlWriter.WriteStartElement('CmdletMetadata') - $xmlWriter.WriteAttributeString('Verb', 'Set') - $xmlWriter.WriteAttributeString('DefaultCmdletParameterSet', 'Default') - $xmlWriter.WriteAttributeString('ConfirmImpact', 'Medium') - $xmlWriter.WriteEndElement() - - $xmlWriter.WriteStartElement('Method') - $xmlWriter.WriteAttributeString('MethodName', 'Update') - $xmlWriter.WriteAttributeString('CmdletParameterSet', 'Default') - - AddParametersNode $xmlWriter $keyProperties $null $nonKeyProperties $null $true $true $complexTypeMapping - $xmlWriter.WriteEndElement() - $xmlWriter.WriteEndElement() -} diff --git a/src/Modules/Windows-Full/Microsoft.PowerShell.ODataUtils/Microsoft.PowerShell.ODataV4Adapter.ps1 b/src/Modules/Windows-Full/Microsoft.PowerShell.ODataUtils/Microsoft.PowerShell.ODataV4Adapter.ps1 deleted file mode 100644 index 9ecd112b2e8..00000000000 --- a/src/Modules/Windows-Full/Microsoft.PowerShell.ODataUtils/Microsoft.PowerShell.ODataV4Adapter.ps1 +++ /dev/null @@ -1,2668 +0,0 @@ -Import-LocalizedData LocalizedData -FileName Microsoft.PowerShell.ODataUtilsStrings.psd1 - -# Add .NET classes used by the module -Add-Type -TypeDefinition $global:BaseClassDefinitions - -######################################################### -# Generates PowerShell module containing client side -# proxy cmdlets that can be used to interact with an -# OData based server side endpoint. -######################################################### -function ExportODataEndpointProxy -{ - param - ( - [string] $Uri, - [string] $OutputModule, - [string] $MetadataUri, - [PSCredential] $Credential, - [string] $CreateRequestMethod, - [string] $UpdateRequestMethod, - [string] $CmdletAdapter, - [Hashtable] $ResourceNameMapping, - [switch] $Force, - [Hashtable] $CustomData, - [switch] $AllowClobber, - [switch] $AllowUnsecureConnection, - [Hashtable] $Headers, - [string] $ProgressBarStatus, - [System.Management.Automation.PSCmdlet] $PSCmdlet - ) - - # Record of all metadata XML files which have been opened for parsing - # used to avoid parsing the same file twice, if referenced in multiple - # metadata files - $script:processedFiles = @() - - # Record of all referenced and parsed metadata files (including entry point metadata) - $script:GlobalMetadata = New-Object System.Collections.ArrayList - - # The namespace name might have invalid characters or might be conflicting with class names in inheritance scenarios - # We will be normalizing these namespaces and saving them into $normalizedNamespaces, where key is the original namespace and value is normalized namespace - $script:normalizedNamespaces = @{} - - # This information will be used during recursive referenced metadata files loading - $ODataEndpointProxyParameters = [ODataUtils.ODataEndpointProxyParameters] @{ - "MetadataUri" = $MetadataUri; - "Uri" = $Uri; - "Credential" = $Credential; - "OutputModule" = $OutputModule; - "Force" = $Force; - "AllowClobber" = $AllowClobber; - "AllowUnsecureConnection" = $AllowUnsecureConnection; - } - - # Recursively fetch all metadatas (referenced by entry point metadata) - $GlobalMetadata = GetTypeInfo -callerPSCmdlet $pscmdlet -MetadataUri $MetadataUri -ODataEndpointProxyParameters $ODataEndpointProxyParameters -Headers $Headers - # Now that we are done with recursive metadata references parsing we can get rid of this variable - $script:GlobalMetadata = $null - - VerifyMetadata $GlobalMetadata $AllowClobber.IsPresent $PSCmdlet $ProgressBarStatus - - # Get Uri Resource path key format. It can be either 'EmbeddedKey' or 'SeparateKey'. - # If not provided, default value will be set to 'EmbeddedKey'. - $UriResourcePathKeyFormat = 'EmbeddedKey' - if ($CustomData -and $CustomData.ContainsKey("UriResourcePathKeyFormat")) - { - $UriResourcePathKeyFormat = $CustomData."UriResourcePathKeyFormat" - } - - GenerateClientSideProxyModule $GlobalMetadata $ODataEndpointProxyParameters $OutputModule $CreateRequestMethod $UpdateRequestMethod $CmdletAdapter $ResourceNameMapping $CustomData $UriResourcePathKeyFormat $ProgressBarStatus $script:normalizedNamespaces -} - -######################################################### -# GetTypeInfo is a helper method used to get all the types -# from metadata files in a recursive manner -######################################################### -function GetTypeInfo -{ - param - ( - [System.Management.Automation.PSCmdlet] $callerPSCmdlet, - [string] $MetadataUri, - [ODataUtils.ODataEndpointProxyParameters] $ODataEndpointProxyParameters, - [Hashtable] $Headers - ) - - if($null -eq $callerPSCmdlet) { throw ($LocalizedData.ArguementNullError -f "callerPSCmdlet", "GetTypeInfo") } - - $metadataSet = New-Object System.Collections.ArrayList - $metadataXML = GetMetaData $MetadataUri $callerPSCmdlet $ODataEndpointProxyParameters.Credential $Headers $ODataEndpointProxyParameters.AllowUnsecureConnection - $script:processedFiles += $MetadataUri - - # parses all referenced metadata XML files recursively - foreach ($reference in $metadataXML.Edmx.Reference) - { - if (-not $script:processedFiles.Contains($reference.Uri)) - { - $tmpMetadataSet = $null - $tmpMetadataSet = GetTypeInfo -callerPSCmdlet $callerPSCmdlet -MetadataUri $reference.Uri -ODataEndpointProxyParameters $ODataEndpointProxyParameters - AddMetadataToMetadataSet -Metadatas $metadataSet -NewMetadata $tmpMetadataSet - } - } - - $metadatas = ParseMetadata -MetadataXML $metadataXML -ODataVersion $metadataXML.Edmx.Version -MetadataUri $MetadataUri -Uri $ODataEndpointProxyParameters.Uri -MetadataSet $script:GlobalMetadata - AddMetadataToMetadataSet -Metadatas $script:GlobalMetadata -NewMetadata $metadatas - AddMetadataToMetadataSet -Metadatas $metadataSet -NewMetadata $metadatas - - return $metadataSet -} - -function AddMetadataToMetadataSet -{ - param - ( - [System.Collections.ArrayList] $Metadatas, - $NewMetadata - ) - - if($null -eq $NewMetadata) { throw ($LocalizedData.ArguementNullError -f "NewMetadata", "AddMetadataToMetadataSet") } - - if ($NewMetadata.GetType().Name -eq 'MetadataV4') - { - $Metadatas.Add($NewMetadata) | Out-Null - } - else - { - $Metadatas.AddRange($NewMetadata) | Out-Null - } -} - -######################################################### -# Normalization of Namespace name will be required in following scenarios: -# 1. Namespace name contains combination of dots and numbers -# 2. Namespace name collides with Class name (EntityType, EntitySet, etc.) -# If normalization is needed, all dots will be replaced with underscores and Ns suffix added -# User will receive warning notifying her about the namespace name change -######################################################### -function NormalizeNamespace -{ - param - ( - [string] $MetadataNamespace, - [string] $MetadataUri, - [Hashtable] $NormalizedNamespaces, - [boolean] $DoesNamespaceConflictsWithClassName - ) - - $doesNamespaceContainsInvalidChars = $false - - # Check if namespace name contains invalid combination if dots and numbers - if ($MetadataNamespace -match '\.[0-9]' -or $MetadataNamespace -match '[0-9]\.') - { - # Normalization needed - $doesNamespaceContainsInvalidChars = $true - } - - # Normalize if needed - if ($doesNamespaceContainsInvalidChars -or $DoesNamespaceConflictsWithClassName) - { - if ($NormalizedNamespaces.ContainsKey($MetadataNamespace)) - { - # It's possible we've already attempted to normalize that namespace. In that case we'll update normalized name. - $NormalizedNamespaces[$MetadataNamespace] = NormalizeNamespaceHelper $NormalizedNamespaces[$MetadataNamespace] $doesNamespaceContainsInvalidChars $DoesNamespaceConflictsWithClassName - } - else - { - $NormalizedNamespaces.Add($MetadataNamespace, (NormalizeNamespaceHelper $MetadataNamespace $doesNamespaceContainsInvalidChars $DoesNamespaceConflictsWithClassName)) - } - } - - # Print warning - if ($doesNamespaceContainsInvalidChars) - { - # Normalization needed - $warningMessage = ($LocalizedData.InValidSchemaNamespaceContainsInvalidChars -f $MetadataUri, $MetadataNamespace, $NormalizedNamespaces[$MetadataNamespace]) - Write-Warning $warningMessage - } - if ($DoesNamespaceConflictsWithClassName) - { - # Collision between namespace name and type name detected (example: namespace TaskService { class Service : Service.BasicService { ... } ... }) - # Normalization needed - $warningMessage = ($LocalizedData.InValidSchemaNamespaceConflictWithClassName -f $MetadataUri, $MetadataNamespace, $NormalizedNamespaces[$MetadataNamespace]) - Write-Warning $warningMessage - } -} - -function NormalizeNamespaceCollisionWithClassName -{ - param - ( - [string] $InheritingType, - [string] $BaseTypeName, - [string] $MetadataUri - ) - - if (![string]::IsNullOrEmpty($BaseTypeName)) - { - $dotNetNamespace = '' - if ($BaseTypeName.LastIndexOf(".") -gt 0) - { - # BaseTypeStr contains Namespace and TypeName. Extract Namespace name. - $dotNetNamespace = $BaseTypeName.SubString(0, $BaseTypeName.LastIndexOf(".")) - } - - if (![string]::IsNullOrEmpty($dotNetNamespace) -and $InheritingType -eq $dotNetNamespace) - { - # Collision between namespace name and type name detected (example: namespace TaskService { class Service : Service.BasicService { ... } ... }) - # Normalization needed - NormalizeNamespace $dotNetNamespace $MetadataUri $script:normalizedNamespaces $true - break - } - } -} - -######################################################### -# This helper method is used by functions, -# writing directly to CDXML files or to .Net namespace/class definitions ComplexTypes file -######################################################### -function GetNamespace -{ - param - ( - [string] $Namespace, - $NormalizedNamespaces, - [boolean] $isClassNameIncluded = $false - ) - - $dotNetNamespace = $Namespace - $dotNetClassName = '' - - # Extract only namespace name - if ($isClassNameIncluded) - { - if ($Namespace.LastIndexOf(".") -gt 0) - { - # For example, from following namespace (Namespace.TypeName) Service.1.0.0.Service we'll extract only namespace name, which is Service.1.0.0 - $dotNetNamespace = $Namespace.SubString(0, $Namespace.LastIndexOf(".")) - $dotNetClassName = $Namespace.SubString($Namespace.LastIndexOf(".") + 1, $Namespace.Length - $Namespace.LastIndexOf(".") - 1) - } - } - - # Check if the namespace has to be normalized. - if ($NormalizedNamespaces.ContainsKey($dotNetNamespace)) - { - $dotNetNamespace = $NormalizedNamespaces.Get_Item($dotNetNamespace) - } - - if (![string]::IsNullOrEmpty($dotNetClassName)) - { - return ($dotNetNamespace + "." + $dotNetClassName) - } - else - { - return $dotNetNamespace - } -} - -function NormalizeNamespaceHelper -{ - param - ( - [string] $Namespace, - [boolean] $DoesNamespaceContainsInvalidChars, - [boolean] $DoesNamespaceConflictsWithClassName - ) - - # For example, following namespace: Service.1.0.0 - # Will change to: Service_1_0_0 - # Ns postfix in Namespace name will allow to differentiate between this namespace - # and a colliding type name from different namespace - $updatedNs = $Namespace - if ($DoesNamespaceContainsInvalidChars) - { - $updatedNs = $updatedNs.Replace('.', '_') - } - if ($DoesNamespaceConflictsWithClassName) - { - $updatedNs = $updatedNs + "Ns" - } - - $updatedNs -} - -######################################################### -# Processes EntityTypes (OData V4 schema) from plain text -# xml metadata into our custom structure -######################################################### -function ParseEntityTypes -{ - param - ( - [System.Xml.XmlElement] $SchemaXML, - [ODataUtils.MetadataV4] $Metadata, - [System.Collections.ArrayList] $GlobalMetadata, - [hashtable] $EntityAndComplexTypesQueue, - [string] $CustomNamespace, - [AllowEmptyString()] - [string] $Alias - ) - - if($null -eq $SchemaXML) { throw ($LocalizedData.ArguementNullError -f "SchemaXML", "ParseEntityTypes") } - - foreach ($entityType in $SchemaXML.EntityType) - { - $baseType = $null - - if ($null -ne $entityType.BaseType) - { - # add it to the processing queue - $baseType = GetBaseType $entityType $Metadata $SchemaXML.Namespace $GlobalMetadata - if ($null -eq $baseType) - { - $EntityAndComplexTypesQueue[$entityType.BaseType] += @(@{type='EntityType'; value=$entityType}) - } - - # Check if Namespace has to be normalized because of the collision with the inheriting Class name - NormalizeNamespaceCollisionWithClassName -InheritingType $entityType.Name -BaseTypeName $entityType.BaseType -MetadataUri $Metadata.Uri - } - - [ODataUtils.EntityTypeV4] $newType = ParseMetadataTypeDefinition $entityType $baseType $Metadata $schema.Namespace $Alias $true $entityType.BaseType - $Metadata.EntityTypes += $newType - AddDerivedTypes $newType $entityAndComplexTypesQueue $Metadata $SchemaXML.Namespace - } -} - -######################################################### -# Processes ComplexTypes from plain text xml metadata -# into our custom structure -######################################################### -function ParseComplexTypes -{ - param - ( - [System.Xml.XmlElement] $SchemaXML, - [ODataUtils.MetadataV4] $Metadata, - [System.Collections.ArrayList] $GlobalMetadata, - [hashtable] $EntityAndComplexTypesQueue, - [string] $CustomNamespace, - [AllowEmptyString()] - [string] $Alias - ) - - if($null -eq $SchemaXMLl) { throw ($LocalizedData.ArguementNullError -f "SchemaXML", "ParseComplexTypes") } - - foreach ($complexType in $SchemaXML.ComplexType) - { - $baseType = $null - - if ($null -ne $complexType.BaseType) - { - # add it to the processing queue - $baseType = GetBaseType $complexType $metadata $SchemaXML.Namespace $GlobalMetadata - if ($null -eq $baseType -and $null -ne $entityAndComplexTypesQueue -and $entityAndComplexTypesQueue.ContainsKey($complexType.BaseType)) - { - $entityAndComplexTypesQueue[$complexType.BaseType] += @(@{type='ComplexType'; value=$complexType}) - continue - } - - # Check if Namespace has to be normalized because of the collision with the inheriting Class name - NormalizeNamespaceCollisionWithClassName -InheritingType $complexType.Name -BaseTypeName $complexType.BaseType -MetadataUri $Metadata.Uri - } - - [ODataUtils.EntityTypeV4] $newType = ParseMetadataTypeDefinition $complexType $baseType $Metadata $schema.Namespace -Alias $Alias $false $complexType.BaseType - $Metadata.ComplexTypes += $newType - AddDerivedTypes $newType $entityAndComplexTypesQueue $metadata $schema.Namespace - } -} - -######################################################### -# Processes TypeDefinition from plain text xml metadata -# into our custom structure -######################################################### -function ParseTypeDefinitions -{ - param - ( - [System.Xml.XmlElement] $SchemaXML, - [ODataUtils.MetadataV4] $Metadata, - [System.Collections.ArrayList] $GlobalMetadata, - [string] $CustomNamespace, - [AllowEmptyString()] - [string] $Alias - ) - - if($null -eq $SchemaXML) { throw ($LocalizedData.ArguementNullError -f "SchemaXML", "ParseTypeDefinitions") } - - - foreach ($typeDefinition in $SchemaXML.TypeDefinition) - { - $newType = [ODataUtils.EntityTypeV4] @{ - "Namespace" = $Metadata.Namespace; - "Alias" = $Metadata.Alias; - "Name" = $typeDefinition.Name; - "BaseTypeStr" = $typeDefinition.UnderlyingType; - } - $Metadata.TypeDefinitions += $newType - } -} - -######################################################### -# Processes EnumTypes from plain text xml metadata -# into our custom structure -######################################################### -function ParseEnumTypes -{ - param - ( - [System.Xml.XmlElement] $SchemaXML, - [ODataUtils.MetadataV4] $Metadata - ) - - if($null -eq $SchemaXML) { throw ($LocalizedData.ArguementNullError -f "SchemaXML", "ParseEnumTypes") } - - foreach ($enum in $SchemaXML.EnumType) - { - $newEnumType = [ODataUtils.EnumType] @{ - "Namespace" = $Metadata.Namespace; - "Alias" = $Metadata.Alias; - "Name" = $enum.Name; - "UnderlyingType" = $enum.UnderlyingType; - "IsFlags" = $enum.IsFlags; - "Members" = @() - } - - if (!$newEnumType.UnderlyingType) - { - # If no type specified set the default type which is Edm.Int32 - $newEnumType.UnderlyingType = "Edm.Int32" - } - - if ($null -eq $newEnumType.IsFlags) - { - # If no value is specified for IsFlags, its value defaults to false. - $newEnumType.IsFlags = $false - } - - $enumValue = 0 - $currentEnumValue = 0 - - # Now parse EnumType elements - foreach ($element in $enum.Member) - { - - if ($element.Value -eq "" -and $newEnumType.IsFlags -eq $true) - { - # When IsFlags set to true each edm:Member element MUST specify a non-negative integer Value in the value attribute - $errorMessage = ($LocalizedData.InValidMetadata) - $detailedErrorMessage = "When IsFlags set to true each edm:Member element MUST specify a non-negative integer Value in the value attribute in " + $newEnumType.Name + " EnumType" - $exception = [System.InvalidOperationException]::new($errorMessage, $detailedErrorMessage) - $errorRecord = CreateErrorRecordHelper "InValidMetadata" $null ([System.Management.Automation.ErrorCategory]::InvalidData) $detailedErrorMessage nu - $PSCmdlet.ThrowTerminatingError($errorRecord) - } - elseif (($null -eq $element.Value) -or ($element.Value.GetType().Name -eq "Int32" -and $element.Value -eq "")) - { - # If no values are specified, the members are assigned consecutive integer values in the order of their appearance, - # starting with zero for the first member. - $currentEnumValue = $enumValue - } - else - { - $currentEnumValue = $element.Value - } - - $tmp = [ODataUtils.EnumMember] @{ - "Name" = $element.Name; - "Value" = $currentEnumValue; - } - - $newEnumType.Members += $tmp - $enumValue++ - } - - $Metadata.EnumTypes += $newEnumType - } -} - -######################################################### -# Processes SingletonTypes from plain text xml metadata -# into our custom structure -######################################################### -function ParseSingletonTypes -{ - param - ( - [System.Xml.XmlElement] $SchemaEntityContainerXML, - [ODataUtils.MetadataV4] $Metadata - ) - - if($null -eq $SchemaEntityContainerXML) { throw ($LocalizedData.ArguementNullError -f "SchemaEntityContainerXML", "ParseSingletonTypes") } - - foreach ($singleton in $SchemaEntityContainerXML.Singleton) - { - $navigationPropertyBindings = @() - - foreach ($navigationPropertyBinding in $singleton.NavigationPropertyBinding) - { - $tmp = [ODataUtils.NavigationPropertyBinding] @{ - "Path" = $navigationPropertyBinding.Path; - "Target" = $navigationPropertyBinding.Target; - } - - $navigationPropertyBindings += $tmp - } - - $newSingletonType = [ODataUtils.SingletonType] @{ - "Namespace" = $Metadata.Namespace; - "Alias" = $Metadata.Alias; - "Name" = $singleton.Name; - "Type" = $singleton.Type; - "NavigationPropertyBindings" = $navigationPropertyBindings; - } - - $Metadata.SingletonTypes += $newSingletonType - } -} - -######################################################### -# Processes EntitySets from plain text xml metadata -# into our custom structure -######################################################### -function ParseEntitySets -{ - param - ( - [System.Xml.XmlElement] $SchemaEntityContainerXML, - [ODataUtils.MetadataV4] $Metadata, - [string] $Namespace, - [AllowEmptyString()] - [string] $Alias - ) - - if($null -eq $SchemaEntityContainerXML) { throw ($LocalizedData.ArguementNullError -f "SchemaEntityContainerXML", "ParseEntitySets") } - - $entityTypeToEntitySetMapping = @{}; - foreach ($entitySet in $SchemaEntityContainerXML.EntitySet) - { - $entityType = $metadata.EntityTypes | Where-Object { $_.Name -eq $entitySet.EntityType.Split('.')[-1] } - $entityTypeName = $entityType.Name - - if($entityTypeToEntitySetMapping.ContainsKey($entityTypeName)) - { - $existingEntitySetName = $entityTypeToEntitySetMapping[$entityTypeName] - throw ($LocalizedData.EntityNameConflictError -f $entityTypeName, $existingEntitySetName, $entitySet.Name, $entityTypeName ) - } - else - { - $entityTypeToEntitySetMapping.Add($entityTypeName, $entitySet.Name) - } - - $newEntitySet = [ODataUtils.EntitySetV4] @{ - "Namespace" = $Namespace; - "Alias" = $Alias; - "Name" = $entitySet.Name; - "Type" = $entityType; - } - - $Metadata.EntitySets += $newEntitySet - } -} - -######################################################### -# Processes Actions from plain text xml metadata -# into our custom structure -######################################################### -function ParseActions -{ - param - ( - [System.Object[]] $SchemaActionsXML, - [ODataUtils.MetadataV4] $Metadata - ) - - if($null -eq $SchemaActionsXML) { throw ($LocalizedData.ArguementNullError -f "SchemaActionsXML", "ParseActions") } - - foreach ($action in $SchemaActionsXML) - { - # HttpMethod is only used for legacy Service Operations - if ($null -eq $action.HttpMethod) - { - $newAction = [ODataUtils.ActionV4] @{ - "Namespace" = $Metadata.Namespace; - "Alias" = $Metadata.Alias; - "Name" = $action.Name; - "Action" = $Metadata.Namespace + '.' + $action.Name; - } - - # Actions are always SideEffecting, otherwise it's an OData function - foreach ($parameter in $action.Parameter) - { - if ($null -ne $parameter.Nullable) - { - $parameterIsNullable = [System.Convert]::ToBoolean($parameter.Nullable); - } - else - { - $parameterIsNullable = $true - } - - $newParameter = [ODataUtils.TypeProperty] @{ - "Name" = $parameter.Name; - "TypeName" = $parameter.Type; - "IsNullable" = $parameterIsNullable; - } - - $newAction.Parameters += $newParameter - } - - if ($null -ne $action.EntitySet) - { - $newAction.EntitySet = $metadata.EntitySets | Where-Object { $_.Name -eq $action.EntitySet } - } - - $Metadata.Actions += $newAction - } - } -} - -######################################################### -# Processes Functions from plain text xml metadata -# into our custom structure -######################################################### -function ParseFunctions -{ - param - ( - [System.Object[]] $SchemaFunctionsXML, - [ODataUtils.MetadataV4] $Metadata - ) - - if($null -eq $SchemaFunctionsXML) { throw ($LocalizedData.ArguementNullError -f "SchemaFunctionsXML", "ParseFunctions") } - - foreach ($function in $SchemaFunctionsXML) - { - # HttpMethod is only used for legacy Service Operations - if ($null -eq $function.HttpMethod) - { - $newFunction = [ODataUtils.FunctionV4] @{ - "Namespace" = $Metadata.Namespace; - "Alias" = $Metadata.Alias; - "Name" = $function.Name; - "Function" = $Metadata.Namespace + '.' + $function.Name; - "EntitySet" = $function.EntitySetPath; - "ReturnType" = $function.ReturnType; - } - - # Future TODO - consider removing this hack once all the service we run against fix this issue - # Hack - sometimes service does not return ReturnType, however this information can be found in InnerXml - if ($newFunction.ReturnType -eq '' -or $newFunction.ReturnType -eq 'System.Xml.XmlElement') - { - try - { - [xml] $innerXML = '' + $function.InnerXml + '' - $newFunction.Returntype = $innerXML.Params.ReturnType.Type - } - catch - { - # Do nothing - } - } - - # Keep only EntityType name - $newFunction.ReturnType = $newFunction.ReturnType.Replace('Collection(', '') - $newFunction.ReturnType = $newFunction.ReturnType.Replace(')', '') - - # Actions are always SideEffecting, otherwise it's an OData function - foreach ($parameter in $function.Parameter) - { - if ($null -ne $parameter.Nullable) - { - $parameterIsNullable = [System.Convert]::ToBoolean($parameter.Nullable); - } - - $newParameter = [ODataUtils.Parameter] @{ - "Name" = $parameter.Name; - "Type" = $parameter.Type; - "Nullable" = $parameterIsNullable; - } - - $newFunction.Parameters += $newParameter - } - - $Metadata.Functions += $newFunction - } - } -} - -######################################################### -# Processes plain text xml metadata (OData V4 schema version) into our custom structure -# MetadataSet contains all parsed so far referenced Metadatas (for base class lookup) -######################################################### -function ParseMetadata -{ - param - ( - [xml] $MetadataXML, - [string] $ODataVersion, - [string] $MetadataUri, - [string] $Uri, - [System.Collections.ArrayList] $MetadataSet - ) - - if($null -eq $MetadataXML) { throw ($LocalizedData.ArguementNullError -f "MetadataXML", "ParseMetadata") } - - # This is a processing queue for those types that require base types that haven't been defined yet - $entityAndComplexTypesQueue = @{} - [System.Collections.ArrayList] $metadatas = [System.Collections.ArrayList]::new() - - foreach ($schema in $MetadataXML.Edmx.DataServices.Schema) - { - if ($null -eq $schema) - { - Write-Error $LocalizedData.EmptySchema - continue - } - - [ODataUtils.MetadataV4] $metadata = [ODataUtils.MetadataV4]::new() - $metadata.ODataVersion = $ODataVersion - $metadata.MetadataUri = $MetadataUri - $metadata.Uri = $Uri - $metadata.Namespace = $schema.Namespace - $metadata.Alias = $schema.Alias - - ParseEntityTypes -SchemaXML $schema -metadata $metadata -GlobalMetadata $MetadataSet -EntityAndComplexTypesQueue $entityAndComplexTypesQueue -CustomNamespace $CustomNamespace -Alias $metadata.Alias - ParseComplexTypes -SchemaXML $schema -metadata $metadata -GlobalMetadata $MetadataSet -EntityAndComplexTypesQueue $entityAndComplexTypesQueue -CustomNamespace $CustomNamespace -Alias $metadata.Alias - ParseTypeDefinitions -SchemaXML $schema -metadata $metadata -GlobalMetadata $MetadataSet -CustomNamespace $CustomNamespace -Alias $metadata.Alias - ParseEnumTypes -SchemaXML $schema -metadata $metadata - - foreach ($entityContainer in $schema.EntityContainer) - { - if ($entityContainer.IsDefaultEntityContainer) - { - $metadata.DefaultEntityContainerName = $entityContainer.Name - } - - ParseSingletonTypes -SchemaEntityContainerXML $entityContainer -Metadata $metadata - ParseEntitySets -SchemaEntityContainerXML $entityContainer -Metadata $metadata -Namespace $schema.Namespace -Alias $schema.Alias - } - - if ($schema.Action) - { - ParseActions -SchemaActionsXML $schema.Action -Metadata $metadata - } - - if ($schema.Function) - { - ParseFunctions -SchemaFunctionsXML $schema.Function -Metadata $metadata - } - - # In this call we check if the Namespace or Alias have to be normalized because it has invalid combination of dots and numbers. - # Note: In ParseEntityTypes and ParseComplexTypes we check for scenario where namespace/alias collide with inheriting class name. - NormalizeNamespace $metadata.Namespace $metadata.Uri $script:normalizedNamespaces $false - NormalizeNamespace $metadata.Alias $metadata.Uri $script:normalizedNamespaces $false - - $metadatas.Add($metadata) | Out-Null - } - - $metadatas -} - -######################################################### -# Verifies processed metadata for correctness -######################################################### -function VerifyMetadata -{ - param - ( - [System.Collections.ArrayList] $metadataSet, - [boolean] $allowClobber, - $callerPSCmdlet, - [string] $progressBarStatus - ) - - if($null -eq $callerPSCmdlet) { throw ($LocalizedData.ArguementNullError -f "PSCmdlet", "VerifyMetaData") } - if($null -eq $progressBarStatus) { throw ($LocalizedData.ArguementNullError -f "ProgressBarStatus", "VerifyMetaData") } - - Write-Verbose $LocalizedData.VerboseVerifyingMetadata - - $reservedProperties = @("Filter", "OrderBy", "Skip", "Top", "ConnectionUri", "CertificateThumbPrint", "Credential") - $validEntitySets = @() - $sessionCommands = Get-Command -All - - - foreach ($metadata in $metadataSet) - { - foreach ($entitySet in $metadata.EntitySets) - { - if ($null -eq $entitySet.Type) - { - $errorMessage = ($LocalizedData.EntitySetUndefinedType -f $metadata.MetadataUri, $entitySet.Name) - $exception = [System.InvalidOperationException]::new($errorMessage) - $errorRecord = CreateErrorRecordHelper "ODataEndpointProxyInvalidMetaDataUri" $null ([System.Management.Automation.ErrorCategory]::InvalidArgument) $exception $metadata.MetadataUri - $callerPSCmdlet.ThrowTerminatingError($errorRecord) - } - - $hasConflictingProperty = $false - $hasConflictingCommand = $false - $entityAndNavigationProperties = $entitySet.Type.EntityProperties + $entitySet.Type.NavigationProperties - foreach($entityProperty in $entityAndNavigationProperties) - { - if($reservedProperties.Contains($entityProperty.Name)) - { - $hasConflictingProperty = $true - if(!$allowClobber) - { - # Write Error message and skip current Entity Set. - $errorMessage = ($LocalizedData.SkipEntitySetProxyCreation -f $entitySet.Name, $entitySet.Type.Name, $entityProperty.Name) - $exception = [System.InvalidOperationException]::new($errorMessage) - $errorRecord = CreateErrorRecordHelper "ODataEndpointDefaultPropertyCollision" $null ([System.Management.Automation.ErrorCategory]::InvalidOperation) $exception $metadata.MetadataUri - $callerPSCmdlet.WriteError($errorRecord) - } - else - { - $warningMessage = ($LocalizedData.EntitySetProxyCreationWithWarning -f $entitySet.Name, $entityProperty.Name, $entitySet.Type.Name) - $callerPSCmdlet.WriteWarning($warningMessage) - } - } - } - - foreach($currentCommand in $sessionCommands) - { - if(($null -ne $currentCommand.Noun -and $currentCommand.Noun -eq $entitySet.Name) -and - ($currentCommand.Verb -eq "Get" -or - $currentCommand.Verb -eq "Set" -or - $currentCommand.Verb -eq "New" -or - $currentCommand.Verb -eq "Remove")) - { - $hasConflictingCommand = $true - VerifyMetadataHelper $LocalizedData.SkipEntitySetConflictCommandCreation ` - $LocalizedData.EntitySetConflictCommandCreationWithWarning ` - $entitySet.Name $currentCommand.Name $metadata.MetadataUri $allowClobber $callerPSCmdlet - } - } - - foreach($currentAction in $metadata.Actions) - { - $actionCommand = "Invoke-" + "$($entitySet.Name)$($currentAction.Verb)" - - foreach($currentCommand in $sessionCommands) - { - if($actionCommand -eq $currentCommand.Name) - { - $hasConflictingCommand = $true - VerifyMetadataHelper $LocalizedData.SkipEntitySetConflictCommandCreation ` - $LocalizedData.EntitySetConflictCommandCreationWithWarning $entitySet.Name ` - $currentCommand.Name $metadata.MetadataUri $allowClobber $callerPSCmdlet - } - } - } - - if(!($hasConflictingProperty -or $hasConflictingCommand)-or $allowClobber) - { - $validEntitySets += $entitySet - } - } - - $metadata.EntitySets = $validEntitySets - - $validServiceActions = @() - $hasConflictingServiceActionCommand - foreach($currentAction in $metadata.Actions) - { - $serviceActionCommand = "Invoke-" + "$($currentAction.Verb)" - - foreach($currentCommand in $sessionCommands) - { - if($serviceActionCommand -eq $currentCommand.Name) - { - $hasConflictingServiceActionCommand = $true - VerifyMetadataHelper $LocalizedData.SkipConflictServiceActionCommandCreation ` - $LocalizedData.ConflictServiceActionCommandCreationWithWarning $entitySet.Name ` - $currentCommand.Name $metadata.MetadataUri $allowClobber $callerPSCmdlet - } - } - - if(!$hasConflictingServiceActionCommand -or $allowClobber) - { - $validServiceActions += $currentAction - } - } - - $metadata.Actions = $validServiceActions - } - - # Update Progress bar. - ProgressBarHelper "Export-ODataEndpointProxy" $progressBarStatus 5 20 1 1 -} - -######################################################### -# Takes xml definition of a class from metadata document, -# plus existing metadata structure and finds its base class -######################################################### -function GetBaseType -{ - param - ( - [System.Xml.XmlElement] $MetadataEntityDefinition, - [ODataUtils.MetadataV4] $Metadata, - [string] $Namespace, - [System.Collections.ArrayList] $GlobalMetadata - ) - - if ($null -ne $metadataEntityDefinition -and - $null -ne $metaData -and - $null -ne $MetadataEntityDefinition.BaseType) - { - $baseType = $Metadata.EntityTypes | Where-Object { $_.Namespace + "." + $_.Name -eq $MetadataEntityDefinition.BaseType -or $_.Alias + "." + $_.Name -eq $MetadataEntityDefinition.BaseType } - if ($null -eq $baseType) - { - $baseType = $Metadata.ComplexTypes | Where-Object { $_.Namespace + "." + $_.Name -eq $MetadataEntityDefinition.BaseType -or $_.Alias + "." + $_.Name -eq $MetadataEntityDefinition.BaseType } - } - - if ($null -eq $baseType) - { - # Look in other metadatas, since the class can be defined in referenced metadata - foreach ($referencedMetadata in $GlobalMetadata) - { - if ($null -ne ($baseType = $referencedMetadata.EntityTypes | Where-Object { $_.Namespace + "." + $_.Name -eq $MetadataEntityDefinition.BaseType -or $_.Alias + "." + $_.Name -eq $MetadataEntityDefinition.BaseType }) -or - $null -ne ($baseType = $referencedMetadata.ComplexTypes | Where-Object { $_.Namespace + "." + $_.Name -eq $MetadataEntityDefinition.BaseType -or $_.Alias + "." + $_.Name -eq $MetadataEntityDefinition.BaseType })) - { - # Found base class - break - } - } - } - } - - if ($null -ne $baseType) - { - $baseType[0] - } -} - -######################################################### -# Takes base class name and global metadata structure -# and finds its base class -######################################################### -function GetBaseTypeByName -{ - param - ( - [String] $BaseTypeStr, - [System.Collections.ArrayList] $GlobalMetadata - ) - - if ($null -ne $BaseTypeStr) - { - - # Look for base class definition in all referenced metadatas (including entry point) - foreach ($referencedMetadata in $GlobalMetadata) - { - if ($null -ne ($baseType = $referencedMetadata.EntityTypes | Where-Object { $_.Namespace + "." + $_.Name -eq $BaseTypeStr -or $_.Alias + "." + $_.Name -eq $BaseTypeStr }) -or - $null -ne ($baseType = $referencedMetadata.ComplexTypes | Where-Object { $_.Namespace + "." + $_.Name -eq $BaseTypeStr -or $_.Alias + "." + $_.Name -eq $BaseTypeStr })) - { - # Found base class - break - } - } - } - - if ($null -ne $baseType) - { - $baseType[0] - } - else - { - $null - } -} - -######################################################### -# Processes derived types of a newly added type, -# that were previously waiting in the queue -######################################################### -function AddDerivedTypes { - param( - [ODataUtils.EntityTypeV4] $baseType, - $entityAndComplexTypesQueue, - [ODataUtils.MetadataV4] $metadata, - [string] $namespace - ) - - if($null -eq $baseType) { throw ($LocalizedData.ArguementNullError -f "BaseType", "AddDerivedTypes") } - if($null -eq $entityAndComplexTypesQueue) { throw ($LocalizedData.ArguementNullError -f "EntityAndComplexTypesQueue", "AddDerivedTypes") } - if($null -eq $namespace) { throw ($LocalizedData.ArguementNullError -f "Namespace", "AddDerivedTypes") } - - $baseTypeFullName = $baseType.Namespace + '.' + $baseType.Name - $baseTypeShortName = $baseType.Alias + '.' + $baseType.Name - - if ($entityAndComplexTypesQueue.ContainsKey($baseTypeFullName) -or $entityAndComplexTypesQueue.ContainsKey($baseTypeShortName)) - { - $types = $entityAndComplexTypesQueue[$baseTypeFullName] + $entityAndComplexTypesQueue[$baseTypeShortName] - - foreach ($type in $types) - { - if ($type.type -eq 'EntityType') - { - $newType = ParseMetadataTypeDefinition ($type.value) $baseType $metadata $namespace $true - $metadata.EntityTypes += $newType - } - else - { - $newType = ParseMetadataTypeDefinition ($type.value) $baseType $metadata $namespace $false - $metadata.ComplexTypes += $newType - } - - AddDerivedTypes $newType $entityAndComplexTypesQueue $metadata $namespace - } - } -} - -######################################################### -# Parses types definitions element of metadata xml -######################################################### -function ParseMetadataTypeDefinitionHelper -{ - param - ( - [System.Xml.XmlElement] $metadataEntityDefinition, - [ODataUtils.EntityTypeV4] $baseType, - [string] $baseTypeStr, - [ODataUtils.MetadataV4] $metadata, - [string] $namespace, - [AllowEmptyString()] - [string] $alias, - [bool] $isEntity - ) - - if($null -eq $metadataEntityDefinition) { throw ($LocalizedData.ArguementNullError -f "MetadataEntityDefinition", "ParseMetadataTypeDefinition") } - if($null -eq $namespace) { throw ($LocalizedData.ArguementNullError -f "Namespace", "ParseMetadataTypeDefinition") } - - [ODataUtils.EntityTypeV4] $newEntityType = CreateNewEntityType -metadataEntityDefinition $metadataEntityDefinition -baseType $baseType -baseTypeStr $baseTypeStr -namespace $namespace -alias $alias -isEntity $isEntity - - if ($null -ne $baseType) - { - # Add properties inherited from BaseType - ParseMetadataBaseTypeDefinitionHelper $newEntityType $baseType - } - - # properties defined on EntityType - $newEntityType.EntityProperties += $metadataEntityDefinition.Property | ForEach-Object { - if ($null -ne $_) - { - if ($null -ne $_.Nullable) - { - $newPropertyIsNullable = [System.Convert]::ToBoolean($_.Nullable) - } - else - { - $newPropertyIsNullable = $true - } - - [ODataUtils.TypeProperty] @{ - "Name" = $_.Name; - "TypeName" = $_.Type; - "IsNullable" = $newPropertyIsNullable; - } - } - } - - # odataId property will be inherited from base type, if it exists. - # Otherwise, it should be added to current type - if ($null -eq $baseType) - { - # @odata.Id property (renamed to odataId) is required for dynamic Uri creation - # This property is only available when user executes auto-generated cmdlet with -AllowAdditionalData, - # but ODataAdapter needs it to construct Uri to access navigation properties. - # Thus, we need to fetch this info for scenario when -AllowAdditionalData isn't used. - $newEntityType.EntityProperties += [ODataUtils.TypeProperty] @{ - "Name" = "odataId"; - "TypeName" = "Edm.String"; - "IsNullable" = $True; - } - } - - # Property name can't be identical to entity type name. - # If such property exists, "Property" suffix will be added to its name. - foreach ($property in $newEntityType.EntityProperties) - { - if ($property.Name -eq $newEntityType.Name) - { - $property.Name += "Property" - } - } - - if ($null -ne $metadataEntityDefinition -and $null -ne $metadataEntityDefinition.Key) - { - foreach ($entityTypeKey in $metadataEntityDefinition.Key.PropertyRef) - { - ($newEntityType.EntityProperties | Where-Object { $_.Name -eq $entityTypeKey.Name }).IsKey = $true - } - } - - $newEntityType -} - -######################################################### -# Add base class entity and navigation properties to inheriting class -######################################################### -function ParseMetadataBaseTypeDefinitionHelper -{ - param - ( - [ODataUtils.EntityTypeV4] $EntityType, - [ODataUtils.EntityTypeV4] $BaseType - ) - - if ($null -ne $EntityType -and $null -ne $BaseType) - { - # Add properties inherited from BaseType - $EntityType.EntityProperties += $BaseType.EntityProperties - $EntityType.NavigationProperties += $BaseType.NavigationProperties - } -} - -######################################################### -# Create new EntityType object -######################################################### -function CreateNewEntityType -{ - param - ( - [System.Xml.XmlElement] $metadataEntityDefinition, - [ODataUtils.EntityTypeV4] $baseType, - [string] $baseTypeStr, - [string] $namespace, - [AllowEmptyString()] - [string] $alias, - [bool] $isEntity - ) - $newEntityType = [ODataUtils.EntityTypeV4] @{ - "Namespace" = $namespace; - "Alias" = $alias; - "Name" = $metadataEntityDefinition.Name; - "IsEntity" = $isEntity; - "BaseType" = $baseType; - "BaseTypeStr" = $baseTypeStr; - } - - $newEntityType -} - -######################################################### -# Parses navigation properties from metadata xml -######################################################### -function ParseMetadataTypeDefinitionNavigationProperties -{ - param - ( - [System.Xml.XmlElement] $metadataEntityDefinition, - [ODataUtils.EntityTypeV4] $entityType - ) - - # navigation properties defined on EntityType - $newEntityType.NavigationProperties = @{} - $newEntityType.NavigationProperties.Clear() - - foreach ($navigationProperty in $metadataEntityDefinition.NavigationProperty) - { - $tmp = [ODataUtils.NavigationPropertyV4] @{ - "Name" = $navigationProperty.Name; - "Type" = $navigationProperty.Type; - "Nullable" = $navigationProperty.Nullable; - "Partner" = $navigationProperty.Partner; - "ContainsTarget" = $navigationProperty.ContainsTarget; - "OnDelete" = $navigationProperty.OnDelete; - } - - $referentialConstraints = @{} - foreach ($constraint in $navigationProperty.ReferentialConstraints) - { - $tmp = [ODataUtils.ReferencedConstraint] @{ - "Property" = $constraint.Property; - "ReferencedProperty" = $constraint.ReferencedProperty; - } - } - - $newEntityType.NavigationProperties += $tmp - } -} - -######################################################### -# Parses types definitions element of metadata xml for OData V4 schema -######################################################### -function ParseMetadataTypeDefinition -{ - param - ( - [System.Xml.XmlElement] $metadataEntityDefinition, - [ODataUtils.EntityTypeV4] $baseType, - [ODataUtils.MetadataV4] $metadata, - [string] $namespace, - [AllowEmptyString()] - [string] $alias, - [bool] $isEntity, - [string] $baseTypeStr - ) - - if($null -eq $metadataEntityDefinition) { throw ($LocalizedData.ArguementNullError -f "MetadataEntityDefinition", "ParseMetadataTypeDefinition") } - if($null -eq $namespace) { throw ($LocalizedData.ArguementNullError -f "Namespace", "ParseMetadataTypeDefinition") } - - [ODataUtils.EntityTypeV4] $newEntityType = ParseMetadataTypeDefinitionHelper -metadataEntityDefinition $metadataEntityDefinition -baseType $baseType -baseTypeStr $baseTypeStr -metadata $metadata -namespace $namespace -alias $alias -isEntity $isEntity - ParseMetadataTypeDefinitionNavigationProperties -metadataEntityDefinition $metadataEntityDefinition -entityType $newEntityType - - $newEntityType -} - -######################################################### -# Create psd1 and cdxml files required to auto-generate -# cmdlets for given service. -######################################################### -function GenerateClientSideProxyModule -{ - param - ( - [System.Collections.ArrayList] $GlobalMetadata, - [ODataUtils.ODataEndpointProxyParameters] $ODataEndpointProxyParameters, - [string] $OutputModule, - [string] $CreateRequestMethod, - [string] $UpdateRequestMethod, - [string] $CmdletAdapter, - [Hashtable] $resourceNameMappings, - [Hashtable] $CustomData, - [string] $UriResourcePathKeyFormat, - [string] $progressBarStatus, - $NormalizedNamespaces - ) - - if($null -eq $progressBarStatus) { throw ($LocalizedData.ArguementNullError -f "ProgressBarStatus", "GenerateClientSideProxyModule") } - - Write-Verbose ($LocalizedData.VerboseSavingModule -f $OutputModule) - - # Save ComplexTypes for all metadata schemas in single file - $typeDefinitionFileName = "ComplexTypeDefinitions.psm1" - $complexTypeMapping = GenerateComplexTypeDefinition $GlobalMetadata $OutputModule $typeDefinitionFileName $NormalizedNamespaces - - ProgressBarHelper "Export-ODataEndpointProxy" $progressBarStatus 20 20 1 1 - - $actions = @() - $functions = @() - - $currentEntryCount = 0 - foreach ($Metadata in $GlobalMetadata) - { - foreach ($entitySet in $Metadata.EntitySets) - { - $currentEntryCount += 1 - SaveCDXML $entitySet $Metadata $GlobalMetadata $Metadata.Uri $OutputModule $CreateRequestMethod $UpdateRequestMethod $CmdletAdapter $resourceNameMappings $CustomData $complexTypeMapping $UriResourcePathKeyFormat $NormalizedNamespaces - - ProgressBarHelper "Export-ODataEndpointProxy" $progressBarStatus 40 20 $Metadata.EntitySets.Count $currentEntryCount - } - - $currentEntryCount = 0 - foreach ($singleton in $Metadata.SingletonTypes) - { - $currentEntryCount += 1 - SaveCDXMLSingletonCmdlets $singleton $Metadata $GlobalMetadata $Metadata.Uri $OutputModule $CreateRequestMethod $UpdateRequestMethod $CmdletAdapter $resourceNameMappings $CustomData $complexTypeMapping $NormalizedNamespaces - - ProgressBarHelper "Export-ODataEndpointProxy" $progressBarStatus 40 20 $Metadata.Singletons.Count $currentEntryCount - } - - $actions += $Metadata.Actions | Where-Object { $_.EntitySet -eq '' -or $null -eq $_.EntitySet } - $functions += $Metadata.Functions | Where-Object { $_.EntitySet -eq '' -or $null -eq $_.EntitySet} - } - - if ($actions.Count -gt 0 -or $functions.Count -gt 0) - { - # Save Service Actions for all metadata schemas in single file - SaveServiceActionsCDXML $GlobalMetadata $ODataEndpointProxyParameters "$OutputModule\ServiceActions.cdxml" $complexTypeMapping $progressBarStatus $CmdletAdapter - } - - $moduleDirInfo = [System.IO.DirectoryInfo]::new($OutputModule) - $moduleManifestName = $moduleDirInfo.Name + ".psd1" - - if ($actions.Count -gt 0 -or $functions.Count -gt 0) - { - $additionalModules = @($typeDefinitionFileName, 'ServiceActions.cdxml') - } - else - { - $additionalModules = @($typeDefinitionFileName) - } - - GenerateModuleManifest $GlobalMetadata $OutputModule\$moduleManifestName $additionalModules $resourceNameMappings $progressBarStatus -} - -######################################################### -# Generates CDXML module for a specific OData EntitySet -######################################################### -function SaveCDXML -{ - param - ( - [ODataUtils.EntitySetV4] $EntitySet, - [ODataUtils.MetadataV4] $Metadata, - [System.Collections.ArrayList] $GlobalMetadata, - [string] $Uri, - [string] $OutputModule, - [string] $CreateRequestMethod, - [string] $UpdateRequestMethod, - [string] $CmdletAdapter, - [Hashtable] $resourceNameMappings, - [Hashtable] $CustomData, - [Hashtable] $complexTypeMapping, - [string] $UriResourcePathKeyFormat, - $normalizedNamespaces - ) - - if($null -eq $EntitySet) { throw ($LocalizedData.ArguementNullError -f "EntitySet", "GenerateClientSideProxyModule") } - if($null -eq $Metadata) { throw ($LocalizedData.ArguementNullError -f "metadata", "GenerateClientSideProxyModule") } - - $entitySetName = $EntitySet.Name - if(($null -ne $resourceNameMappings) -and - $resourceNameMappings.ContainsKey($entitySetName)) - { - $entitySetName = $resourceNameMappings[$entitySetName] - } - else - { - $entitySetName = $EntitySet.Type.Name - } - - $Path = "$OutputModule\$entitySetName.cdxml" - - $xmlWriter = New-Object System.XMl.XmlTextWriter($Path,$Null) - - if ($null -eq $xmlWriter) - { - throw ($LocalizedData.XmlWriterInitializationError -f $EntitySet.Name) - } - - $xmlWriter = SaveCDXMLHeader $xmlWriter $Uri $EntitySet.Name $entitySetName $CmdletAdapter - - # Get the keys - $keys = $EntitySet.Type.EntityProperties | Where-Object { $_.IsKey } - - $navigationProperties = $EntitySet.Type.NavigationProperties - - SaveCDXMLInstanceCmdlets $xmlWriter $Metadata $GlobalMetadata $EntitySet.Type $keys $navigationProperties $CmdletAdapter $complexTypeMapping $false - - $nonKeyProperties = $EntitySet.Type.EntityProperties | Where-Object { -not $_.isKey } - $nullableProperties = $nonKeyProperties | Where-Object { $_.isNullable } - $nonNullableProperties = $nonKeyProperties | Where-Object { -not $_.isNullable } - - $xmlWriter.WriteStartElement('StaticCmdlets') - - $keyProperties = $keys - - SaveCDXMLNewCmdlet $xmlWriter $Metadata $GlobalMetadata $keyProperties $nonNullableProperties $nullableProperties $navigationProperties $CmdletAdapter $complexTypeMapping - - GenerateSetProxyCmdlet $xmlWriter $keyProperties $nonKeyProperties $complexTypeMapping - - SaveCDXMLRemoveCmdlet $xmlWriter $Metadata $GlobalMetadata $keyProperties $navigationProperties $CmdletAdapter $complexTypeMapping - - $entityActions = $Metadata.Actions | Where-Object { ($_.EntitySet.Namespace -eq $EntitySet.Namespace) -and ($_.EntitySet.Name -eq $EntitySet.Name) } - - if ($entityActions.Length -gt 0) - { - foreach($action in $entityActions) - { - $xmlWriter = SaveCDXMLAction $xmlWriter $Metadata $action $EntitySet.Name $true $keys $complexTypeMapping - } - } - - $entityFunctions = $Metadata.Functions | Where-Object { ($_.EntitySet.Namespace -eq $EntitySet.Namespace) -and ($_.EntitySet.Name -eq $EntitySet.Name) } - - if ($entityFunctions.Length -gt 0) - { - foreach($function in $entityFunctions) - { - $xmlWriter = SaveCDXMLFunction $xmlWriter $Metadata $function $EntitySet.Name $true $keys $complexTypeMapping - } - } - - $xmlWriter.WriteEndElement() - - $normalizedDotNetNamespace = GetNamespace $EntitySet.Type.Namespace $normalizedNamespaces - $normalizedDotNetAlias = GetNamespace $EntitySet.Alias $normalizedNamespaces - $normalizedDotNetEntitySetNamespace = $normalizedDotNetNamespace - - $xmlWriter.WriteStartElement('CmdletAdapterPrivateData') - - $xmlWriter.WriteStartElement('Data') - $xmlWriter.WriteAttributeString('Name', 'EntityTypeName') - $xmlWriter.WriteString("$($normalizedDotNetNamespace).$($EntitySet.Type.Name)") - $xmlWriter.WriteEndElement() - $xmlWriter.WriteStartElement('Data') - $xmlWriter.WriteAttributeString('Name', 'EntityTypeAliasName') - if (!$EntitySet.Alias) - { - $xmlWriter.WriteString("") - } - else - { - $xmlWriter.WriteString("$($normalizedDotNetAlias).$($EntitySet.Type.Name)") - } - $xmlWriter.WriteEndElement() - $xmlWriter.WriteStartElement('Data') - $xmlWriter.WriteAttributeString('Name', 'EntitySetName') - $xmlWriter.WriteString("$($normalizedDotNetEntitySetNamespace).$($EntitySet.Name)") - $xmlWriter.WriteEndElement() - - # Add URI resource path format (webservice.svc/ResourceName/ResourceId vs webservice.svc/ResourceName(QueryKeyName=ResourceId)) - if ($null -ne $UriResourcePathKeyFormat -and $UriResourcePathKeyFormat -ne [string]::Empty) - { - $xmlWriter.WriteStartElement('Data') - $xmlWriter.WriteAttributeString('Name', 'UriResourcePathKeyFormat') - $xmlWriter.WriteString("$UriResourcePathKeyFormat") - $xmlWriter.WriteEndElement() - } - - # Add information about navigation properties and their types - # Used in scenario where user requests navigation property in -Select query - foreach ($navProperty in $navigationProperties) - { - if ($navProperty) - { - $xmlWriter.WriteStartElement('Data') - $xmlWriter.WriteAttributeString('Name', $navProperty.Name + 'NavigationProperty') - $xmlWriter.WriteString($navProperty.Type) - $xmlWriter.WriteEndElement() - } - } - - # Add CreateRequestMethod and UpdateRequestMethod to privateData - $xmlWriter.WriteStartElement('Data') - $xmlWriter.WriteAttributeString('Name', 'CreateRequestMethod') - $xmlWriter.WriteString("$CreateRequestMethod") - $xmlWriter.WriteEndElement() - - $xmlWriter.WriteStartElement('Data') - $xmlWriter.WriteAttributeString('Name', 'UpdateRequestMethod') - $xmlWriter.WriteString("$UpdateRequestMethod") - $xmlWriter.WriteEndElement() - - $xmlWriter.WriteEndElement() - - SaveCDXMLFooter $xmlWriter - - Write-Verbose ($LocalizedData.VerboseSavedCDXML -f $($entitySetName), $Path) -} - -######################################################### -# Save Singleton Cmdlets to CDXML -######################################################### -function SaveCDXMLSingletonCmdlets -{ - param - ( - [ODataUtils.SingletonType] $Singleton, - [ODataUtils.MetadataV4] $Metadata, - [System.Collections.ArrayList] $GlobalMetadata, - [string] $Uri, - [string] $OutputModule, - [string] $CreateRequestMethod, - [string] $UpdateRequestMethod, - [string] $CmdletAdapter, - [Hashtable] $resourceNameMappings, - [Hashtable] $CustomData, - [Hashtable] $complexTypeMapping, - $normalizedNamespaces - ) - - if($null -eq $Singleton) { throw ($LocalizedData.ArguementNullError -f "Singleton", "SaveCDXMLSingletonCmdlets") } - if($null -eq $Metadata) { throw ($LocalizedData.ArguementNullError -f "Metadata", "SaveCDXMLSingletonCmdlets") } - - $singletonName = $singleton.Name - $singletonType = $singleton.Type - - $Path = "$OutputModule\$singletonName" + "Singleton" + ".cdxml" - - $xmlWriter = New-Object System.XMl.XmlTextWriter($Path,$Null) - - if ($null -eq $xmlWriter) - { - throw ($LocalizedData.XmlWriterInitializationError -f $singletonName) - } - - # Get associated EntityType - $associatedEntityType = $Metadata.EntityTypes | Where-Object { $_.Namespace + "." + $_.Name -eq $singletonType -or $_.Alias + "." + $_.Name -eq $singletonType} - - if ($null -eq $associatedEntityType) - { - # Look in other metadatas, since the class can be defined in referenced metadata - foreach ($referencedMetadata in $GlobalMetadata) - { - if ($null -ne ($associatedEntityType = $referencedMetadata.EntityTypes | Where-Object { $_.Namespace + "." + $_.Name -eq $singletonType -or $_.Alias + "." + $_.Name -eq $singletonType })) - { - # Found associated class - break - } - } - } - - if ($null -ne $associatedEntityType) - { - $xmlWriter = SaveCDXMLHeader $xmlWriter $Uri $singletonName $singletonName $CmdletAdapter - - if ($null -eq $associatedEntityType.BaseType -and $null -ne $associatedEntityType.BaseTypeStr -and $associatedEntityType.BaseTypeStr -ne '') - { - $associatedEntitybaseType = GetBaseTypeByName $associatedEntityType.BaseTypeStr $GlobalMetadata - - # Make another pass on base class to make sure its properties were added to associated entity type - ParseMetadataBaseTypeDefinitionHelper $associatedEntityType $associatedEntitybaseType - } - - # Get the keys depending on whether the url contains variables or not - $keys = $associatedEntityType.EntityProperties | Where-Object { $_.IsKey } - - $navigationProperties = $associatedEntityType.NavigationProperties - - SaveCDXMLInstanceCmdlets $xmlWriter $Metadata $GlobalMetadata $associatedEntityType $keys $navigationProperties $CmdletAdapter $complexTypeMapping $true - - $nonKeyProperties = $associatedEntityType.EntityProperties | Where-Object { -not $_.isKey } - $nullableProperties = $nonKeyProperties | Where-Object { $_.isNullable } - $nonNullableProperties = $nonKeyProperties | Where-Object { -not $_.isNullable } - - $xmlWriter.WriteStartElement('StaticCmdlets') - - $keyProperties = $keys - - GenerateSetProxyCmdlet $xmlWriter $keyProperties $nonKeyProperties $complexTypeMapping - - $entityActions = $Metadata.Actions | Where-Object { $_.EntitySet.Name -eq $associatedEntityType.Name } - - if ($entityActions.Length -gt 0) - { - foreach($action in $entityActions) - { - $xmlWriter = SaveCDXMLAction $xmlWriter $Metadata $action $EntitySet.Name $true $keys $complexTypeMapping - } - } - - $entityFunctions = $Metadata.Functions | Where-Object { $_.EntitySet.Name -eq $associatedEntityType.Name } - - if ($entityFunctions.Length -gt 0) - { - foreach($function in $entityFunctions) - { - $xmlWriter = SaveCDXMLFunction $xmlWriter $Metadata $function $associatedEntityType.Name $true $keys $complexTypeMapping - } - } - - $xmlWriter.WriteEndElement() - - $normalizedDotNetNamespace = GetNamespace $associatedEntityType.Namespace $normalizedNamespaces - $normalizedDotNetAlias = GetNamespace $associatedEntityType.Alias $normalizedNamespaces - - $xmlWriter.WriteStartElement('CmdletAdapterPrivateData') - - $xmlWriter.WriteStartElement('Data') - $xmlWriter.WriteAttributeString('Name', 'EntityTypeAliasName') - if (!$associatedEntityType.Alias) - { - $xmlWriter.WriteString("") - } - else - { - $xmlWriter.WriteString("$($normalizedDotNetAlias).$($associatedEntityType.Name)") - } - $xmlWriter.WriteEndElement() - $xmlWriter.WriteStartElement('Data') - $xmlWriter.WriteAttributeString('Name', 'EntityTypeName') - $xmlWriter.WriteString("$($normalizedDotNetNamespace).$($associatedEntityType.Name)") - $xmlWriter.WriteEndElement() - $xmlWriter.WriteStartElement('Data') - $xmlWriter.WriteAttributeString('Name', 'IsSingleton') - $xmlWriter.WriteString("True") - $xmlWriter.WriteEndElement() - - # Add information about navigation properties and their types - # Used in scenario where user requests navigation property in -Select query - foreach ($navProperty in $navigationProperties) - { - if ($navProperty) - { - $xmlWriter.WriteStartElement('Data') - $xmlWriter.WriteAttributeString('Name', $navProperty.Name + 'NavigationProperty') - $xmlWriter.WriteString($navProperty.Type) - $xmlWriter.WriteEndElement() - } - } - - # Add UpdateRequestMethod to privateData - $xmlWriter.WriteStartElement('Data') - $xmlWriter.WriteAttributeString('Name', 'UpdateRequestMethod') - $xmlWriter.WriteString("$UpdateRequestMethod") - $xmlWriter.WriteEndElement() - - $xmlWriter.WriteEndElement() - - SaveCDXMLFooter $xmlWriter - - Write-Verbose ($LocalizedData.VerboseSavedCDXML -f $($associatedEntityType.Name), $Path) - } -} - -######################################################### -# Saves InstanceCmdlets node to CDXML -######################################################### -function SaveCDXMLInstanceCmdlets -{ - param - ( - [System.XMl.XmlTextWriter] $xmlWriter, - [ODataUtils.MetadataV4] $Metadata, - [System.Collections.ArrayList] $GlobalMetadata, - [ODataUtils.EntityTypeV4] $EntityType, - $keys, - $navigationProperties, - $CmdletAdapter, - [Hashtable] $complexTypeMapping, - [bool] $isSingleton - ) - - if($null -eq $xmlWriter) { throw ($LocalizedData.ArguementNullError -f "xmlWriter", "SaveCDXMLInstanceCmdlets") } - if($null -eq $Metadata) { throw ($LocalizedData.ArguementNullError -f "Metadata", "SaveCDXMLInstanceCmdlets") } - - $xmlWriter.WriteStartElement('InstanceCmdlets') - $xmlWriter.WriteStartElement('GetCmdletParameters') - # adding key parameters and association parameters to QueryableProperties, each in a different parameter set - # to be used by GET cmdlet - if (($keys.Length -gt 0) -or ($navigationProperties.Length -gt 0)) - { - $queryableNavProperties = @{} - - if ($isSingleton -eq $false) - { - foreach ($navProperty in $navigationProperties) - { - if ($null -ne $navProperty) - { - $associatedType = GetAssociatedType $Metadata $GlobalMetadata $navProperty - $associatedTypeKeyProperties = $associatedType.EntityProperties | Where-Object { $_.IsKey } - - # Make sure associated parameter (based on navigation property) has EntitySet or Singleton, which makes it accessible from the service root - # Otherwise the Uri for associated navigation property won't be valid - if ($associatedTypeKeyProperties.Length -gt 0 -and (ShouldBeAssociatedParameter $GlobalMetadata $EntityType $associatedType $isSingleton)) - { - $queryableNavProperties.Add($navProperty, $associatedTypeKeyProperties) - } - } - } - } - - $defaultCmdletParameterSet = 'Default' - if ($isSingleton -eq $true -and $queryableNavProperties.Count -gt 0) - { - foreach($item in $queryableNavProperties.GetEnumerator()) - { - $defaultCmdletParameterSet = $item.Key.Name - break - } - } - $xmlWriter.WriteAttributeString('DefaultCmdletParameterSet', $defaultCmdletParameterSet) - - - $xmlWriter.WriteStartElement('QueryableProperties') - - $position = 0 - - if ($isSingleton -eq $false) - { - $keys | Where-Object { $null -ne $_ } | ForEach-Object { - $xmlWriter.WriteStartElement('Property') - $xmlWriter.WriteAttributeString('PropertyName', $_.Name) - - $xmlWriter.WriteStartElement('Type') - $PSTypeName = Convert-ODataTypeToCLRType $_.TypeName $complexTypeMapping - $xmlWriter.WriteAttributeString('PSType', $PSTypeName) - $xmlWriter.WriteEndElement() - - $xmlWriter.WriteStartElement('RegularQuery') - $xmlWriter.WriteStartElement('CmdletParameterMetadata') - $xmlWriter.WriteAttributeString('PSName', $_.Name) - $xmlWriter.WriteAttributeString('CmdletParameterSets', 'Default') - $xmlWriter.WriteAttributeString('IsMandatory', $_.IsMandatory.ToString().ToLower()) - $xmlWriter.WriteAttributeString('Position', $position) - $xmlWriter.WriteEndElement() - $xmlWriter.WriteEndElement() - $xmlWriter.WriteEndElement() - - $position++ - } - } - - if ($queryableNavProperties.Count -gt 0) - { - foreach($item in $queryableNavProperties.GetEnumerator()) - { - $xmlWriter.WriteStartElement('Property') - $xmlWriter.WriteAttributeString('PropertyName', $item.Key.Name + ':' + $item.Value.Name + ':Key') - - $xmlWriter.WriteStartElement('Type') - $PSTypeName = Convert-ODataTypeToCLRType $item.Value.TypeName $complexTypeMapping - $xmlWriter.WriteAttributeString('PSType', $PSTypeName) - $xmlWriter.WriteEndElement() - - $xmlWriter.WriteStartElement('RegularQuery') - $xmlWriter.WriteStartElement('CmdletParameterMetadata') - $xmlWriter.WriteAttributeString('PSName', 'Associated' + $item.Key.Name + $item.Value.Name) - $xmlWriter.WriteAttributeString('CmdletParameterSets', $item.Key.Name) - $xmlWriter.WriteAttributeString('IsMandatory', 'false') - $xmlWriter.WriteEndElement() - $xmlWriter.WriteEndElement() - $xmlWriter.WriteEndElement() - } - } - - if ($isSingleton -eq $false) - { - # Add Query Parameters (i.e., Top, Skip, OrderBy, Filter) to the generated Get-* cmdlets. - $queryParameters = - @{ - "Filter" = "Edm.String"; - "IncludeTotalResponseCount" = "switch"; - "OrderBy" = "Edm.String"; - "Select" = "Edm.String"; - "Skip" = "Edm.Int32"; - "Top" = "Edm.Int32"; - } - } - else - { - $queryParameters = - @{ - "Select" = "Edm.String"; - } - } - - foreach($currentQueryParameter in $queryParameters.Keys) - { - $xmlWriter.WriteStartElement('Property') - $xmlWriter.WriteAttributeString('PropertyName', "QueryOption:" + $currentQueryParameter) - $xmlWriter.WriteStartElement('Type') - $PSTypeName = Convert-ODataTypeToCLRType $queryParameters[$currentQueryParameter] - $xmlWriter.WriteAttributeString('PSType', $PSTypeName) - $xmlWriter.WriteEndElement() - $xmlWriter.WriteStartElement('RegularQuery') - $xmlWriter.WriteStartElement('CmdletParameterMetadata') - $xmlWriter.WriteAttributeString('PSName', $currentQueryParameter) - - if($queryParameters[$currentQueryParameter] -eq "Edm.String") - { - $xmlWriter.WriteStartElement('ValidateNotNullOrEmpty') - $xmlWriter.WriteEndElement() - } - - if($queryParameters[$currentQueryParameter] -eq "Edm.Int32") - { - $xmlWriter.WriteStartElement('ValidateRange') - $xmlWriter.WriteAttributeString('Min', "1") - $xmlWriter.WriteAttributeString('Max', [int]::MaxValue) - $xmlWriter.WriteEndElement() - } - - $xmlWriter.WriteEndElement() - $xmlWriter.WriteEndElement() - $xmlWriter.WriteEndElement() - } - - - $xmlWriter.WriteEndElement() - } - - $xmlWriter.WriteEndElement() - - $xmlWriter.WriteStartElement('GetCmdlet') - $xmlWriter.WriteStartElement('CmdletMetadata') - $xmlWriter.WriteAttributeString('Verb', 'Get') - $xmlWriter.WriteEndElement() - $xmlWriter.WriteEndElement() - - $xmlWriter.WriteEndElement() -} - -# Helper Method -# Returns true if navigation property of $AssociatedType has corresponding EntitySet or Singleton -# If yes, then it should become an associated parameter in CDXML -function ShouldBeAssociatedParameter -{ - param - ( - [System.Collections.ArrayList] $GlobalMetadata, - [ODataUtils.EntityTypeV4] $EntityType, - [ODataUtils.EntityTypeV4] $AssociatedType - ) - - # Check if associated type has navigation property, which links back to current type - $associatedTypeNavProperties = $AssociatedType.NavigationProperties | Where-Object { - $_.Type -eq ($EntityType.Namespace + "." + $EntityType.Name) -or - $_.Type -eq ($EntityType.Alias + "." + $EntityType.Name) -or - $_.Type -eq ("Collection(" + $EntityType.Namespace + "." + $EntityType.Name + ")") -or - $_.Type -eq ("Collection(" + $EntityType.Alias + "." + $EntityType.Name + ")") - } - - if ($associatedTypeNavProperties.Length -lt 1) - { - return $false - } - - # Now check if associated parameter type (i.e, type of navigation property) has corresponding EntitySet or Singleton, - # which makes it accessible from the service root. - # Otherwise the Uri for associated navigation property won't be valid - foreach ($currentMetadata in $GlobalMetadata) - { - # Look for EntitySet with given type - foreach ($currentEntitySet in $currentMetadata.EntitySets) - { - if ($currentEntitySet.Type.Namespace -eq $EntityType.Namespace -and - $currentEntitySet.Type.Name -eq $EntityType.Name) - { - return $true - } - } - - # Look for Singleton with given type - foreach ($currentSingleton in $currentMetadata.Singletons) - { - if ($currentSingleton.Type.Namespace -eq $EntityType.Namespace -and - $currentSingleton.Type.Name -eq $EntityType.Name) - { - return $true - } - } - } - - return $false -} - -######################################################### -# Saves NewCmdlet node to CDXML -######################################################### -function SaveCDXMLNewCmdlet -{ - param - ( - [System.XMl.XmlTextWriter] $xmlWriter, - [ODataUtils.MetadataV4] $Metadata, - [System.Collections.ArrayList] $GlobalMetadata, - $keyProperties, - $nonNullableProperties, - $nullableProperties, - $navigationProperties, - $CmdletAdapter, - $complexTypeMapping - ) - - if($null -eq $xmlWriter) { throw ($LocalizedData.ArguementNullError -f "xmlWriter", "SaveCDXMLNewCmdlet") } - if($null -eq $Metadata) { throw ($LocalizedData.ArguementNullError -f "Metadata", "SaveCDXMLNewCmdlet") } - - $xmlWriter.WriteStartElement('Cmdlet') - $xmlWriter.WriteStartElement('CmdletMetadata') - $xmlWriter.WriteAttributeString('Verb', 'New') - $xmlWriter.WriteAttributeString('DefaultCmdletParameterSet', 'Default') - $xmlWriter.WriteAttributeString('ConfirmImpact', 'Medium') - $xmlWriter.WriteEndElement() - - $xmlWriter.WriteStartElement('Method') - $xmlWriter.WriteAttributeString('MethodName', 'Create') - $xmlWriter.WriteAttributeString('CmdletParameterSet', 'Default') - - AddParametersNode $xmlWriter $keyProperties $nonNullableProperties $nullableProperties $null $true $true $complexTypeMapping - - $xmlWriter.WriteEndElement() - - $navigationProperties | Where-Object { $null -ne $_ } | ForEach-Object { - $associatedType = GetAssociatedType $Metadata $GlobalMetadata $_ - $associatedEntitySet = GetEntitySetForEntityType $Metadata $associatedType - - $xmlWriter.WriteStartElement('Method') - $xmlWriter.WriteAttributeString('MethodName', "Association:Create:$($associatedEntitySet.Name)") - $xmlWriter.WriteAttributeString('CmdletParameterSet', $_.Name) - - $associatedKeys = ($associatedType.EntityProperties | Where-Object { $_.isKey }) - - AddParametersNode $xmlWriter $associatedKeys $keyProperties $null "Associated$($_.Name)" $true $true $complexTypeMapping - - $xmlWriter.WriteEndElement() - } - - $xmlWriter.WriteEndElement() -} - -######################################################### -# Get corresponding EntityType for given EntitySet -######################################################### -function GetEntitySetForEntityType { - param( - [ODataUtils.MetadataV4] $Metadata, - [ODataUtils.EntityTypeV4] $entityType - ) - - if($null -eq $entityType) { throw ($LocalizedData.ArguementNullError -f "EntityType", "GetEntitySetForEntityType") } - - $result = $Metadata.EntitySets | Where-Object { ($_.Type.Namespace -eq $entityType.Namespace) -and ($_.Type.Name -eq $entityType.Name) } - - if (($result.Count -eq 0) -and ($null -ne $entityType.BaseType)) - { - GetEntitySetForEntityType $Metadata $entityType.BaseType - } - elseif ($result.Count -gt 1) - { - throw ($LocalizedData.WrongCountEntitySet -f (($entityType.Namespace + "." + $entityType.Name), $result.Count)) - } - - $result -} - -######################################################### -# Saves RemoveCmdlet node to CDXML -######################################################### -function SaveCDXMLRemoveCmdlet -{ - param - ( - [System.XMl.XmlTextWriter] $xmlWriter, - [ODataUtils.MetadataV4] $Metadata, - [System.Collections.ArrayList] $GlobalMetadata, - $keyProperties, - $navigationProperties, - $CmdletAdapter, - $complexTypeMapping - ) - - if($null -eq $xmlWriter) { throw ($LocalizedData.ArguementNullError -f "xmlWriter", "SaveCDXMLRemoveCmdlet") } - if($null -eq $Metadata) { throw ($LocalizedData.ArguementNullError -f "Metadata", "SaveCDXMLRemoveCmdlet") } - - $xmlWriter.WriteStartElement('Cmdlet') - $xmlWriter.WriteStartElement('CmdletMetadata') - $xmlWriter.WriteAttributeString('Verb', 'Remove') - $xmlWriter.WriteAttributeString('DefaultCmdletParameterSet', 'Default') - $xmlWriter.WriteAttributeString('ConfirmImpact', 'Medium') - $xmlWriter.WriteEndElement() - - $xmlWriter.WriteStartElement('Method') - $xmlWriter.WriteAttributeString('MethodName', 'Delete') - $xmlWriter.WriteAttributeString('CmdletParameterSet', 'Default') - - AddParametersNode $xmlWriter $keyProperties $nul $null $null $true $true $complexTypeMapping - - $xmlWriter.WriteEndElement() - - $navigationProperties | Where-Object { $null -ne $_ } | ForEach-Object { - - $associatedType = GetAssociatedType $Metadata $GlobalMetadata $_ - $associatedEntitySet = GetEntitySetForEntityType $Metadata $associatedType - - $xmlWriter.WriteStartElement('Method') - $xmlWriter.WriteAttributeString('MethodName', "Association:Delete:$($associatedEntitySet.Name)") - $xmlWriter.WriteAttributeString('CmdletParameterSet', $_.Name) - - $associatedType = GetAssociatedType $Metadata $GlobalMetadata $_ - $associatedKeys = ($associatedType.EntityProperties | Where-Object { $_.isKey }) - - AddParametersNode $xmlWriter $associatedKeys $keyProperties $null "Associated$($_.Name)" $true $true $complexTypeMapping - - $xmlWriter.WriteEndElement() - } - $xmlWriter.WriteEndElement() -} - -######################################################### -# Gets associated instance's EntityType for a given navigation property -######################################################### -function GetAssociatedType { - param( - [ODataUtils.MetadataV4] $Metadata, - [System.Collections.ArrayList] $GlobalMetadata, - [ODataUtils.NavigationPropertyV4] $navProperty - ) - - $associationType = $navProperty.Type - $associationType = $associationType.Replace($Metadata.Namespace + ".", "") - $associationType = $associationType.Replace($Metadata.Alias + ".", "") - $associationType = $associationType.Replace("Collection(", "") - $associationType = $associationType.Replace(")", "") - - $associatedType = $Metadata.EntityTypes | Where-Object { $_.Name -eq $associationType } - - if (!$associatedType -and $null -ne $GlobalMetadata) - { - $associationFullTypeName = $navProperty.Type.Replace("Collection(", "").Replace(")", "") - - foreach ($referencedMetadata in $GlobalMetadata) - { - if ($null -ne ($associatedType = $referencedMetadata.EntityTypes | Where-Object { $_.Namespace + "." + $_.Name -eq $associationFullTypeName -or $_.Alias + "." + $_.Name -eq $associationFullTypeName }) -or - $null -ne ($associatedType = $referencedMetadata.ComplexTypes | Where-Object { $_.Namespace + "." + $_.Name -eq $associationFullTypeName -or $_.Alias + "." + $_.Name -eq $associationFullTypeName }) -or - $null -ne ($associatedType = $referencedMetadata.EnumTypes | Where-Object { $_.Namespace + "." + $_.Name -eq $associationFullTypeName -or $_.Alias + "." + $_.Name -eq $associationFullTypeName })) - { - # Found associated class - break - } - } - } - - if ($associatedType.Count -lt 1) - { - throw ($LocalizedData.AssociationNotFound -f $associationType) - } - elseif ($associatedType.Count -gt 1) - { - throw ($LocalizedData.TooManyMatchingAssociationTypes -f $associatedType.Count, $associationType) - } - - # return associated EntityType - $associatedType -} - -######################################################### -# Saves CDXML for Instance/Service level actions -######################################################### -function SaveCDXMLAction -{ - param - ( - [System.Xml.XmlWriter] $xmlWriter, - [ODataUtils.ActionV4] $action, - [AllowEmptyString()] - [string] $noun, - [bool] $isInstanceAction, - [ODataUtils.TypeProperty] $keys, - [Hashtable] $complexTypeMapping - ) - - if($null -eq $xmlWriter) { throw ($LocalizedData.ArguementNullError -f "xmlWriter", "SaveCDXMLAction") } - if($null -eq $action) { throw ($LocalizedData.ArguementNullError -f "action", "SaveCDXMLAction") } - - $xmlWriter.WriteStartElement('Cmdlet') - - $xmlWriter.WriteStartElement('CmdletMetadata') - $xmlWriter.WriteAttributeString('Verb', 'Invoke') - $xmlWriter.WriteAttributeString('Noun', "$($noun)$($action.Name)") - $xmlWriter.WriteAttributeString('ConfirmImpact', 'Medium') - $xmlWriter.WriteEndElement() - - $xmlWriter.WriteStartElement('Method') - $xmlWriter.WriteAttributeString('MethodName', "Action:$($action.Name):$($action.EntitySet.Name)") - - $xmlWriter.WriteStartElement('Parameters') - - $keys | Where-Object { $null -ne $_ } | ForEach-Object { - $xmlWriter.WriteStartElement('Parameter') - $xmlWriter.WriteAttributeString('ParameterName', $_.Name + ':Key') - - $xmlWriter.WriteStartElement('Type') - $PSTypeName = Convert-ODataTypeToCLRType $_.TypeName $complexTypeMapping - $xmlWriter.WriteAttributeString('PSType', $PSTypeName) - $xmlWriter.WriteEndElement() - - $xmlWriter.WriteStartElement('CmdletParameterMetadata') - $xmlWriter.WriteAttributeString('PSName', $_.Name) - $xmlWriter.WriteAttributeString('IsMandatory', 'true') - $xmlWriter.WriteEndElement() - $xmlWriter.WriteEndElement() - } - - $i = -1 - foreach ($parameter in $action.Parameters) - { - $i++ - - # for Instance actions, first parameter is Entity Set which we refer to using keys - if ($isInstanceAction -and ($i -eq 0)) - { - continue - } - - $xmlWriter.WriteStartElement('Parameter') - $xmlWriter.WriteAttributeString('ParameterName', $parameter.Name) - - $xmlWriter.WriteStartElement('Type') - $PSTypeName = Convert-ODataTypeToCLRType $parameter.TypeName - $xmlWriter.WriteAttributeString('PSType', $PSTypeName) - $xmlWriter.WriteEndElement() - - $xmlWriter.WriteStartElement('CmdletParameterMetadata') - $xmlWriter.WriteAttributeString('PSName', $parameter.Name) - if (-not $parameter.IsNullable) - { - $xmlWriter.WriteAttributeString('IsMandatory', 'true') - } - $xmlWriter.WriteEndElement() - $xmlWriter.WriteEndElement() - } - - # Add -Force parameter to Service Action cmdlets. - AddParametersNode $xmlWriter $null $null $null $null $true $false $complexTypeMapping - - $xmlWriter.WriteEndElement() - $xmlWriter.WriteEndElement() - - $xmlWriter.WriteEndElement() - - $xmlWriter -} - -######################################################### -# Saves CDXML for Instance/Service level functions -######################################################### -function SaveCDXMLFunction -{ - param - ( - [System.Xml.XmlWriter] $xmlWriter, - [ODataUtils.FunctionV4] $function, - [AllowEmptyString()] - [string] $noun, - [bool] $isInstanceAction, - [ODataUtils.TypeProperty] $keys, - [Hashtable] $complexTypeMapping - ) - - if($null -eq $xmlWriter) { throw ($LocalizedData.ArguementNullError -f "xmlWriter", "SaveCDXMLFunction") } - if($null -eq $function) { throw ($LocalizedData.ArguementNullError -f "function", "SaveCDXMLFunction") } - - $xmlWriter.WriteStartElement('Cmdlet') - - $xmlWriter.WriteStartElement('CmdletMetadata') - $xmlWriter.WriteAttributeString('Verb', 'Invoke') - $xmlWriter.WriteAttributeString('Noun', "$($noun)$($function.Name)") - $xmlWriter.WriteAttributeString('ConfirmImpact', 'Medium') - $xmlWriter.WriteEndElement() - - $xmlWriter.WriteStartElement('Method') - if (!$function.EntitySet) - { - $xmlWriter.WriteAttributeString('MethodName', "Action:$($function.Name):$($function.ReturnType)") - } - else - { - $xmlWriter.WriteAttributeString('MethodName', "Action:$($function.Name):$($function.EntitySet)") - } - - $xmlWriter.WriteStartElement('Parameters') - - $keys | Where-Object { $null -ne $_ } | ForEach-Object { - $xmlWriter.WriteStartElement('Parameter') - $xmlWriter.WriteAttributeString('ParameterName', $_.Name + ':Key') - - $xmlWriter.WriteStartElement('Type') - $PSTypeName = Convert-ODataTypeToCLRType $_.TypeName $complexTypeMapping - $xmlWriter.WriteAttributeString('PSType', $PSTypeName) - $xmlWriter.WriteEndElement() - - $xmlWriter.WriteStartElement('CmdletParameterMetadata') - $xmlWriter.WriteAttributeString('PSName', $_.Name) - $xmlWriter.WriteAttributeString('IsMandatory', 'true') - $xmlWriter.WriteEndElement() - $xmlWriter.WriteEndElement() - } - - $i = -1 - foreach ($parameter in $function.Parameters) - { - $i++ - - # for Instance actions, first parameter is Entity Set which we refer to using keys - if ($isInstanceAction -and ($i -eq 0)) - { - continue - } - - $xmlWriter.WriteStartElement('Parameter') - $xmlWriter.WriteAttributeString('ParameterName', $parameter.Name) - - $xmlWriter.WriteStartElement('Type') - $PSTypeName = Convert-ODataTypeToCLRType $parameter.Type - $xmlWriter.WriteAttributeString('PSType', $PSTypeName) - $xmlWriter.WriteEndElement() - - $xmlWriter.WriteStartElement('CmdletParameterMetadata') - $xmlWriter.WriteAttributeString('PSName', $parameter.Name) - if (-not $parameter.IsNullable) - { - $xmlWriter.WriteAttributeString('IsMandatory', 'true') - } - $xmlWriter.WriteEndElement() - $xmlWriter.WriteEndElement() - } - - # Add -Force parameter to Service Function cmdlets. - AddParametersNode $xmlWriter $null $null $null $null $true $false $complexTypeMapping - - $xmlWriter.WriteEndElement() - $xmlWriter.WriteEndElement() - - $xmlWriter.WriteEndElement() - - $xmlWriter -} - -######################################################### -# Saves CDXML for Service-level actions and functions -######################################################### -function SaveServiceActionsCDXML -{ - param - ( - [System.Collections.ArrayList] $GlobalMetadata, - [ODataUtils.ODataEndpointProxyParameters] $ODataEndpointProxyParameters, - [string] $Path, - [Hashtable] $complexTypeMapping, - [string] $progressBarStatus, - [string] $CmdletAdapter - ) - - $xmlWriter = New-Object System.XMl.XmlTextWriter($Path,$Null) - - if ($null -eq $xmlWriter) - { - throw $LocalizedData.XmlWriterInitializationError -f "ServiceActions" - } - - $xmlWriter = SaveCDXMLHeader $xmlWriter $ODataEndpointProxyParameters.Uri 'ServiceActions' 'ServiceActions' -CmdletAdapter $CmdletAdapter - - $actions = @() - $functions = @() - - foreach ($Metadata in $GlobalMetadata) - { - $actions += $Metadata.Actions | Where-Object { $_.EntitySet -eq [string]::Empty -or $null -eq $_.EntitySet } - $functions += $Metadata.Functions | Where-Object { $_.EntitySet -eq [string]::Empty -or $null -eq $_.EntitySet} - } - - if ($actions.Length -gt 0 -or $functions.Length -gt 0) - { - $xmlWriter.WriteStartElement('StaticCmdlets') - } - - # Save actions - if ($actions.Length -gt 0) - { - foreach ($action in $actions) - { - if ($null -ne $action) - { - $xmlWriter = SaveCDXMLAction $xmlWriter $action '' $false $null $complexTypeMapping - } - } - } - - # Save functions - if ($functions.Length -gt 0) - { - foreach ($function in $functions) - { - if ($null -ne $function) - { - $xmlWriter = SaveCDXMLFunction $xmlWriter $function '' $false $null $complexTypeMapping - } - } - } - - if ($actions.Length -gt 0 -or $functions.Length -gt 0) - { - $xmlWriter.WriteEndElement() - } - - $xmlWriter.WriteStartElement('CmdletAdapterPrivateData') - $xmlWriter.WriteStartElement('Data') - $xmlWriter.WriteAttributeString('Name', 'Namespace') - $xmlWriter.WriteString("$($EntitySet.Namespace)") - $xmlWriter.WriteEndElement() - $xmlWriter.WriteStartElement('Data') - $xmlWriter.WriteAttributeString('Name', 'Alias') - if (!$EntitySet.Alias) - { - $xmlWriter.WriteString("") - } - else - { - $xmlWriter.WriteString("$($EntitySet.Alias)") - } - $xmlWriter.WriteEndElement() - - $xmlWriter.WriteStartElement('Data') - $xmlWriter.WriteAttributeString('Name', 'CreateRequestMethod') - $xmlWriter.WriteString("Post") - $xmlWriter.WriteEndElement() - $xmlWriter.WriteEndElement() - - SaveCDXMLFooter $xmlWriter - - Write-Verbose ($LocalizedData.VerboseSavedServiceActions -f $Path) - - # Write progress bar message - ProgressBarHelper "Export-ODataEndpointProxy" $progressBarStatus 60 20 1 1 -} - -######################################################### -# GenerateModuleManifest is a helper function used -# to generate a wrapper module manifest file. The -# generated module manifest is persisted to the disk at -# the specified OutputModule path. When the module -# manifest is imported, the following commands will -# be imported: -# 1. Get, Set, New & Remove proxy cmdlets for entity -# sets and singletons. -# 2. If the server side Odata endpoint exposes complex -# types, enum types, type definitions, then the corresponding -# client side proxy types imported. -# 3. Service Action/Function proxy cmdlets. -######################################################### -function GenerateModuleManifest -{ - param - ( - [System.Collections.ArrayList] $GlobalMetadata, - [String] $ModulePath, - [string[]] $AdditionalModules, - [Hashtable] $resourceNameMappings, - [string] $progressBarStatus - ) - - if($null -eq $progressBarStatus) { throw ($LocalizedData.ArguementNullError -f "progressBarStatus", "GenerateModuleManifest") } - - $NestedModules = @() - - foreach ($Metadata in $GlobalMetadata) - { - foreach ($entitySet in $Metadata.EntitySets) - { - $entitySetName = $entitySet.Name - if(($null -ne $resourceNameMappings) -and - $resourceNameMappings.ContainsKey($entitySetName)) - { - $entitySetName = $resourceNameMappings[$entitySetName] - } - else - { - $entitySetName = $entitySet.Type.Name - } - - $NestedModules += "$OutputModule\$($entitySetName).cdxml" - } - - foreach ($singleton in $Metadata.SingletonTypes) - { - $singletonName = $singleton.Name - $NestedModules += "$OutputModule\$($singletonName)" + "Singleton" + ".cdxml" - } - } - - New-ModuleManifest -Path $ModulePath -NestedModules ($AdditionalModules + $NestedModules) - - Write-Verbose ($LocalizedData.VerboseSavedModuleManifest -f $ModulePath) - - # Update the Progress Bar. - ProgressBarHelper "Export-ODataEndpointProxy" $progressBarStatus 80 20 1 1 -} - -######################################################### -# This is a helper function used to generate complex -# type definition from the metadata. -######################################################### -function GenerateComplexTypeDefinition -{ - param - ( - [System.Collections.ArrayList] $GlobalMetadata, - [string] $OutputModule, - [string] $typeDefinationFileName, - $normalizedNamespaces - ) - - $Path = "$OutputModule\$typeDefinationFileName" - $date = Get-Date - - $output = @" -# This module was generated by PSODataUtils on $date. - -`$typeDefinitions = @" -using System; -using System.Management.Automation; -using System.ComponentModel; - -"@ - # We are currently generating classes for EntityType & ComplexType - # definition exposed in the metadata. - - $complexTypeMapping = @{} - - # First, create complex type mappings for all metadata files at once - foreach ($metadata in $GlobalMetadata) - { - $typesToBeGenerated = $metadata.EntityTypes+$metadata.ComplexTypes - $enumTypesToBeGenerated = $metadata.EnumTypes - $typeDefinitionsToBeGenerated = $metadata.TypeDefinitions - - foreach ($entityType in $typesToBeGenerated) - { - if ($null -ne $entityType) - { - $entityTypeFullName = $entityType.Namespace + '.' + $entityType.Name - if(!$complexTypeMapping.ContainsKey($entityTypeFullName)) - { - $complexTypeMapping.Add($entityTypeFullName, $entityType.Name) - } - - # In short name we use Alias instead of Namespace - # We will add short name to $complexTypeMapping to enable Alias based search - if ($null -ne $entityType.Alias -and $entityType.Alias -ne "") - { - $entityTypeShortName = $entityType.Alias + '.' + $entityType.Name - if(!$complexTypeMapping.ContainsKey($entityTypeShortName)) - { - $complexTypeMapping.Add($entityTypeShortName, $entityType.Name) - } - } - } - } - - foreach ($enumType in $enumTypesToBeGenerated) - { - if ($null -ne $enumType) - { - $enumTypeFullName = $enumType.Namespace + '.' + $enumType.Name - if(!$complexTypeMapping.ContainsKey($enumTypeFullName)) - { - $complexTypeMapping.Add($enumTypeFullName, $enumType.Name) - } - - if (($null -ne $enumType.Alias -and $enumType.Alias -ne "") -or ($null -ne $metadata.Alias -and $metadata.Alias -ne [string]::Empty)) - { - if ($null -ne $enumType.Alias -and $enumType.Alias -ne "") - { - $alias = $enumType.Alias - } - else - { - $alias = $metadata.Alias - } - - $enumTypeShortName = $alias + '.' + $enumType.Name - if(!$complexTypeMapping.ContainsKey($enumTypeShortName)) - { - $complexTypeMapping.Add($enumTypeShortName, $enumType.Name) - } - } - } - } - - foreach ($typeDefinition in $typeDefinitionsToBeGenerated) - { - if ($null -ne $typeDefinition) - { - $typeDefinitionFullName = $typeDefinition.Namespace + '.' + $typeDefinition.Name - if(!$complexTypeMapping.ContainsKey($typeDefinitionFullName)) - { - $complexTypeMapping.Add($typeDefinitionFullName, $typeDefinition.Name) - } - - # In short name we use Alias instead of Namespace - # We will add short name to $complexTypeMapping to enable Alias based search - if ($typeDefinition.Alias) - { - $typeDefinitionShortName = $typeDefinition.Alias + '.' + $typeDefinition.Name - if(!$complexTypeMapping.ContainsKey($typeDefinitionShortName)) - { - $complexTypeMapping.Add($typeDefinitionShortName, $typeDefinition.Name) - } - } - } - } - } - - # Now classes definitions will be generated - foreach ($metadata in $GlobalMetadata) - { - $typesToBeGenerated = $metadata.EntityTypes+$metadata.ComplexTypes - $enumTypesToBeGenerated = $metadata.EnumTypes - $typeDefinitionsToBeGenerated = $metadata.TypeDefinitions - - if($typesToBeGenerated.Count -gt 0 -or $enumTypesToBeGenerated.Count -gt 0) - { - if ($null -ne $metadata.Alias -and $metadata.Alias -ne [string]::Empty) - { - # Check if this namespace has to be normalized in the .Net namespace/class definitions file. - $dotNetAlias = GetNamespace $metadata.Alias $normalizedNamespaces - - $output += @" - -namespace $($dotNetAlias) -{ -"@ - } - else - { - # Check if this namespace has to be normalized in the .Net namespace/class definitions file. - $dotNetNamespace = GetNamespace $metadata.Namespace $normalizedNamespaces - - $output += @" - -namespace $($dotNetNamespace) -{ -"@ - } - - foreach ($typeDefinition in $typeDefinitionsToBeGenerated) - { - if ($null -ne $typeDefinition) - { - Write-Verbose ($LocalizedData.VerboseAddingTypeDefinationToGeneratedModule -f $typeDefinitionFullName, "$OutputModule\$typeDefinationFileName") - - $output += "`n public class $($typeDefinition.Name)`n {" - $typeName = Convert-ODataTypeToCLRType $typeDefinition.BaseTypeStr $complexTypeMapping - $dotNetPropertyNamespace = GetNamespace $typeName $normalizedNamespaces $true - $output += "`n public $dotNetPropertyNamespace value;" - $output += @" -`n } -"@ - } - } - - $DotNETKeywords = ("abstract", "as", "base", "bool", "break", "byte", "case", "catch", "char", "checked", "class", "const", "continue", "decimal", "default", "delegate", "do", "double", "else", "enum", "event", "explicit", "extern", "false", "finally", "fixed", "float", "for", "foreach", "goto", "if", "implicit", "in", "in", "int", "interface", "internal", "is", "lock", "long", "namespace", "new", "null", "object", "operator", "out", "out", "override", "params", "private", "protected", "public", "readonly", "ref", "return", "sbyte", "sealed", "short", "sizeof", "stackalloc", "static", "string", "struct", "switch", "this", "throw", "true", "try", "typeof", "uint", "ulong", "unchecked", "unsafe", "ushort", "using", "virtual", "void", "volatile", "while", "add", "alias", "ascending", "async", "await", "descending", "dynamic", "from", "get", "global", "group", "into", "join", "let", "orderby", "partial", "partial", "remove", "select", "set", "value", "var", "where", "yield") - - foreach ($enumType in $enumTypesToBeGenerated) - { - if ($null -ne $enumType) - { - $enumTypeFullName = $enumType.Namespace + '.' + $enumType.Name - - Write-Verbose ($LocalizedData.VerboseAddingTypeDefinationToGeneratedModule -f $enumTypeFullName, "$OutputModule\$typeDefinationFileName") - - $output += "`n public enum $($enumType.Name)`n {" - - $properties = $null - - for($index = 0; $index -lt $enumType.Members.Count; $index++) - { - $memberName = $enumType.Members[$index].Name - $formattedMemberName = [System.Text.RegularExpressions.Regex]::Replace($memberName, "[^0-9a-zA-Z]", "_"); - $memberValue = $enumType.Members[$index].Value - - if ($DotNETKeywords -contains $formattedMemberName) - { - # If member name is a known keyword in .Net, add '@' prefix - $formattedMemberName = '@' + $formattedMemberName - } - - if ($formattedMemberName -match "^[0-9]*$") - { - # If member name is a numeric value, add 'm_' prefix - $formattedMemberName = 'm_' + $formattedMemberName - } - - if ($memberName -ne $formattedMemberName -or $formattedMemberName -like '@*' -or $formattedMemberName -like 'm_*') - { - # Add Description attribute to preserve original value - $properties += "`n [Description(`"$($memberName)`")]$formattedMemberName" - } - else - { - $properties += "`n $memberName" - } - - if ($memberValue) - { - $properties += " = $memberValue," - } - else - { - $properties += "," - } - } - - $output += $properties - $output += @" -`n } -"@ - } - } - - foreach ($entityType in $typesToBeGenerated) - { - if ($null -ne $entityType) - { - $entityTypeFullName = $entityType.Namespace + '.' + $entityType.Name - - Write-Verbose ($LocalizedData.VerboseAddingTypeDefinationToGeneratedModule -f $entityTypeFullName, "$OutputModule\$typeDefinationFileName") - - if ($null -ne $entityType.BaseTypeStr -and $entityType.BaseTypeStr -ne '' -and $null -eq $entityType.BaseType) - { - # This class inherits from another class, but we were not able to find base class during Parsing. - # We'll make another attempt. - foreach ($referencedMetadata in $GlobalMetadata) - { - if ($null -ne ($baseType = $referencedMetadata.EntityTypes | Where-Object { $_.Namespace + "." + $_.Name -eq $entityType.BaseTypeStr -or $_.Alias + "." + $_.Name -eq $entityType.BaseTypeStr }) -or - $null -ne ($baseType = $referencedMetadata.ComplexTypes | Where-Object { $_.Namespace + "." + $_.Name -eq $entityType.BaseTypeStr -or $_.Alias + "." + $_.Name -eq $entityType.BaseTypeStr })) - { - # Found base class - $entityType.BaseType = $baseType - break - } - } - } - - if($null -ne $entityType.BaseType) - { - if ((![string]::IsNullOrEmpty($entityType.BaseType.Alias) -and $entityType.BaseType.Alias -eq $entityType.Alias) -or - (![string]::IsNullOrEmpty($entityType.BaseType.Namespace) -and $entityType.BaseType.Namespace -eq $entityType.Namespace)) - { - $fullBaseTypeName = $entityType.BaseType.Name - } - else - { - # Base type can be defined in different namespace. For that reason we include namespace or alias. - if (![string]::IsNullOrEmpty($entityType.BaseType.Alias)) - { - # Check if derived alias has to be normalized. - $normalizedDotNetAlias = GetNamespace $entityType.BaseType.Alias $normalizedNamespaces - $fullBaseTypeName = $normalizedDotNetAlias + "." + $entityType.BaseType.Name - } - else - { - # Check if derived namespace has to be normalized. - $normalizedDotNetNamespace = GetNamespace $entityType.BaseType.Namespace $normalizedNamespaces - $fullBaseTypeName = $normalizedDotNetNamespace + "." + $entityType.BaseType.Name - } - } - - $output += "`n public class $($entityType.Name) : $($fullBaseTypeName)`n {" - } - else - { - $output += "`n public class $($entityType.Name)`n {" - } - - $properties = $null - - for($index = 0; $index -lt $entityType.EntityProperties.Count; $index++) - { - $property = $entityType.EntityProperties[$index] - $typeName = Convert-ODataTypeToCLRType $property.TypeName $complexTypeMapping - - if ($typeName.StartsWith($metadata.Namespace + ".")) - { - $dotNetPropertyNamespace = $typeName.Replace($metadata.Namespace + ".", "") - } - elseif ($typeName.StartsWith($metadata.Alias + ".")) - { - $dotNetPropertyNamespace = $typeName.Replace($metadata.Alias + ".", "") - } - else - { - $dotNetPropertyNamespace = GetNamespace $typeName $normalizedNamespaces $true - } - - $properties += "`n public $dotNetPropertyNamespace $($property.Name);" - } - - $output += $properties - $output += @" -`n } -"@ - } - } - - $output += "`n}`n" - } - } - $output += """@`n" - $output += "Add-Type -TypeDefinition `$typeDefinitions -IgnoreWarnings`n" - $output | Out-File -FilePath $Path - Write-Verbose ($LocalizedData.VerboseSavedTypeDefinationModule -f $typeDefinationFileName, $OutputModule) - - return $complexTypeMapping -} \ No newline at end of file diff --git a/src/Modules/Windows-Full/Microsoft.PowerShell.ODataUtils/en-US/Microsoft.PowerShell.ODataUtilsStrings.psd1 b/src/Modules/Windows-Full/Microsoft.PowerShell.ODataUtils/en-US/Microsoft.PowerShell.ODataUtilsStrings.psd1 deleted file mode 100644 index 0c04b467060..00000000000 --- a/src/Modules/Windows-Full/Microsoft.PowerShell.ODataUtils/en-US/Microsoft.PowerShell.ODataUtilsStrings.psd1 +++ /dev/null @@ -1,55 +0,0 @@ -# Localized PSODataUtils.psd1 - -ConvertFrom-StringData @' -###PSLOC -SelectedAdapter=Dot sourcing '{0}'. -ArchitectureNotSupported=This module is not supported on your processor architecture ({0}). -ArguementNullError=Failed to generate proxy as '{0}' is pointing to $null in '{1}'. -EmptyMetadata=Read metadata was empty. Url: {0}. -InvalidEndpointAddress=Invalid endpoint address ({0}). Web response with status code '{1}' was obtained while accessing this endpoint address. -NoEntitySets=Metadata from URI '{0}' does not contain Entity Sets. No output will be written. -NoEntityTypes=Metadata from URI '{0}' does not contain Entity Types. No output will be written. -MetadataUriDoesNotExist=Metadata specified at the URI '{0}' does not exist. No output will be written. -InValidIdentifierInMetadata=Metadata specified at URI '{0}' contains an invalid Identifier '{1}'. Only valid C# identifiers are supported in the generated complex types during the proxy creation. -InValidMetadata=Failed to process metadata specified at URI '{0}'. No output will be written. -InValidXmlInMetadata=Metadata specified at URI '{0}' contains an invalid XML. No output will be written. -ODataVersionNotFound=Metadata specified at URI '{0}' does not contain the OData Version. No output will be written. -ODataVersionNotSupported=The OData version '{0}' specified in the metadata located at the URI '{1}' is not supported. Only OData versions between '{2}' and '{3}' are supported by '{4}' during proxy generation. No output will be written. -InValidSchemaNamespace=Metadata specified at URI '{0}' is invalid. NULL or Empty values are not supported for Namespace attribute in the schema. -InValidSchemaNamespaceConflictWithClassName=Metadata specified at URI '{0}' contains invalid Namespace {1} name, which conflicts with another type name. To avoid compilation error {1} will be changed to {2}. -InValidSchemaNamespaceContainsInvalidChars=Metadata specified at URI '{0}' contains invalid Namespace name {1} with a combination of dots and numbers in it, which is not allowed in .Net. To avoid compilation error {1} will be changed to {2}. -InValidUri=URI '{0}' is invalid. No output will be written. -RedfishNotEnabled=This version of Microsoft.PowerShell.ODataUtils doesn’t support Redfish, please run: ‘update-module Microsoft.PowerShell.ODataUtils’ to get Redfish support. -EntitySetUndefinedType=Metadata from URI '{0}' does not contain the Type for Entity Set '{1}'. No output will be written. -XmlWriterInitializationError=There was an error initiating XmlWriter for writing the {0} CDXML module. -EmptySchema=Edmx.DataServices.Schema node should not be null. -VerboseReadingMetadata=Reading metadata from uri {0}. -VerboseParsingMetadata=Parsing metadata... -VerboseVerifyingMetadata=Verifying metadata... -VerboseSavingModule=Saving output module to path {0}. -VerboseSavedCDXML=Saved CDXML module for {0} to {1}. -VerboseSavedServiceActions=Saved Service Actions CDXML module for to {0}. -VerboseSavedModuleManifest=Saved module manifest at {0}. -AssociationNotFound=Association {0} not found in Metadata.Associations. -TooManyMatchingAssociationTypes=Found {0} {1} associations in Metadata.Associations. Expected only one. -ZeroMatchingAssociationTypes=Navigation property {0} not found on association {1}. -WrongCountEntitySet=Expected one EntitySet for EntityType {0}, but got {1}. -EntityNameConflictError=Proxy creation is not supported when multiple EntitySets are mapped to the same EntityType. The metadata located at the URI '{0}' contains EntitySets '{1}' and '{2}' that are mapped to the same EntityType '{3}'. -VerboseSavedTypeDefinationModule=Saved Type definition module '{0}' at '{1}'. -VerboseAddingTypeDefinationToGeneratedModule=Adding Type definition for '{0}' to '{1}' module. -OutputPathNotFound=Could not find a part of the path '{0}'. -ModuleAlreadyExistsAndForceParameterIsNotSpecified=The directory '{0}' already exists. Use the -Force parameter if you want to overwrite the directory and files within the directory. -InvalidOutputModulePath=Path '{0}' specified to -OutputModule parameter does not contain the module name. -OutputModulePathIsNotUnique=Path '{0}' specified to -OutputModule parameter resolves to multiple paths in the file system. Provide a unique file system path to -OutputModule parameter. -OutputModulePathIsNotFileSystemPath=Path '{0}' specified to -OutputModule parameter is not a file system. Provide a unique file system path to -OutputModule parameter. -SkipEntitySetProxyCreation=CDXML module creation has been skipped for the Entity Set '{0}' because its Entity Type '{1}' contains a property '{2}' that collides with one of the default properties of the generated cmdlets. -EntitySetProxyCreationWithWarning=CDXML module creation for the Entity Set '{0}' succeeded but contains a property '{1}' in the Entity Type '{2}' that collides with one of the default properties of the generated cmdlets. -SkipEntitySetConflictCommandCreation=CDXML module creation has been skipped for the Entity Set '{0}' because the exported command '{1}' conflicts with the inbox command. -EntitySetConflictCommandCreationWithWarning=CDXML module creation for the Entity Set '{0}' succeeded but contains a command '{1}' that collides with the inbox command. -SkipConflictServiceActionCommandCreation=CDXML module creation has been skipped for the Service Action '{0}' because the exported command '{1}' conflicts with the inbox command. -ConflictServiceActionCommandCreationWithWarning=CDXML module creation for the Service Action '{0}' succeeded but contains a command '{1}' that collides with the inbox command. -AllowUnsecureConnectionMessage=The cmdlet '{0}' is trying to establish an Unsecure connection with the OData endpoint through the URI '{1}'. Either supply a secure URI to the -{2} parameter or use -AllowUnsecureConnection switch parameter if you intend to use the current URI. -ProgressBarMessage=Creating proxy for the OData endpoint at the URI '{0}'. -###PSLOC - -'@ \ No newline at end of file diff --git a/src/Modules/Windows-Full/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1 b/src/Modules/Windows-Full/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1 deleted file mode 100644 index 25008e2703a..00000000000 --- a/src/Modules/Windows-Full/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1 +++ /dev/null @@ -1,32 +0,0 @@ -@{ -GUID="1DA87E53-152B-403E-98DC-74D7B4D63D59" -Author="Microsoft Corporation" -CompanyName="Microsoft Corporation" -Copyright="© Microsoft Corporation. All rights reserved." -ModuleVersion="3.1.0.0" -PowerShellVersion="3.0" -CLRVersion="4.0" -CmdletsToExport= "Format-List", "Format-Custom", "Format-Table", "Format-Wide", - "Out-File", "Out-Printer", "Out-String", - "Out-GridView", "Get-FormatData", "Export-FormatData", "ConvertFrom-Json", "ConvertTo-Json", - "Invoke-RestMethod", "Invoke-WebRequest", "Register-ObjectEvent", "Register-EngineEvent", - "Wait-Event", "Get-Event", "Remove-Event", "Get-EventSubscriber", "Unregister-Event", "New-Guid", - "New-Event", "Add-Member", "Add-Type", "Compare-Object", "ConvertTo-Html", "ConvertFrom-StringData", - "Export-Csv", "Import-Csv", "ConvertTo-Csv", "ConvertFrom-Csv", "Export-Alias", "Invoke-Expression", - "Get-Alias", "Get-Culture", "Get-Date", "Get-Host", "Get-Member", "Get-Random", "Get-UICulture", - "Get-Unique", "Export-PSSession", "Import-PSSession", "Import-Alias", "Import-LocalizedData", - "Select-String", "Measure-Object", "New-Alias", "New-TimeSpan", "Read-Host", "Set-Alias", "Set-Date", - "Start-Sleep", "Tee-Object", "Measure-Command", "Update-List", "Update-TypeData", "Update-FormatData", - "Remove-TypeData", "Get-TypeData", "Write-Host", "Write-Progress", "New-Object", "Select-Object", - "Group-Object", "Sort-Object", "Get-Variable", "New-Variable", "Set-Variable", "Remove-Variable", - "Clear-Variable", "Export-Clixml", "Import-Clixml", "Import-PowerShellDataFile", "ConvertTo-Xml", "Select-Xml", "Write-Debug", - "Write-Verbose", "Write-Warning", "Write-Error", "Write-Information", "Write-Output", "Set-PSBreakpoint", "Get-PSBreakpoint", - "Remove-PSBreakpoint", "Enable-PSBreakpoint", "Disable-PSBreakpoint", "Get-PSCallStack", - "Send-MailMessage", "Get-TraceSource", "Set-TraceSource", "Trace-Command", "Show-Command", "Unblock-File", "Get-FileHash", - "Get-Runspace", "Debug-Runspace", "Enable-RunspaceDebug", "Disable-RunspaceDebug", "Get-RunspaceDebug", "Wait-Debugger", - "ConvertFrom-String", "Convert-String" , "Get-Uptime", "New-TemporaryFile", "Get-Verb", "Format-Hex" -FunctionsToExport= "ConvertFrom-SddlString" -AliasesToExport= "CFS", "fhx" -NestedModules="Microsoft.PowerShell.Commands.Utility.dll","Microsoft.PowerShell.Utility.psm1" -HelpInfoURI = 'https://go.microsoft.com/fwlink/?linkid=390787' -} diff --git a/src/Modules/Windows-Full/PSScheduledJob/PSScheduledJob.Format.ps1xml b/src/Modules/Windows-Full/PSScheduledJob/PSScheduledJob.Format.ps1xml deleted file mode 100644 index be7f28d740a..00000000000 --- a/src/Modules/Windows-Full/PSScheduledJob/PSScheduledJob.Format.ps1xml +++ /dev/null @@ -1,117 +0,0 @@ - - - - - ScheduledJobTrigger - - Microsoft.PowerShell.ScheduledJob.ScheduledJobTrigger - - - - - - 10 - left - - - - 15 - left - - - - 22 - left - - - - 23 - left - - - - 10 - left - - - - - - - Id - - - Frequency - - - At - - - DaysOfWeek - - - Enabled - - - - - - - - ScheduledJobDefinition - - Microsoft.PowerShell.ScheduledJob.ScheduledJobDefinition - - - - - - 10 - left - - - - 15 - left - - - - 15 - left - - - - 40 - left - - - - 10 - left - - - - - - - Id - - - Name - - - $_.JobTriggers.Count - - - Command - - - Enabled - - - - - - - - diff --git a/src/Modules/Windows-Full/PSScheduledJob/PSScheduledJob.psd1 b/src/Modules/Windows-Full/PSScheduledJob/PSScheduledJob.psd1 deleted file mode 100644 index 042e8735a3e..00000000000 --- a/src/Modules/Windows-Full/PSScheduledJob/PSScheduledJob.psd1 +++ /dev/null @@ -1,33 +0,0 @@ -@{ - -ModuleToProcess = 'Microsoft.PowerShell.ScheduledJob.dll' - -ModuleVersion = '1.1.0.0' - -GUID = '50cdb55f-5ab7-489f-9e94-4ec21ff51e59' - -Author = 'Microsoft Corporation' - -CompanyName = 'Microsoft Corporation' - -Copyright = ' Microsoft Corporation. All rights reserved.' - -PowerShellVersion = '3.0' - -CLRVersion = '4.0' - -TypesToProcess = 'PSScheduledJob.types.ps1xml' - -FormatsToProcess="PSScheduledJob.Format.ps1xml" - -CmdletsToExport = 'New-JobTrigger', 'Add-JobTrigger', 'Remove-JobTrigger', - 'Get-JobTrigger', 'Set-JobTrigger', 'Enable-JobTrigger', - 'Disable-JobTrigger', 'New-ScheduledJobOption', 'Get-ScheduledJobOption', - 'Set-ScheduledJobOption', 'Register-ScheduledJob', 'Get-ScheduledJob', - 'Set-ScheduledJob', 'Unregister-ScheduledJob', 'Enable-ScheduledJob', - 'Disable-ScheduledJob' -AliasesToExport = @() -FunctionsToExport = @() - -HelpInfoURI = 'https://go.microsoft.com/fwlink/?linkid=390816' -} diff --git a/src/Modules/Windows-Full/PSScheduledJob/PSScheduledJob.types.ps1xml b/src/Modules/Windows-Full/PSScheduledJob/PSScheduledJob.types.ps1xml deleted file mode 100644 index 1bba20b14b1..00000000000 --- a/src/Modules/Windows-Full/PSScheduledJob/PSScheduledJob.types.ps1xml +++ /dev/null @@ -1,24 +0,0 @@ - - - - - Microsoft.Management.Infrastructure.CimInstance - - Microsoft.PowerShell.ScheduledJob.JobTriggerToCimInstanceConverter, Microsoft.PowerShell.ScheduledJob, Version=3.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35 - - - diff --git a/src/Modules/Windows-Core+Full/CimCmdlets/CimCmdlets.psd1 b/src/Modules/Windows/CimCmdlets/CimCmdlets.psd1 similarity index 77% rename from src/Modules/Windows-Core+Full/CimCmdlets/CimCmdlets.psd1 rename to src/Modules/Windows/CimCmdlets/CimCmdlets.psd1 index 294de972f5c..734fe45016d 100644 --- a/src/Modules/Windows-Core+Full/CimCmdlets/CimCmdlets.psd1 +++ b/src/Modules/Windows/CimCmdlets/CimCmdlets.psd1 @@ -1,18 +1,18 @@ -@{ -GUID="{Fb6cc51d-c096-4b38-b78d-0fed6277096a}" -Author="Microsoft Corporation" -CompanyName="Microsoft Corporation" -Copyright="© Microsoft Corporation. All rights reserved." -ModuleVersion="1.0.0.0" -PowerShellVersion="3.0" -CLRVersion="4.0" -RootModule="Microsoft.Management.Infrastructure.CimCmdlets" -RequiredAssemblies="Microsoft.Management.Infrastructure.CimCmdlets.dll","Microsoft.Management.Infrastructure.Dll" -CmdletsToExport= "Get-CimAssociatedInstance", "Get-CimClass", "Get-CimInstance", "Get-CimSession", "Invoke-CimMethod", - "New-CimInstance","New-CimSession","New-CimSessionOption","Register-CimIndicationEvent","Remove-CimInstance", - "Remove-CimSession","Set-CimInstance", - "Export-BinaryMiLog","Import-BinaryMiLog" -AliasesToExport = "gcim","scim","ncim", "rcim","icim","gcai","rcie","ncms","rcms","gcms","ncso","gcls" -FunctionsToExport = @() -HelpInfoUri="https://go.microsoft.com/fwlink/?linkid=390758" -} +@{ +GUID="{Fb6cc51d-c096-4b38-b78d-0fed6277096a}" +Author="PowerShell" +CompanyName="Microsoft Corporation" +Copyright="Copyright (c) Microsoft Corporation." +ModuleVersion="7.0.0.0" +CompatiblePSEditions = @("Core") +PowerShellVersion="3.0" +RootModule="Microsoft.Management.Infrastructure.CimCmdlets" +RequiredAssemblies="Microsoft.Management.Infrastructure.CimCmdlets.dll","Microsoft.Management.Infrastructure.Dll" +FunctionsToExport = @() +CmdletsToExport= "Get-CimAssociatedInstance", "Get-CimClass", "Get-CimInstance", "Get-CimSession", "Invoke-CimMethod", + "New-CimInstance","New-CimSession","New-CimSessionOption","Register-CimIndicationEvent","Remove-CimInstance", + "Remove-CimSession","Set-CimInstance", + "Export-BinaryMiLog","Import-BinaryMiLog" +AliasesToExport = "gcim","scim","ncim", "rcim","icim","gcai","rcie","ncms","rcms","gcms","ncso","gcls" +HelpInfoUri="https://aka.ms/powershell75-help" +} diff --git a/src/Modules/Windows/Microsoft.PowerShell.Diagnostics/Diagnostics.format.ps1xml b/src/Modules/Windows/Microsoft.PowerShell.Diagnostics/Diagnostics.format.ps1xml new file mode 100644 index 00000000000..8e0c106f7dd --- /dev/null +++ b/src/Modules/Windows/Microsoft.PowerShell.Diagnostics/Diagnostics.format.ps1xml @@ -0,0 +1,79 @@ + + + + + + + + Counter + + Microsoft.PowerShell.Commands.GetCounter.PerformanceCounterSampleSet + + + + + + 25 + left + + + + left + 100 + + + + + + + + Timestamp + + + Readings + + + + + + + + Counter + + Microsoft.PowerShell.Commands.GetCounter.CounterFileInfo + + + + + 30 + left + + + 30 + left + + + 30 + left + + + + + + + + OldestRecord + + + NewestRecord + + + SampleCount + + + + + + + + diff --git a/src/Modules/Windows/Microsoft.PowerShell.Diagnostics/Event.format.ps1xml b/src/Modules/Windows/Microsoft.PowerShell.Diagnostics/Event.format.ps1xml new file mode 100644 index 00000000000..3d105bc7c31 --- /dev/null +++ b/src/Modules/Windows/Microsoft.PowerShell.Diagnostics/Event.format.ps1xml @@ -0,0 +1,121 @@ + + + + + Default + + System.Diagnostics.Eventing.Reader.EventLogRecord + + + ProviderName + + + + + + 25 + + + 8 + right + + + 16 + + + + + + + + + TimeCreated + + + Id + + + LevelDisplayName + + + Message + + + + + + + + + Default + + System.Diagnostics.Eventing.Reader.EventLogConfiguration + + + + + + + 9 + + + + 18 + right + + + + 11 + right + + + + + + + + LogMode + + + MaximumSizeInBytes + + + RecordCount + + + LogName + + + + + + + + Default + + System.Diagnostics.Eventing.Reader.ProviderMetadata + + + + + + + Name + + + LogLinks + + + Opcodes + + + Tasks + + + + + + + + + diff --git a/src/Modules/Windows/Microsoft.PowerShell.Diagnostics/GetEvent.types.ps1xml b/src/Modules/Windows/Microsoft.PowerShell.Diagnostics/GetEvent.types.ps1xml new file mode 100644 index 00000000000..e63a9b56d94 --- /dev/null +++ b/src/Modules/Windows/Microsoft.PowerShell.Diagnostics/GetEvent.types.ps1xml @@ -0,0 +1,139 @@ + + + + + + System.Diagnostics.Eventing.Reader.EventLogConfiguration + + + PSStandardMembers + + + DefaultDisplayPropertySet + + LogName + MaximumSizeInBytes + RecordCount + LogMode + + + + + + + + System.Diagnostics.Eventing.Reader.EventLogRecord + + + PSStandardMembers + + + DefaultDisplayPropertySet + + TimeCreated + ProviderName + Id + Message + + + + + + + + System.Diagnostics.Eventing.Reader.ProviderMetadata + + + ProviderName + Name + + + PSStandardMembers + + + DefaultDisplayPropertySet + + Name + LogLinks + + + + + + + + Microsoft.PowerShell.Commands.GetCounter.CounterSet + + + Counter + Paths + + + + + Microsoft.PowerShell.Commands.GetCounter.PerformanceCounterSample + + + PSStandardMembers + + + DefaultDisplayPropertySet + + Path + InstanceName + CookedValue + + + + + + + + Microsoft.PowerShell.Commands.GetCounter.PerformanceCounterSampleSet + + + PSStandardMembers + + + DefaultDisplayPropertySet + + Timestamp + Readings + + + + + + + + Microsoft.PowerShell.Commands.GetCounter.PerformanceCounterSampleSet + + + Readings + + $strPaths = "" + foreach ($ctr in $this.CounterSamples) + { + $strPaths += ($ctr.Path + " :" + "`n") + $strPaths += ($ctr.CookedValue.ToString() + "`n`n") + } + return $strPaths + + + + + diff --git a/src/Modules/Windows/Microsoft.PowerShell.Diagnostics/Microsoft.PowerShell.Diagnostics.psd1 b/src/Modules/Windows/Microsoft.PowerShell.Diagnostics/Microsoft.PowerShell.Diagnostics.psd1 new file mode 100644 index 00000000000..7f77777b137 --- /dev/null +++ b/src/Modules/Windows/Microsoft.PowerShell.Diagnostics/Microsoft.PowerShell.Diagnostics.psd1 @@ -0,0 +1,16 @@ +@{ +GUID="CA046F10-CA64-4740-8FF9-2565DBA61A4F" +Author="PowerShell" +CompanyName="Microsoft Corporation" +Copyright="Copyright (c) Microsoft Corporation." +ModuleVersion="7.0.0.0" +CompatiblePSEditions = @("Core") +PowerShellVersion="3.0" +FunctionsToExport = @() +CmdletsToExport="Get-WinEvent", "New-WinEvent", "Get-Counter" +AliasesToExport = @() +NestedModules="Microsoft.PowerShell.Commands.Diagnostics.dll" +TypesToProcess="GetEvent.types.ps1xml" +FormatsToProcess="Event.format.ps1xml", "Diagnostics.format.ps1xml" +HelpInfoURI = 'https://aka.ms/powershell75-help' +} diff --git a/src/Modules/Windows/Microsoft.PowerShell.Management/Microsoft.PowerShell.Management.psd1 b/src/Modules/Windows/Microsoft.PowerShell.Management/Microsoft.PowerShell.Management.psd1 new file mode 100644 index 00000000000..f7582920935 --- /dev/null +++ b/src/Modules/Windows/Microsoft.PowerShell.Management/Microsoft.PowerShell.Management.psd1 @@ -0,0 +1,74 @@ +@{ +GUID="EEFCB906-B326-4E99-9F54-8B4BB6EF3C6D" +Author="PowerShell" +CompanyName="Microsoft Corporation" +Copyright="Copyright (c) Microsoft Corporation." +ModuleVersion="7.0.0.0" +CompatiblePSEditions = @("Core") +PowerShellVersion="3.0" +NestedModules="Microsoft.PowerShell.Commands.Management.dll" +HelpInfoURI = 'https://aka.ms/powershell75-help' +FunctionsToExport = @() +AliasesToExport = @("gcb", "gin", "gtz", "scb", "stz") +CmdletsToExport=@("Add-Content", + "Clear-Content", + "Get-Clipboard", + "Set-Clipboard", + "Clear-ItemProperty", + "Join-Path", + "Convert-Path", + "Copy-ItemProperty", + "Get-ChildItem", + "Get-Content", + "Get-ItemProperty", + "Get-ItemPropertyValue", + "Move-ItemProperty", + "Get-Location", + "Set-Location", + "Push-Location", + "Pop-Location", + "New-PSDrive", + "Remove-PSDrive", + "Get-PSDrive", + "Get-Item", + "New-Item", + "Set-Item", + "Remove-Item", + "Move-Item", + "Rename-Item", + "Copy-Item", + "Clear-Item", + "Invoke-Item", + "Get-PSProvider", + "New-ItemProperty", + "Split-Path", + "Test-Path", + "Test-Connection", + "Get-Process", + "Stop-Process", + "Wait-Process", + "Debug-Process", + "Start-Process", + "Remove-ItemProperty", + "Rename-ItemProperty", + "Resolve-Path", + "Get-Service", + "Stop-Service", + "Start-Service", + "Suspend-Service", + "Resume-Service", + "Restart-Service", + "Set-Service", + "New-Service", + "Remove-Service", + "Set-Content", + "Set-ItemProperty", + "Restart-Computer", + "Stop-Computer", + "Rename-Computer", + "Get-ComputerInfo", + "Get-TimeZone", + "Set-TimeZone", + "Get-HotFix", + "Clear-RecycleBin") +} diff --git a/src/Modules/Windows/Microsoft.PowerShell.Security/Microsoft.PowerShell.Security.psd1 b/src/Modules/Windows/Microsoft.PowerShell.Security/Microsoft.PowerShell.Security.psd1 new file mode 100644 index 00000000000..0953b2d1cca --- /dev/null +++ b/src/Modules/Windows/Microsoft.PowerShell.Security/Microsoft.PowerShell.Security.psd1 @@ -0,0 +1,18 @@ +@{ +GUID = "A94C8C7E-9810-47C0-B8AF-65089C13A35A" +Author = "PowerShell" +CompanyName = "Microsoft Corporation" +Copyright = "Copyright (c) Microsoft Corporation." +ModuleVersion = "7.0.0.0" +CompatiblePSEditions = @("Core") +PowerShellVersion = "3.0" +FunctionsToExport = @() +CmdletsToExport = "Get-Acl", "Set-Acl", "Get-PfxCertificate", "Get-Credential", "Get-ExecutionPolicy", "Set-ExecutionPolicy", "Get-AuthenticodeSignature", "Set-AuthenticodeSignature", "ConvertFrom-SecureString", "ConvertTo-SecureString", "Get-CmsMessage", "Unprotect-CmsMessage", "Protect-CmsMessage" , "New-FileCatalog" , "Test-FileCatalog" +AliasesToExport = @() +NestedModules = "Microsoft.PowerShell.Security.dll" +# 'Security.types.ps1xml' refers to types from 'Microsoft.PowerShell.Security.dll' and thus requiring to load the assembly before processing the type file. +# We declare 'Microsoft.PowerShell.Security.dll' in 'RequiredAssemblies' so as to make sure it's loaded before the type file processing. +RequiredAssemblies = "Microsoft.PowerShell.Security.dll" +TypesToProcess = "Security.types.ps1xml" +HelpInfoURI = 'https://aka.ms/powershell75-help' +} diff --git a/src/Modules/Windows/Microsoft.PowerShell.Security/Security.types.ps1xml b/src/Modules/Windows/Microsoft.PowerShell.Security/Security.types.ps1xml new file mode 100644 index 00000000000..b1171c98e6a --- /dev/null +++ b/src/Modules/Windows/Microsoft.PowerShell.Security/Security.types.ps1xml @@ -0,0 +1,124 @@ + + + + + + System.Security.AccessControl.ObjectSecurity + + + Path + + Microsoft.PowerShell.Commands.SecurityDescriptorCommandsBase + GetPath + + + + Owner + + Microsoft.PowerShell.Commands.SecurityDescriptorCommandsBase + GetOwner + + + + Group + + Microsoft.PowerShell.Commands.SecurityDescriptorCommandsBase + GetGroup + + + + Access + + Microsoft.PowerShell.Commands.SecurityDescriptorCommandsBase + GetAccess + + + + Sddl + + Microsoft.PowerShell.Commands.SecurityDescriptorCommandsBase + GetSddl + + + + AccessToString + + $toString = ""; + $first = $true; + if ( ! $this.Access ) { return "" } + foreach($ace in $this.Access) + { + if($first) + { + $first = $false; + } + else + { + $tostring += "`n"; + } + $toString += $ace.IdentityReference.ToString(); + $toString += " "; + $toString += $ace.AccessControlType.ToString(); + $toString += " "; + if($ace -is [System.Security.AccessControl.FileSystemAccessRule]) + { + $toString += $ace.FileSystemRights.ToString(); + } + elseif($ace -is [System.Security.AccessControl.RegistryAccessRule]) + { + $toString += $ace.RegistryRights.ToString(); + } + } + return $toString; + + + + AuditToString + + $toString = ""; + $first = $true; + if ( ! (& { Set-StrictMode -Version 1; $this.audit }) ) { return "" } + foreach($ace in (& { Set-StrictMode -Version 1; $this.audit })) + { + if($first) + { + $first = $false; + } + else + { + $tostring += "`n"; + } + $toString += $ace.IdentityReference.ToString(); + $toString += " "; + $toString += $ace.AuditFlags.ToString(); + $toString += " "; + if($ace -is [System.Security.AccessControl.FileSystemAuditRule]) + { + $toString += $ace.FileSystemRights.ToString(); + } + elseif($ace -is [System.Security.AccessControl.RegistryAuditRule]) + { + $toString += $ace.RegistryRights.ToString(); + } + } + return $toString; + + + + + + diff --git a/src/Modules/Windows/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1 b/src/Modules/Windows/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1 new file mode 100644 index 00000000000..2043543a8a5 --- /dev/null +++ b/src/Modules/Windows/Microsoft.PowerShell.Utility/Microsoft.PowerShell.Utility.psd1 @@ -0,0 +1,33 @@ +@{ +GUID = "1DA87E53-152B-403E-98DC-74D7B4D63D59" +Author = "PowerShell" +CompanyName = "Microsoft Corporation" +Copyright = "Copyright (c) Microsoft Corporation." +ModuleVersion = "7.0.0.0" +CompatiblePSEditions = @("Core") +PowerShellVersion = "3.0" +CmdletsToExport = @( + 'Export-Alias', 'Get-Alias', 'Import-Alias', 'New-Alias', 'Remove-Alias', 'Set-Alias', 'Export-Clixml', 'Import-Clixml', + 'Measure-Command', 'Trace-Command', 'ConvertFrom-Csv', 'ConvertTo-Csv', 'Export-Csv', 'Import-Csv', 'Get-Culture', + 'Format-Custom', 'Get-Date', 'Set-Date', 'Write-Debug', 'Wait-Debugger', 'Register-EngineEvent', 'Write-Error', + 'Get-Event', 'New-Event', 'Remove-Event', 'Unregister-Event', 'Wait-Event', 'Get-EventSubscriber', 'Invoke-Expression', + 'Out-File', 'Unblock-File', 'Get-FileHash', 'Export-FormatData', 'Get-FormatData', 'Update-FormatData', 'New-Guid', + 'Format-Hex', 'Get-Host', 'Read-Host', 'Write-Host', 'ConvertTo-Html', 'Write-Information', 'ConvertFrom-Json', + 'ConvertTo-Json', 'Test-Json', 'Format-List', 'Import-LocalizedData', 'Send-MailMessage', 'ConvertFrom-Markdown', + 'Show-Markdown', 'Get-MarkdownOption', 'Set-MarkdownOption', 'Add-Member', 'Get-Member', 'Compare-Object', 'Group-Object', + 'Measure-Object', 'New-Object', 'Select-Object', 'Sort-Object', 'Tee-Object', 'Register-ObjectEvent', 'Write-Output', + 'Import-PowerShellDataFile', 'Write-Progress', 'Disable-PSBreakpoint', 'Enable-PSBreakpoint', 'Get-PSBreakpoint', + 'Remove-PSBreakpoint', 'Set-PSBreakpoint', 'Get-PSCallStack', 'Export-PSSession', 'Import-PSSession', 'Get-Random', 'Get-SecureRandom' + 'Invoke-RestMethod', 'Debug-Runspace', 'Get-Runspace', 'Disable-RunspaceDebug', 'Enable-RunspaceDebug', + 'Get-RunspaceDebug', 'ConvertFrom-SddlString', 'Start-Sleep', 'Join-String', 'Out-String', 'Select-String', + 'ConvertFrom-StringData', 'Format-Table', 'New-TemporaryFile', 'New-TimeSpan', 'Get-TraceSource', 'Set-TraceSource', + 'Add-Type', 'Get-TypeData', 'Remove-TypeData', 'Update-TypeData', 'Get-UICulture', 'Get-Unique', 'Get-Uptime', + 'Clear-Variable', 'Get-Variable', 'New-Variable', 'Remove-Variable', 'Set-Variable', 'Get-Verb', 'Write-Verbose', + 'Write-Warning', 'Invoke-WebRequest', 'Format-Wide', 'ConvertTo-Xml', 'Select-Xml', 'Get-Error', 'Update-List', + 'Out-GridView', 'Show-Command', 'Out-Printer', 'ConvertTo-CliXml', 'ConvertFrom-CliXml' +) +FunctionsToExport = @() +AliasesToExport = @('fhx') +NestedModules = @("Microsoft.PowerShell.Commands.Utility.dll") +HelpInfoURI = 'https://aka.ms/powershell75-help' +} diff --git a/src/Modules/Windows/Microsoft.WSMan.Management/Microsoft.WSMan.Management.psd1 b/src/Modules/Windows/Microsoft.WSMan.Management/Microsoft.WSMan.Management.psd1 new file mode 100644 index 00000000000..ced706c9fde --- /dev/null +++ b/src/Modules/Windows/Microsoft.WSMan.Management/Microsoft.WSMan.Management.psd1 @@ -0,0 +1,15 @@ +@{ +GUID="766204A6-330E-4263-A7AB-46C87AFC366C" +Author="PowerShell" +CompanyName="Microsoft Corporation" +Copyright="Copyright (c) Microsoft Corporation." +ModuleVersion="7.0.0.0" +CompatiblePSEditions = @("Core") +PowerShellVersion="3.0" +FunctionsToExport = @() +CmdletsToExport="Disable-WSManCredSSP", "Enable-WSManCredSSP", "Get-WSManCredSSP", "Set-WSManQuickConfig", "Test-WSMan", "Invoke-WSManAction", "Connect-WSMan", "Disconnect-WSMan", "Get-WSManInstance", "Set-WSManInstance", "Remove-WSManInstance", "New-WSManInstance", "New-WSManSessionOption" +AliasesToExport = @() +NestedModules="Microsoft.WSMan.Management.dll" +FormatsToProcess="WSMan.format.ps1xml" +HelpInfoURI = 'https://aka.ms/powershell75-help' +} diff --git a/src/Modules/Windows-Core+Full/Microsoft.WSMan.Management/WSMan.format.ps1xml b/src/Modules/Windows/Microsoft.WSMan.Management/WSMan.format.ps1xml similarity index 94% rename from src/Modules/Windows-Core+Full/Microsoft.WSMan.Management/WSMan.format.ps1xml rename to src/Modules/Windows/Microsoft.WSMan.Management/WSMan.format.ps1xml index a7bdea98469..bbee94971d5 100644 --- a/src/Modules/Windows-Core+Full/Microsoft.WSMan.Management/WSMan.format.ps1xml +++ b/src/Modules/Windows/Microsoft.WSMan.Management/WSMan.format.ps1xml @@ -1,236 +1,236 @@ - - - - - - - System.Xml.XmlElement#http://schemas.dmtf.org/wbem/wsman/identity/1/wsmanidentity.xsd#IdentifyResponse - - System.Xml.XmlElement#http://schemas.dmtf.org/wbem/wsman/identity/1/wsmanidentity.xsd#IdentifyResponse - - - - - - - wsmid - - - ProtocolVersion - - - ProductVendor - - - ProductVersion - - - - - - - - Microsoft.WSMan.Management.WSManConfigElement - - Microsoft.WSMan.Management.WSManConfigElement - - - PSParentPath - - - - - - - 15 - - - - 30 - - - - - - - TypeNameOfElement - - - Name - - - - - - - - Microsoft.WSMan.Management.WSManConfigContainerElement - - Microsoft.WSMan.Management.WSManConfigContainerElement - - - PSParentPath - - - - - - - 15 - - - - 35 - - - - - - - - - - TypeNameOfElement - - - Keys - - - Name - - - - - - - - Microsoft.WSMan.Management.WSManConfigLeafElement - - Microsoft.WSMan.Management.WSManConfigLeafElement - - - PSParentPath - - - - - - - 15 - - - - 30 - - - - 15 - - - - - - - - - - TypeNameOfElement - - - Name - - - SourceOfValue - - - Value - - - - - - - - Microsoft.WSMan.Management.WSManConfigLeafElement#InitParams - - Microsoft.WSMan.Management.WSManConfigLeafElement#InitParams - - - PSParentPath - - - - - - - 30 - - - - 20 - - - - - - - Name - - - Value - - - - - - - - Microsoft.WSMan.Management.WSManConfigContainerElement#ComputerLevel - - Microsoft.WSMan.Management.WSManConfigContainerElement#ComputerLevel - - - PSParentPath - - - - - - - 45 - - - - 20 - - - - - - - Name - - - TypeNameOfElement - - - - - - - - + + + + + + + System.Xml.XmlElement#http://schemas.dmtf.org/wbem/wsman/identity/1/wsmanidentity.xsd#IdentifyResponse + + System.Xml.XmlElement#http://schemas.dmtf.org/wbem/wsman/identity/1/wsmanidentity.xsd#IdentifyResponse + + + + + + + wsmid + + + ProtocolVersion + + + ProductVendor + + + ProductVersion + + + + + + + + Microsoft.WSMan.Management.WSManConfigElement + + Microsoft.WSMan.Management.WSManConfigElement + + + PSParentPath + + + + + + + 15 + + + + 30 + + + + + + + TypeNameOfElement + + + Name + + + + + + + + Microsoft.WSMan.Management.WSManConfigContainerElement + + Microsoft.WSMan.Management.WSManConfigContainerElement + + + PSParentPath + + + + + + + 15 + + + + 35 + + + + + + + + + + TypeNameOfElement + + + Keys + + + Name + + + + + + + + Microsoft.WSMan.Management.WSManConfigLeafElement + + Microsoft.WSMan.Management.WSManConfigLeafElement + + + PSParentPath + + + + + + + 15 + + + + 30 + + + + 15 + + + + + + + + + + TypeNameOfElement + + + Name + + + SourceOfValue + + + Value + + + + + + + + Microsoft.WSMan.Management.WSManConfigLeafElement#InitParams + + Microsoft.WSMan.Management.WSManConfigLeafElement#InitParams + + + PSParentPath + + + + + + + 30 + + + + 20 + + + + + + + Name + + + Value + + + + + + + + Microsoft.WSMan.Management.WSManConfigContainerElement#ComputerLevel + + Microsoft.WSMan.Management.WSManConfigContainerElement#ComputerLevel + + + PSParentPath + + + + + + + 45 + + + + 20 + + + + + + + Name + + + TypeNameOfElement + + + + + + + + diff --git a/src/Modules/Windows/PSDiagnostics/PSDiagnostics.psd1 b/src/Modules/Windows/PSDiagnostics/PSDiagnostics.psd1 new file mode 100644 index 00000000000..3b53d6740e5 --- /dev/null +++ b/src/Modules/Windows/PSDiagnostics/PSDiagnostics.psd1 @@ -0,0 +1,14 @@ +@{ + GUID="c61d6278-02a3-4618-ae37-a524d40a7f44 " + Author="PowerShell" + CompanyName="Microsoft Corporation" + Copyright="Copyright (c) Microsoft Corporation." + ModuleVersion="7.0.0.0" + CompatiblePSEditions = @("Core") + PowerShellVersion="3.0" + ModuleToProcess="PSDiagnostics.psm1" + FunctionsToExport="Disable-PSTrace","Disable-PSWSManCombinedTrace","Disable-WSManTrace","Enable-PSTrace","Enable-PSWSManCombinedTrace","Enable-WSManTrace","Get-LogProperties","Set-LogProperties","Start-Trace","Stop-Trace" + CmdletsToExport = @() + AliasesToExport = @() + HelpInfoUri="https://aka.ms/powershell75-help" +} diff --git a/src/Modules/Windows/PSDiagnostics/PSDiagnostics.psm1 b/src/Modules/Windows/PSDiagnostics/PSDiagnostics.psm1 new file mode 100644 index 00000000000..c31e9f25963 --- /dev/null +++ b/src/Modules/Windows/PSDiagnostics/PSDiagnostics.psm1 @@ -0,0 +1,448 @@ +# Copyright (c) Microsoft Corporation. +# Licensed under the MIT License. + +<# + PowerShell Diagnostics Module + This module contains a set of wrapper scripts that + enable a user to use ETW tracing in PowerShell 7. + #> + +$script:windir = [System.Environment]::GetEnvironmentVariable("windir", [System.EnvironmentVariableTarget]::Machine) + +$script:Logman = "${script:windir}\system32\logman.exe" +$script:wsmanlogfile = "${script:windir}\system32\wsmtraces.log" +$script:wsmprovfile = "${script:windir}\system32\wsmtraceproviders.txt" +$script:wsmsession = "wsmlog" +$script:pssession = "PSTrace" +$script:psprovidername = "PowerShellCore" +$script:wsmprovidername = "Microsoft-Windows-WinRM" +$script:oplog = "/Operational" +$script:analyticlog = "/Analytic" +$script:debuglog = "/Debug" +$script:wevtutil = "${script:windir}\system32\wevtutil.exe" +$script:slparam = "sl" +$script:glparam = "gl" + +function Start-Trace +{ + Param( + [Parameter(Mandatory=$true, + Position=0)] + [string] + $SessionName, + [Parameter(Position=1)] + [ValidateNotNullOrEmpty()] + [string] + $OutputFilePath, + [Parameter(Position=2)] + [ValidateNotNullOrEmpty()] + [string] + $ProviderFilePath, + [Parameter()] + [Switch] + $ETS, + [Parameter()] + [ValidateSet("bin", "bincirc", "csv", "tsv", "sql")] + $Format, + [Parameter()] + [int] + $MinBuffers=0, + [Parameter()] + [int] + $MaxBuffers=256, + [Parameter()] + [int] + $BufferSizeInKB = 0, + [Parameter()] + [int] + $MaxLogFileSizeInMB=0 + ) + + Process + { + $executestring = " start $SessionName" + + if ($ETS) + { + $executestring += " -ets" + } + + if ($null -ne $OutputFilePath) + { + $executestring += " -o ""$OutputFilePath""" + } + + if ($null -ne $ProviderFilePath) + { + $executestring += " -pf ""$ProviderFilePath""" + } + + if ($null -ne $Format) + { + $executestring += " -f $Format" + } + + if ($MinBuffers -ne 0 -or $MaxBuffers -ne 256) + { + $executestring += " -nb $MinBuffers $MaxBuffers" + } + + if ($BufferSizeInKB -ne 0) + { + $executestring += " -bs $BufferSizeInKB" + } + + if ($MaxLogFileSizeInMB -ne 0) + { + $executestring += " -max $MaxLogFileSizeInMB" + } + + & $script:Logman $executestring.Split(" ") + } +} + +function Stop-Trace +{ + param( + [Parameter(Mandatory=$true, + Position=0)] + $SessionName, + [Parameter()] + [switch] + $ETS + ) + + Process + { + if ($ETS) + { + & $script:Logman update $SessionName -ets + & $script:Logman stop $SessionName -ets + } + else + { + & $script:Logman update $SessionName + & $script:Logman stop $SessionName + } + } +} + +function Enable-WSManTrace +{ + + # winrm + "{04c6e16d-b99f-4a3a-9b3e-b8325bbc781e} 0xffffffff 0xff" | Out-File $script:wsmprovfile -Encoding ascii + + # winrsmgr + "{c0a36be8-a515-4cfa-b2b6-2676366efff7} 0xffffffff 0xff" | Out-File $script:wsmprovfile -Encoding ascii -Append + + # WinrsExe + "{f1cab2c0-8beb-4fa2-90e1-8f17e0acdd5d} 0xffffffff 0xff" | Out-File $script:wsmprovfile -Encoding ascii -Append + + # WinrsCmd + "{03992646-3dfe-4477-80e3-85936ace7abb} 0xffffffff 0xff" | Out-File $script:wsmprovfile -Encoding ascii -Append + + # IPMIPrv + "{651d672b-e11f-41b7-add3-c2f6a4023672} 0xffffffff 0xff" | Out-File $script:wsmprovfile -Encoding ascii -Append + + #IpmiDrv + "{D5C6A3E9-FA9C-434e-9653-165B4FC869E4} 0xffffffff 0xff" | Out-File $script:wsmprovfile -Encoding ascii -Append + + # WSManProvHost + "{6e1b64d7-d3be-4651-90fb-3583af89d7f1} 0xffffffff 0xff" | Out-File $script:wsmprovfile -Encoding ascii -Append + + # Event Forwarding + "{6FCDF39A-EF67-483D-A661-76D715C6B008} 0xffffffff 0xff" | Out-File $script:wsmprovfile -Encoding ascii -Append + + Start-Trace -SessionName $script:wsmsession -ETS -OutputFilePath $script:wsmanlogfile -Format bincirc -MinBuffers 16 -MaxBuffers 256 -BufferSizeInKB 64 -MaxLogFileSizeInMB 256 -ProviderFilePath $script:wsmprovfile +} + +function Disable-WSManTrace +{ + Stop-Trace $script:wsmsession -ETS +} + +function Enable-PSWSManCombinedTrace +{ + param ( + [switch] $DoNotOverwriteExistingTrace + ) + + $provfile = [io.path]::GetTempFilename() + + if ($DoNotOverwriteExistingTrace) { + $fileName = [string][guid]::newguid() + $logfile = $PSHOME + "\\Traces\\PSTrace_$fileName.etl" + } else { + $logfile = $PSHOME + "\\Traces\\PSTrace.etl" + } + + "$script:psprovidername 0 5" | Out-File $provfile -Encoding ascii + "$script:wsmprovidername 0 5" | Out-File $provfile -Encoding ascii -Append + + if (!(Test-Path $PSHOME\Traces)) + { + New-Item -ItemType Directory -Force $PSHOME\Traces | Out-Null + } + + if (Test-Path $logfile) + { + Remove-Item -Force $logfile | Out-Null + } + + Start-Trace -SessionName $script:pssession -OutputFilePath $logfile -ProviderFilePath $provfile -ETS + + Remove-Item $provfile -Force -ErrorAction SilentlyContinue +} + +function Disable-PSWSManCombinedTrace +{ + Stop-Trace -SessionName $script:pssession -ETS +} + +function Set-LogProperties +{ + param( + [Parameter(Mandatory=$true, Position=0, ValueFromPipeline=$true)] + [Microsoft.PowerShell.Diagnostics.LogDetails] + $LogDetails, + [switch] $Force + ) + + Process + { + if ($LogDetails.AutoBackup -and !$LogDetails.Retention) + { + throw (New-Object System.InvalidOperationException) + } + + $enabled = $LogDetails.Enabled.ToString() + $retention = $LogDetails.Retention.ToString() + $autobackup = $LogDetails.AutoBackup.ToString() + $maxLogSize = $LogDetails.MaxLogSize.ToString() + $osVersion = [Version] (Get-CimInstance Win32_OperatingSystem).Version + + if (($LogDetails.Type -eq "Analytic") -or ($LogDetails.Type -eq "Debug")) + { + if ($LogDetails.Enabled) + { + if($osVersion -lt 6.3.7600) + { + & $script:wevtutil $script:slparam $LogDetails.Name -e:$Enabled + } + else + { + & $script:wevtutil /q:$Force $script:slparam $LogDetails.Name -e:$Enabled + } + } + else + { + if($osVersion -lt 6.3.7600) + { + & $script:wevtutil $script:slparam $LogDetails.Name -e:$Enabled -rt:$Retention -ms:$MaxLogSize + } + else + { + & $script:wevtutil /q:$Force $script:slparam $LogDetails.Name -e:$Enabled -rt:$Retention -ms:$MaxLogSize + } + } + } + else + { + if($osVersion -lt 6.3.7600) + { + & $script:wevtutil $script:slparam $LogDetails.Name -e:$Enabled -rt:$Retention -ab:$AutoBackup -ms:$MaxLogSize + } + else + { + & $script:wevtutil /q:$Force $script:slparam $LogDetails.Name -e:$Enabled -rt:$Retention -ab:$AutoBackup -ms:$MaxLogSize + } + } + } +} + +function ConvertTo-Bool([string]$value) +{ + if ($value -ieq "true") + { + return $true + } + else + { + return $false + } +} + +function Get-LogProperties +{ + param( + [Parameter(Mandatory=$true, ValueFromPipeline=$true, Position=0)] $Name + ) + + Process + { + $details = & $script:wevtutil $script:glparam $Name + $indexes = @(1,2,8,9,10) + $value = @() + foreach($index in $indexes) + { + $value += @(($details[$index].SubString($details[$index].IndexOf(":")+1)).Trim()) + } + + $enabled = ConvertTo-Bool $value[0] + $retention = ConvertTo-Bool $value[2] + $autobackup = ConvertTo-Bool $value[3] + + New-Object Microsoft.PowerShell.Diagnostics.LogDetails $Name, $enabled, $value[1], $retention, $autobackup, $value[4] + } +} + +function Enable-PSTrace +{ + param( + [switch] $Force, + [switch] $AnalyticOnly + ) + + $Properties = Get-LogProperties ($script:psprovidername + $script:analyticlog) + + if (!$Properties.Enabled) { + $Properties.Enabled = $true + if ($Force) { + Set-LogProperties $Properties -Force + } else { + Set-LogProperties $Properties + } + } + + if (!$AnalyticOnly) { + $Properties = Get-LogProperties ($script:psprovidername + $script:debuglog) + if (!$Properties.Enabled) { + $Properties.Enabled = $true + if ($Force) { + Set-LogProperties $Properties -Force + } else { + Set-LogProperties $Properties + } + } + } +} + +function Disable-PSTrace +{ + param( + [switch] $AnalyticOnly + ) + $Properties = Get-LogProperties ($script:psprovidername + $script:analyticlog) + if ($Properties.Enabled) { + $Properties.Enabled = $false + Set-LogProperties $Properties + } + + if (!$AnalyticOnly) { + $Properties = Get-LogProperties ($script:psprovidername + $script:debuglog) + if ($Properties.Enabled) { + $Properties.Enabled = $false + Set-LogProperties $Properties + } + } +} +Add-Type @" +using System; + +namespace Microsoft.PowerShell.Diagnostics +{ + public class LogDetails + { + public string Name + { + get + { + return name; + } + } + private string name; + + public bool Enabled + { + get + { + return enabled; + } + set + { + enabled = value; + } + } + private bool enabled; + + public string Type + { + get + { + return type; + } + } + private string type; + + public bool Retention + { + get + { + return retention; + } + set + { + retention = value; + } + } + private bool retention; + + public bool AutoBackup + { + get + { + return autoBackup; + } + set + { + autoBackup = value; + } + } + private bool autoBackup; + + public int MaxLogSize + { + get + { + return maxLogSize; + } + set + { + maxLogSize = value; + } + } + private int maxLogSize; + + public LogDetails(string name, bool enabled, string type, bool retention, bool autoBackup, int maxLogSize) + { + this.name = name; + this.enabled = enabled; + this.type = type; + this.retention = retention; + this.autoBackup = autoBackup; + this.maxLogSize = maxLogSize; + } + } +} +"@ + +if (Get-Command logman.exe -Type Application -ErrorAction SilentlyContinue) +{ + Export-ModuleMember Disable-PSTrace, Disable-PSWSManCombinedTrace, Disable-WSManTrace, Enable-PSTrace, Enable-PSWSManCombinedTrace, Enable-WSManTrace, Get-LogProperties, Set-LogProperties, Start-Trace, Stop-Trace +} +else +{ + # Currently we only support these cmdlets as logman.exe is not available on systems like Nano and IoT + Export-ModuleMember Disable-PSTrace, Enable-PSTrace, Get-LogProperties, Set-LogProperties +} diff --git a/src/Modules/nuget.config b/src/Modules/nuget.config new file mode 100644 index 00000000000..388a65572dd --- /dev/null +++ b/src/Modules/nuget.config @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/src/PowerShell.Core.Instrumentation/PowerShell.Core.Instrumentation.man b/src/PowerShell.Core.Instrumentation/PowerShell.Core.Instrumentation.man new file mode 100644 index 00000000000..fb221cfe964 --- /dev/null +++ b/src/PowerShell.Core.Instrumentation/PowerShell.Core.Instrumentation.man @@ -0,0 +1,5725 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + false + + 15728640 + + + + + + true + + 1048985600 + + + + 64 + + + + + + true + + 1048985600 + + + + 64 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +